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     this.addEvents( {
3899          /**
3900          * @scope Roo.dd.DropTarget
3901          */
3902          
3903          /**
3904          * @event enter
3905          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3906          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3907          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3908          * 
3909          * IMPORTANT : it should set this.overClass and this.dropAllowed
3910          * 
3911          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3912          * @param {Event} e The event
3913          * @param {Object} data An object containing arbitrary data supplied by the drag source
3914          */
3915         "enter" : true,
3916         
3917          /**
3918          * @event over
3919          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3920          * This method will be called on every mouse movement while the drag source is over the drop target.
3921          * This default implementation simply returns the dropAllowed config value.
3922          * 
3923          * IMPORTANT : it should set this.dropAllowed
3924          * 
3925          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3926          * @param {Event} e The event
3927          * @param {Object} data An object containing arbitrary data supplied by the drag source
3928          
3929          */
3930         "over" : true,
3931         /**
3932          * @event out
3933          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3934          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3935          * overClass (if any) from the drop element.
3936          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3937          * @param {Event} e The event
3938          * @param {Object} data An object containing arbitrary data supplied by the drag source
3939          */
3940          "out" : true,
3941          
3942         /**
3943          * @event drop
3944          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3945          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3946          * implementation that does something to process the drop event and returns true so that the drag source's
3947          * repair action does not run.
3948          * 
3949          * IMPORTANT : it should set this.success
3950          * 
3951          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3952          * @param {Event} e The event
3953          * @param {Object} data An object containing arbitrary data supplied by the drag source
3954         */
3955          "drop" : true
3956     });
3957             
3958      
3959     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3960         this.el.dom, 
3961         this.ddGroup || this.group,
3962         {
3963             isTarget: true,
3964             listeners : config.listeners || {} 
3965            
3966         
3967         }
3968     );
3969
3970 };
3971
3972 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3973     /**
3974      * @cfg {String} overClass
3975      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3976      */
3977      /**
3978      * @cfg {String} ddGroup
3979      * The drag drop group to handle drop events for
3980      */
3981      
3982     /**
3983      * @cfg {String} dropAllowed
3984      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3985      */
3986     dropAllowed : "x-dd-drop-ok",
3987     /**
3988      * @cfg {String} dropNotAllowed
3989      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3990      */
3991     dropNotAllowed : "x-dd-drop-nodrop",
3992     /**
3993      * @cfg {boolean} success
3994      * set this after drop listener.. 
3995      */
3996     success : false,
3997     /**
3998      * @cfg {boolean} valid
3999      * if the drop point is valid for over/enter..
4000      */
4001     valid : false,
4002     // private
4003     isTarget : true,
4004
4005     // private
4006     isNotifyTarget : true,
4007     
4008     /**
4009      * @hide
4010      */
4011     notifyEnter : function(dd, e, data){
4012         this.valid = true;
4013         this.fireEvent('enter', this, dd, e, data);
4014         if(this.overClass){
4015             this.el.addClass(this.overClass);
4016         }
4017         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4018     },
4019
4020     /**
4021      * @hide
4022      */
4023     notifyOver : function(dd, e, data){
4024         this.valid = true;
4025         this.fireEvent('over', this, dd, e, data);
4026         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4027     },
4028
4029     /**
4030      * @hide
4031      */
4032     notifyOut : function(dd, e, data){
4033         this.fireEvent('out', this, dd, e, data);
4034         if(this.overClass){
4035             this.el.removeClass(this.overClass);
4036         }
4037     },
4038
4039     /**
4040      * @hide
4041      */
4042     notifyDrop : function(dd, e, data){
4043         this.success = false;
4044         this.fireEvent('drop', this, dd, e, data);
4045         return this.success;
4046     }
4047 });/*
4048  * Based on:
4049  * Ext JS Library 1.1.1
4050  * Copyright(c) 2006-2007, Ext JS, LLC.
4051  *
4052  * Originally Released Under LGPL - original licence link has changed is not relivant.
4053  *
4054  * Fork - LGPL
4055  * <script type="text/javascript">
4056  */
4057
4058
4059 /**
4060  * @class Roo.dd.DragZone
4061  * @extends Roo.dd.DragSource
4062  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4063  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4064  * @constructor
4065  * @param {String/HTMLElement/Element} el The container element
4066  * @param {Object} config
4067  */
4068 Roo.dd.DragZone = function(el, config){
4069     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4070     if(this.containerScroll){
4071         Roo.dd.ScrollManager.register(this.el);
4072     }
4073 };
4074
4075 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4076     /**
4077      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4078      * for auto scrolling during drag operations.
4079      */
4080     /**
4081      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4082      * method after a failed drop (defaults to "c3daf9" - light blue)
4083      */
4084
4085     /**
4086      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4087      * for a valid target to drag based on the mouse down. Override this method
4088      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4089      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4090      * @param {EventObject} e The mouse down event
4091      * @return {Object} The dragData
4092      */
4093     getDragData : function(e){
4094         return Roo.dd.Registry.getHandleFromEvent(e);
4095     },
4096     
4097     /**
4098      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4099      * this.dragData.ddel
4100      * @param {Number} x The x position of the click on the dragged object
4101      * @param {Number} y The y position of the click on the dragged object
4102      * @return {Boolean} true to continue the drag, false to cancel
4103      */
4104     onInitDrag : function(x, y){
4105         this.proxy.update(this.dragData.ddel.cloneNode(true));
4106         this.onStartDrag(x, y);
4107         return true;
4108     },
4109     
4110     /**
4111      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4112      */
4113     afterRepair : function(){
4114         if(Roo.enableFx){
4115             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4116         }
4117         this.dragging = false;
4118     },
4119
4120     /**
4121      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4122      * the XY of this.dragData.ddel
4123      * @param {EventObject} e The mouse up event
4124      * @return {Array} The xy location (e.g. [100, 200])
4125      */
4126     getRepairXY : function(e){
4127         return Roo.Element.fly(this.dragData.ddel).getXY();  
4128     }
4129 });/*
4130  * Based on:
4131  * Ext JS Library 1.1.1
4132  * Copyright(c) 2006-2007, Ext JS, LLC.
4133  *
4134  * Originally Released Under LGPL - original licence link has changed is not relivant.
4135  *
4136  * Fork - LGPL
4137  * <script type="text/javascript">
4138  */
4139 /**
4140  * @class Roo.dd.DropZone
4141  * @extends Roo.dd.DropTarget
4142  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4143  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4144  * @constructor
4145  * @param {String/HTMLElement/Element} el The container element
4146  * @param {Object} config
4147  */
4148 Roo.dd.DropZone = function(el, config){
4149     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4150 };
4151
4152 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4153     /**
4154      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4155      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4156      * provide your own custom lookup.
4157      * @param {Event} e The event
4158      * @return {Object} data The custom data
4159      */
4160     getTargetFromEvent : function(e){
4161         return Roo.dd.Registry.getTargetFromEvent(e);
4162     },
4163
4164     /**
4165      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4166      * that it has registered.  This method has no default implementation and should be overridden to provide
4167      * node-specific processing if necessary.
4168      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4169      * {@link #getTargetFromEvent} for this node)
4170      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4171      * @param {Event} e The event
4172      * @param {Object} data An object containing arbitrary data supplied by the drag source
4173      */
4174     onNodeEnter : function(n, dd, e, data){
4175         
4176     },
4177
4178     /**
4179      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4180      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4181      * overridden to provide the proper feedback.
4182      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4183      * {@link #getTargetFromEvent} for this node)
4184      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4185      * @param {Event} e The event
4186      * @param {Object} data An object containing arbitrary data supplied by the drag source
4187      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4188      * underlying {@link Roo.dd.StatusProxy} can be updated
4189      */
4190     onNodeOver : function(n, dd, e, data){
4191         return this.dropAllowed;
4192     },
4193
4194     /**
4195      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4196      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4197      * node-specific processing if necessary.
4198      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4199      * {@link #getTargetFromEvent} for this node)
4200      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4201      * @param {Event} e The event
4202      * @param {Object} data An object containing arbitrary data supplied by the drag source
4203      */
4204     onNodeOut : function(n, dd, e, data){
4205         
4206     },
4207
4208     /**
4209      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4210      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4211      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4212      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4213      * {@link #getTargetFromEvent} for this node)
4214      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4215      * @param {Event} e The event
4216      * @param {Object} data An object containing arbitrary data supplied by the drag source
4217      * @return {Boolean} True if the drop was valid, else false
4218      */
4219     onNodeDrop : function(n, dd, e, data){
4220         return false;
4221     },
4222
4223     /**
4224      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4225      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4226      * it should be overridden to provide the proper feedback if necessary.
4227      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4228      * @param {Event} e The event
4229      * @param {Object} data An object containing arbitrary data supplied by the drag source
4230      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4231      * underlying {@link Roo.dd.StatusProxy} can be updated
4232      */
4233     onContainerOver : function(dd, e, data){
4234         return this.dropNotAllowed;
4235     },
4236
4237     /**
4238      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4239      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4240      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4241      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {Boolean} True if the drop was valid, else false
4246      */
4247     onContainerDrop : function(dd, e, data){
4248         return false;
4249     },
4250
4251     /**
4252      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4253      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4254      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4255      * you should override this method and provide a custom implementation.
4256      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4257      * @param {Event} e The event
4258      * @param {Object} data An object containing arbitrary data supplied by the drag source
4259      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4260      * underlying {@link Roo.dd.StatusProxy} can be updated
4261      */
4262     notifyEnter : function(dd, e, data){
4263         return this.dropNotAllowed;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4268      * This method will be called on every mouse movement while the drag source is over the drop zone.
4269      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4270      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4271      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4272      * registered node, it will call {@link #onContainerOver}.
4273      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4274      * @param {Event} e The event
4275      * @param {Object} data An object containing arbitrary data supplied by the drag source
4276      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4277      * underlying {@link Roo.dd.StatusProxy} can be updated
4278      */
4279     notifyOver : function(dd, e, data){
4280         var n = this.getTargetFromEvent(e);
4281         if(!n){ // not over valid drop target
4282             if(this.lastOverNode){
4283                 this.onNodeOut(this.lastOverNode, dd, e, data);
4284                 this.lastOverNode = null;
4285             }
4286             return this.onContainerOver(dd, e, data);
4287         }
4288         if(this.lastOverNode != n){
4289             if(this.lastOverNode){
4290                 this.onNodeOut(this.lastOverNode, dd, e, data);
4291             }
4292             this.onNodeEnter(n, dd, e, data);
4293             this.lastOverNode = n;
4294         }
4295         return this.onNodeOver(n, dd, e, data);
4296     },
4297
4298     /**
4299      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4300      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4301      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4302      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4303      * @param {Event} e The event
4304      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4305      */
4306     notifyOut : function(dd, e, data){
4307         if(this.lastOverNode){
4308             this.onNodeOut(this.lastOverNode, dd, e, data);
4309             this.lastOverNode = null;
4310         }
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4315      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4316      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4317      * otherwise it will call {@link #onContainerDrop}.
4318      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4319      * @param {Event} e The event
4320      * @param {Object} data An object containing arbitrary data supplied by the drag source
4321      * @return {Boolean} True if the drop was valid, else false
4322      */
4323     notifyDrop : function(dd, e, data){
4324         if(this.lastOverNode){
4325             this.onNodeOut(this.lastOverNode, dd, e, data);
4326             this.lastOverNode = null;
4327         }
4328         var n = this.getTargetFromEvent(e);
4329         return n ?
4330             this.onNodeDrop(n, dd, e, data) :
4331             this.onContainerDrop(dd, e, data);
4332     },
4333
4334     // private
4335     triggerCacheRefresh : function(){
4336         Roo.dd.DDM.refreshCache(this.groups);
4337     }  
4338 });/*
4339  * Based on:
4340  * Ext JS Library 1.1.1
4341  * Copyright(c) 2006-2007, Ext JS, LLC.
4342  *
4343  * Originally Released Under LGPL - original licence link has changed is not relivant.
4344  *
4345  * Fork - LGPL
4346  * <script type="text/javascript">
4347  */
4348
4349
4350 /**
4351  * @class Roo.data.SortTypes
4352  * @singleton
4353  * Defines the default sorting (casting?) comparison functions used when sorting data.
4354  */
4355 Roo.data.SortTypes = {
4356     /**
4357      * Default sort that does nothing
4358      * @param {Mixed} s The value being converted
4359      * @return {Mixed} The comparison value
4360      */
4361     none : function(s){
4362         return s;
4363     },
4364     
4365     /**
4366      * The regular expression used to strip tags
4367      * @type {RegExp}
4368      * @property
4369      */
4370     stripTagsRE : /<\/?[^>]+>/gi,
4371     
4372     /**
4373      * Strips all HTML tags to sort on text only
4374      * @param {Mixed} s The value being converted
4375      * @return {String} The comparison value
4376      */
4377     asText : function(s){
4378         return String(s).replace(this.stripTagsRE, "");
4379     },
4380     
4381     /**
4382      * Strips all HTML tags to sort on text only - Case insensitive
4383      * @param {Mixed} s The value being converted
4384      * @return {String} The comparison value
4385      */
4386     asUCText : function(s){
4387         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4388     },
4389     
4390     /**
4391      * Case insensitive string
4392      * @param {Mixed} s The value being converted
4393      * @return {String} The comparison value
4394      */
4395     asUCString : function(s) {
4396         return String(s).toUpperCase();
4397     },
4398     
4399     /**
4400      * Date sorting
4401      * @param {Mixed} s The value being converted
4402      * @return {Number} The comparison value
4403      */
4404     asDate : function(s) {
4405         if(!s){
4406             return 0;
4407         }
4408         if(s instanceof Date){
4409             return s.getTime();
4410         }
4411         return Date.parse(String(s));
4412     },
4413     
4414     /**
4415      * Float sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Float} The comparison value
4418      */
4419     asFloat : function(s) {
4420         var val = parseFloat(String(s).replace(/,/g, ""));
4421         if(isNaN(val)) val = 0;
4422         return val;
4423     },
4424     
4425     /**
4426      * Integer sorting
4427      * @param {Mixed} s The value being converted
4428      * @return {Number} The comparison value
4429      */
4430     asInt : function(s) {
4431         var val = parseInt(String(s).replace(/,/g, ""));
4432         if(isNaN(val)) val = 0;
4433         return val;
4434     }
4435 };/*
4436  * Based on:
4437  * Ext JS Library 1.1.1
4438  * Copyright(c) 2006-2007, Ext JS, LLC.
4439  *
4440  * Originally Released Under LGPL - original licence link has changed is not relivant.
4441  *
4442  * Fork - LGPL
4443  * <script type="text/javascript">
4444  */
4445
4446 /**
4447 * @class Roo.data.Record
4448  * Instances of this class encapsulate both record <em>definition</em> information, and record
4449  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4450  * to access Records cached in an {@link Roo.data.Store} object.<br>
4451  * <p>
4452  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4453  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4454  * objects.<br>
4455  * <p>
4456  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4457  * @constructor
4458  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4459  * {@link #create}. The parameters are the same.
4460  * @param {Array} data An associative Array of data values keyed by the field name.
4461  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4462  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4463  * not specified an integer id is generated.
4464  */
4465 Roo.data.Record = function(data, id){
4466     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4467     this.data = data;
4468 };
4469
4470 /**
4471  * Generate a constructor for a specific record layout.
4472  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4473  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4474  * Each field definition object may contain the following properties: <ul>
4475  * <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,
4476  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4477  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4478  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4479  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4480  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4481  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4482  * this may be omitted.</p></li>
4483  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4484  * <ul><li>auto (Default, implies no conversion)</li>
4485  * <li>string</li>
4486  * <li>int</li>
4487  * <li>float</li>
4488  * <li>boolean</li>
4489  * <li>date</li></ul></p></li>
4490  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4491  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4492  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4493  * by the Reader into an object that will be stored in the Record. It is passed the
4494  * following parameters:<ul>
4495  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4496  * </ul></p></li>
4497  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4498  * </ul>
4499  * <br>usage:<br><pre><code>
4500 var TopicRecord = Roo.data.Record.create(
4501     {name: 'title', mapping: 'topic_title'},
4502     {name: 'author', mapping: 'username'},
4503     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4504     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4505     {name: 'lastPoster', mapping: 'user2'},
4506     {name: 'excerpt', mapping: 'post_text'}
4507 );
4508
4509 var myNewRecord = new TopicRecord({
4510     title: 'Do my job please',
4511     author: 'noobie',
4512     totalPosts: 1,
4513     lastPost: new Date(),
4514     lastPoster: 'Animal',
4515     excerpt: 'No way dude!'
4516 });
4517 myStore.add(myNewRecord);
4518 </code></pre>
4519  * @method create
4520  * @static
4521  */
4522 Roo.data.Record.create = function(o){
4523     var f = function(){
4524         f.superclass.constructor.apply(this, arguments);
4525     };
4526     Roo.extend(f, Roo.data.Record);
4527     var p = f.prototype;
4528     p.fields = new Roo.util.MixedCollection(false, function(field){
4529         return field.name;
4530     });
4531     for(var i = 0, len = o.length; i < len; i++){
4532         p.fields.add(new Roo.data.Field(o[i]));
4533     }
4534     f.getField = function(name){
4535         return p.fields.get(name);  
4536     };
4537     return f;
4538 };
4539
4540 Roo.data.Record.AUTO_ID = 1000;
4541 Roo.data.Record.EDIT = 'edit';
4542 Roo.data.Record.REJECT = 'reject';
4543 Roo.data.Record.COMMIT = 'commit';
4544
4545 Roo.data.Record.prototype = {
4546     /**
4547      * Readonly flag - true if this record has been modified.
4548      * @type Boolean
4549      */
4550     dirty : false,
4551     editing : false,
4552     error: null,
4553     modified: null,
4554
4555     // private
4556     join : function(store){
4557         this.store = store;
4558     },
4559
4560     /**
4561      * Set the named field to the specified value.
4562      * @param {String} name The name of the field to set.
4563      * @param {Object} value The value to set the field to.
4564      */
4565     set : function(name, value){
4566         if(this.data[name] == value){
4567             return;
4568         }
4569         this.dirty = true;
4570         if(!this.modified){
4571             this.modified = {};
4572         }
4573         if(typeof this.modified[name] == 'undefined'){
4574             this.modified[name] = this.data[name];
4575         }
4576         this.data[name] = value;
4577         if(!this.editing){
4578             this.store.afterEdit(this);
4579         }       
4580     },
4581
4582     /**
4583      * Get the value of the named field.
4584      * @param {String} name The name of the field to get the value of.
4585      * @return {Object} The value of the field.
4586      */
4587     get : function(name){
4588         return this.data[name]; 
4589     },
4590
4591     // private
4592     beginEdit : function(){
4593         this.editing = true;
4594         this.modified = {}; 
4595     },
4596
4597     // private
4598     cancelEdit : function(){
4599         this.editing = false;
4600         delete this.modified;
4601     },
4602
4603     // private
4604     endEdit : function(){
4605         this.editing = false;
4606         if(this.dirty && this.store){
4607             this.store.afterEdit(this);
4608         }
4609     },
4610
4611     /**
4612      * Usually called by the {@link Roo.data.Store} which owns the Record.
4613      * Rejects all changes made to the Record since either creation, or the last commit operation.
4614      * Modified fields are reverted to their original values.
4615      * <p>
4616      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4617      * of reject operations.
4618      */
4619     reject : function(){
4620         var m = this.modified;
4621         for(var n in m){
4622             if(typeof m[n] != "function"){
4623                 this.data[n] = m[n];
4624             }
4625         }
4626         this.dirty = false;
4627         delete this.modified;
4628         this.editing = false;
4629         if(this.store){
4630             this.store.afterReject(this);
4631         }
4632     },
4633
4634     /**
4635      * Usually called by the {@link Roo.data.Store} which owns the Record.
4636      * Commits all changes made to the Record since either creation, or the last commit operation.
4637      * <p>
4638      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4639      * of commit operations.
4640      */
4641     commit : function(){
4642         this.dirty = false;
4643         delete this.modified;
4644         this.editing = false;
4645         if(this.store){
4646             this.store.afterCommit(this);
4647         }
4648     },
4649
4650     // private
4651     hasError : function(){
4652         return this.error != null;
4653     },
4654
4655     // private
4656     clearError : function(){
4657         this.error = null;
4658     },
4659
4660     /**
4661      * Creates a copy of this record.
4662      * @param {String} id (optional) A new record id if you don't want to use this record's id
4663      * @return {Record}
4664      */
4665     copy : function(newId) {
4666         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4667     }
4668 };/*
4669  * Based on:
4670  * Ext JS Library 1.1.1
4671  * Copyright(c) 2006-2007, Ext JS, LLC.
4672  *
4673  * Originally Released Under LGPL - original licence link has changed is not relivant.
4674  *
4675  * Fork - LGPL
4676  * <script type="text/javascript">
4677  */
4678
4679
4680
4681 /**
4682  * @class Roo.data.Store
4683  * @extends Roo.util.Observable
4684  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4685  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4686  * <p>
4687  * 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
4688  * has no knowledge of the format of the data returned by the Proxy.<br>
4689  * <p>
4690  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4691  * instances from the data object. These records are cached and made available through accessor functions.
4692  * @constructor
4693  * Creates a new Store.
4694  * @param {Object} config A config object containing the objects needed for the Store to access data,
4695  * and read the data into Records.
4696  */
4697 Roo.data.Store = function(config){
4698     this.data = new Roo.util.MixedCollection(false);
4699     this.data.getKey = function(o){
4700         return o.id;
4701     };
4702     this.baseParams = {};
4703     // private
4704     this.paramNames = {
4705         "start" : "start",
4706         "limit" : "limit",
4707         "sort" : "sort",
4708         "dir" : "dir"
4709     };
4710
4711     if(config && config.data){
4712         this.inlineData = config.data;
4713         delete config.data;
4714     }
4715
4716     Roo.apply(this, config);
4717     
4718     if(this.reader){ // reader passed
4719         this.reader = Roo.factory(this.reader, Roo.data);
4720         this.reader.xmodule = this.xmodule || false;
4721         if(!this.recordType){
4722             this.recordType = this.reader.recordType;
4723         }
4724         if(this.reader.onMetaChange){
4725             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4726         }
4727     }
4728
4729     if(this.recordType){
4730         this.fields = this.recordType.prototype.fields;
4731     }
4732     this.modified = [];
4733
4734     this.addEvents({
4735         /**
4736          * @event datachanged
4737          * Fires when the data cache has changed, and a widget which is using this Store
4738          * as a Record cache should refresh its view.
4739          * @param {Store} this
4740          */
4741         datachanged : true,
4742         /**
4743          * @event metachange
4744          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4745          * @param {Store} this
4746          * @param {Object} meta The JSON metadata
4747          */
4748         metachange : true,
4749         /**
4750          * @event add
4751          * Fires when Records have been added to the Store
4752          * @param {Store} this
4753          * @param {Roo.data.Record[]} records The array of Records added
4754          * @param {Number} index The index at which the record(s) were added
4755          */
4756         add : true,
4757         /**
4758          * @event remove
4759          * Fires when a Record has been removed from the Store
4760          * @param {Store} this
4761          * @param {Roo.data.Record} record The Record that was removed
4762          * @param {Number} index The index at which the record was removed
4763          */
4764         remove : true,
4765         /**
4766          * @event update
4767          * Fires when a Record has been updated
4768          * @param {Store} this
4769          * @param {Roo.data.Record} record The Record that was updated
4770          * @param {String} operation The update operation being performed.  Value may be one of:
4771          * <pre><code>
4772  Roo.data.Record.EDIT
4773  Roo.data.Record.REJECT
4774  Roo.data.Record.COMMIT
4775          * </code></pre>
4776          */
4777         update : true,
4778         /**
4779          * @event clear
4780          * Fires when the data cache has been cleared.
4781          * @param {Store} this
4782          */
4783         clear : true,
4784         /**
4785          * @event beforeload
4786          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4787          * the load action will be canceled.
4788          * @param {Store} this
4789          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4790          */
4791         beforeload : true,
4792         /**
4793          * @event load
4794          * Fires after a new set of Records has been loaded.
4795          * @param {Store} this
4796          * @param {Roo.data.Record[]} records The Records that were loaded
4797          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4798          */
4799         load : true,
4800         /**
4801          * @event loadexception
4802          * Fires if an exception occurs in the Proxy during loading.
4803          * Called with the signature of the Proxy's "loadexception" event.
4804          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4805          * 
4806          * @param {Proxy} 
4807          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4808          * @param {Object} load options 
4809          * @param {Object} jsonData from your request (normally this contains the Exception)
4810          */
4811         loadexception : true
4812     });
4813     
4814     if(this.proxy){
4815         this.proxy = Roo.factory(this.proxy, Roo.data);
4816         this.proxy.xmodule = this.xmodule || false;
4817         this.relayEvents(this.proxy,  ["loadexception"]);
4818     }
4819     this.sortToggle = {};
4820
4821     Roo.data.Store.superclass.constructor.call(this);
4822
4823     if(this.inlineData){
4824         this.loadData(this.inlineData);
4825         delete this.inlineData;
4826     }
4827 };
4828 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4829      /**
4830     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4831     * without a remote query - used by combo/forms at present.
4832     */
4833     
4834     /**
4835     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4836     */
4837     /**
4838     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4839     */
4840     /**
4841     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4842     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4843     */
4844     /**
4845     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4846     * on any HTTP request
4847     */
4848     /**
4849     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4850     */
4851     /**
4852     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4853     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4854     */
4855     remoteSort : false,
4856
4857     /**
4858     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4859      * loaded or when a record is removed. (defaults to false).
4860     */
4861     pruneModifiedRecords : false,
4862
4863     // private
4864     lastOptions : null,
4865
4866     /**
4867      * Add Records to the Store and fires the add event.
4868      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4869      */
4870     add : function(records){
4871         records = [].concat(records);
4872         for(var i = 0, len = records.length; i < len; i++){
4873             records[i].join(this);
4874         }
4875         var index = this.data.length;
4876         this.data.addAll(records);
4877         this.fireEvent("add", this, records, index);
4878     },
4879
4880     /**
4881      * Remove a Record from the Store and fires the remove event.
4882      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4883      */
4884     remove : function(record){
4885         var index = this.data.indexOf(record);
4886         this.data.removeAt(index);
4887         if(this.pruneModifiedRecords){
4888             this.modified.remove(record);
4889         }
4890         this.fireEvent("remove", this, record, index);
4891     },
4892
4893     /**
4894      * Remove all Records from the Store and fires the clear event.
4895      */
4896     removeAll : function(){
4897         this.data.clear();
4898         if(this.pruneModifiedRecords){
4899             this.modified = [];
4900         }
4901         this.fireEvent("clear", this);
4902     },
4903
4904     /**
4905      * Inserts Records to the Store at the given index and fires the add event.
4906      * @param {Number} index The start index at which to insert the passed Records.
4907      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4908      */
4909     insert : function(index, records){
4910         records = [].concat(records);
4911         for(var i = 0, len = records.length; i < len; i++){
4912             this.data.insert(index, records[i]);
4913             records[i].join(this);
4914         }
4915         this.fireEvent("add", this, records, index);
4916     },
4917
4918     /**
4919      * Get the index within the cache of the passed Record.
4920      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4921      * @return {Number} The index of the passed Record. Returns -1 if not found.
4922      */
4923     indexOf : function(record){
4924         return this.data.indexOf(record);
4925     },
4926
4927     /**
4928      * Get the index within the cache of the Record with the passed id.
4929      * @param {String} id The id of the Record to find.
4930      * @return {Number} The index of the Record. Returns -1 if not found.
4931      */
4932     indexOfId : function(id){
4933         return this.data.indexOfKey(id);
4934     },
4935
4936     /**
4937      * Get the Record with the specified id.
4938      * @param {String} id The id of the Record to find.
4939      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4940      */
4941     getById : function(id){
4942         return this.data.key(id);
4943     },
4944
4945     /**
4946      * Get the Record at the specified index.
4947      * @param {Number} index The index of the Record to find.
4948      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4949      */
4950     getAt : function(index){
4951         return this.data.itemAt(index);
4952     },
4953
4954     /**
4955      * Returns a range of Records between specified indices.
4956      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4957      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4958      * @return {Roo.data.Record[]} An array of Records
4959      */
4960     getRange : function(start, end){
4961         return this.data.getRange(start, end);
4962     },
4963
4964     // private
4965     storeOptions : function(o){
4966         o = Roo.apply({}, o);
4967         delete o.callback;
4968         delete o.scope;
4969         this.lastOptions = o;
4970     },
4971
4972     /**
4973      * Loads the Record cache from the configured Proxy using the configured Reader.
4974      * <p>
4975      * If using remote paging, then the first load call must specify the <em>start</em>
4976      * and <em>limit</em> properties in the options.params property to establish the initial
4977      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4978      * <p>
4979      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4980      * and this call will return before the new data has been loaded. Perform any post-processing
4981      * in a callback function, or in a "load" event handler.</strong>
4982      * <p>
4983      * @param {Object} options An object containing properties which control loading options:<ul>
4984      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4985      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4986      * passed the following arguments:<ul>
4987      * <li>r : Roo.data.Record[]</li>
4988      * <li>options: Options object from the load call</li>
4989      * <li>success: Boolean success indicator</li></ul></li>
4990      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4991      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4992      * </ul>
4993      */
4994     load : function(options){
4995         options = options || {};
4996         if(this.fireEvent("beforeload", this, options) !== false){
4997             this.storeOptions(options);
4998             var p = Roo.apply(options.params || {}, this.baseParams);
4999             // if meta was not loaded from remote source.. try requesting it.
5000             if (!this.reader.metaFromRemote) {
5001                 p._requestMeta = 1;
5002             }
5003             if(this.sortInfo && this.remoteSort){
5004                 var pn = this.paramNames;
5005                 p[pn["sort"]] = this.sortInfo.field;
5006                 p[pn["dir"]] = this.sortInfo.direction;
5007             }
5008             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5009         }
5010     },
5011
5012     /**
5013      * Reloads the Record cache from the configured Proxy using the configured Reader and
5014      * the options from the last load operation performed.
5015      * @param {Object} options (optional) An object containing properties which may override the options
5016      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5017      * the most recently used options are reused).
5018      */
5019     reload : function(options){
5020         this.load(Roo.applyIf(options||{}, this.lastOptions));
5021     },
5022
5023     // private
5024     // Called as a callback by the Reader during a load operation.
5025     loadRecords : function(o, options, success){
5026         if(!o || success === false){
5027             if(success !== false){
5028                 this.fireEvent("load", this, [], options);
5029             }
5030             if(options.callback){
5031                 options.callback.call(options.scope || this, [], options, false);
5032             }
5033             return;
5034         }
5035         // if data returned failure - throw an exception.
5036         if (o.success === false) {
5037             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5038             return;
5039         }
5040         var r = o.records, t = o.totalRecords || r.length;
5041         if(!options || options.add !== true){
5042             if(this.pruneModifiedRecords){
5043                 this.modified = [];
5044             }
5045             for(var i = 0, len = r.length; i < len; i++){
5046                 r[i].join(this);
5047             }
5048             if(this.snapshot){
5049                 this.data = this.snapshot;
5050                 delete this.snapshot;
5051             }
5052             this.data.clear();
5053             this.data.addAll(r);
5054             this.totalLength = t;
5055             this.applySort();
5056             this.fireEvent("datachanged", this);
5057         }else{
5058             this.totalLength = Math.max(t, this.data.length+r.length);
5059             this.add(r);
5060         }
5061         this.fireEvent("load", this, r, options);
5062         if(options.callback){
5063             options.callback.call(options.scope || this, r, options, true);
5064         }
5065     },
5066
5067     /**
5068      * Loads data from a passed data block. A Reader which understands the format of the data
5069      * must have been configured in the constructor.
5070      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5071      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5072      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5073      */
5074     loadData : function(o, append){
5075         var r = this.reader.readRecords(o);
5076         this.loadRecords(r, {add: append}, true);
5077     },
5078
5079     /**
5080      * Gets the number of cached records.
5081      * <p>
5082      * <em>If using paging, this may not be the total size of the dataset. If the data object
5083      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5084      * the data set size</em>
5085      */
5086     getCount : function(){
5087         return this.data.length || 0;
5088     },
5089
5090     /**
5091      * Gets the total number of records in the dataset as returned by the server.
5092      * <p>
5093      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5094      * the dataset size</em>
5095      */
5096     getTotalCount : function(){
5097         return this.totalLength || 0;
5098     },
5099
5100     /**
5101      * Returns the sort state of the Store as an object with two properties:
5102      * <pre><code>
5103  field {String} The name of the field by which the Records are sorted
5104  direction {String} The sort order, "ASC" or "DESC"
5105      * </code></pre>
5106      */
5107     getSortState : function(){
5108         return this.sortInfo;
5109     },
5110
5111     // private
5112     applySort : function(){
5113         if(this.sortInfo && !this.remoteSort){
5114             var s = this.sortInfo, f = s.field;
5115             var st = this.fields.get(f).sortType;
5116             var fn = function(r1, r2){
5117                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5118                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5119             };
5120             this.data.sort(s.direction, fn);
5121             if(this.snapshot && this.snapshot != this.data){
5122                 this.snapshot.sort(s.direction, fn);
5123             }
5124         }
5125     },
5126
5127     /**
5128      * Sets the default sort column and order to be used by the next load operation.
5129      * @param {String} fieldName The name of the field to sort by.
5130      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5131      */
5132     setDefaultSort : function(field, dir){
5133         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5134     },
5135
5136     /**
5137      * Sort the Records.
5138      * If remote sorting is used, the sort is performed on the server, and the cache is
5139      * reloaded. If local sorting is used, the cache is sorted internally.
5140      * @param {String} fieldName The name of the field to sort by.
5141      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5142      */
5143     sort : function(fieldName, dir){
5144         var f = this.fields.get(fieldName);
5145         if(!dir){
5146             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5147                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5148             }else{
5149                 dir = f.sortDir;
5150             }
5151         }
5152         this.sortToggle[f.name] = dir;
5153         this.sortInfo = {field: f.name, direction: dir};
5154         if(!this.remoteSort){
5155             this.applySort();
5156             this.fireEvent("datachanged", this);
5157         }else{
5158             this.load(this.lastOptions);
5159         }
5160     },
5161
5162     /**
5163      * Calls the specified function for each of the Records in the cache.
5164      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5165      * Returning <em>false</em> aborts and exits the iteration.
5166      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5167      */
5168     each : function(fn, scope){
5169         this.data.each(fn, scope);
5170     },
5171
5172     /**
5173      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5174      * (e.g., during paging).
5175      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5176      */
5177     getModifiedRecords : function(){
5178         return this.modified;
5179     },
5180
5181     // private
5182     createFilterFn : function(property, value, anyMatch){
5183         if(!value.exec){ // not a regex
5184             value = String(value);
5185             if(value.length == 0){
5186                 return false;
5187             }
5188             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5189         }
5190         return function(r){
5191             return value.test(r.data[property]);
5192         };
5193     },
5194
5195     /**
5196      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5197      * @param {String} property A field on your records
5198      * @param {Number} start The record index to start at (defaults to 0)
5199      * @param {Number} end The last record index to include (defaults to length - 1)
5200      * @return {Number} The sum
5201      */
5202     sum : function(property, start, end){
5203         var rs = this.data.items, v = 0;
5204         start = start || 0;
5205         end = (end || end === 0) ? end : rs.length-1;
5206
5207         for(var i = start; i <= end; i++){
5208             v += (rs[i].data[property] || 0);
5209         }
5210         return v;
5211     },
5212
5213     /**
5214      * Filter the records by a specified property.
5215      * @param {String} field A field on your records
5216      * @param {String/RegExp} value Either a string that the field
5217      * should start with or a RegExp to test against the field
5218      * @param {Boolean} anyMatch True to match any part not just the beginning
5219      */
5220     filter : function(property, value, anyMatch){
5221         var fn = this.createFilterFn(property, value, anyMatch);
5222         return fn ? this.filterBy(fn) : this.clearFilter();
5223     },
5224
5225     /**
5226      * Filter by a function. The specified function will be called with each
5227      * record in this data source. If the function returns true the record is included,
5228      * otherwise it is filtered.
5229      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5230      * @param {Object} scope (optional) The scope of the function (defaults to this)
5231      */
5232     filterBy : function(fn, scope){
5233         this.snapshot = this.snapshot || this.data;
5234         this.data = this.queryBy(fn, scope||this);
5235         this.fireEvent("datachanged", this);
5236     },
5237
5238     /**
5239      * Query the records by a specified property.
5240      * @param {String} field A field on your records
5241      * @param {String/RegExp} value Either a string that the field
5242      * should start with or a RegExp to test against the field
5243      * @param {Boolean} anyMatch True to match any part not just the beginning
5244      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5245      */
5246     query : function(property, value, anyMatch){
5247         var fn = this.createFilterFn(property, value, anyMatch);
5248         return fn ? this.queryBy(fn) : this.data.clone();
5249     },
5250
5251     /**
5252      * Query by a function. The specified function will be called with each
5253      * record in this data source. If the function returns true the record is included
5254      * in the results.
5255      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5256      * @param {Object} scope (optional) The scope of the function (defaults to this)
5257       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5258      **/
5259     queryBy : function(fn, scope){
5260         var data = this.snapshot || this.data;
5261         return data.filterBy(fn, scope||this);
5262     },
5263
5264     /**
5265      * Collects unique values for a particular dataIndex from this store.
5266      * @param {String} dataIndex The property to collect
5267      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5268      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5269      * @return {Array} An array of the unique values
5270      **/
5271     collect : function(dataIndex, allowNull, bypassFilter){
5272         var d = (bypassFilter === true && this.snapshot) ?
5273                 this.snapshot.items : this.data.items;
5274         var v, sv, r = [], l = {};
5275         for(var i = 0, len = d.length; i < len; i++){
5276             v = d[i].data[dataIndex];
5277             sv = String(v);
5278             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5279                 l[sv] = true;
5280                 r[r.length] = v;
5281             }
5282         }
5283         return r;
5284     },
5285
5286     /**
5287      * Revert to a view of the Record cache with no filtering applied.
5288      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5289      */
5290     clearFilter : function(suppressEvent){
5291         if(this.snapshot && this.snapshot != this.data){
5292             this.data = this.snapshot;
5293             delete this.snapshot;
5294             if(suppressEvent !== true){
5295                 this.fireEvent("datachanged", this);
5296             }
5297         }
5298     },
5299
5300     // private
5301     afterEdit : function(record){
5302         if(this.modified.indexOf(record) == -1){
5303             this.modified.push(record);
5304         }
5305         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5306     },
5307
5308     // private
5309     afterReject : function(record){
5310         this.modified.remove(record);
5311         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5312     },
5313
5314     // private
5315     afterCommit : function(record){
5316         this.modified.remove(record);
5317         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5318     },
5319
5320     /**
5321      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5322      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5323      */
5324     commitChanges : function(){
5325         var m = this.modified.slice(0);
5326         this.modified = [];
5327         for(var i = 0, len = m.length; i < len; i++){
5328             m[i].commit();
5329         }
5330     },
5331
5332     /**
5333      * Cancel outstanding changes on all changed records.
5334      */
5335     rejectChanges : function(){
5336         var m = this.modified.slice(0);
5337         this.modified = [];
5338         for(var i = 0, len = m.length; i < len; i++){
5339             m[i].reject();
5340         }
5341     },
5342
5343     onMetaChange : function(meta, rtype, o){
5344         this.recordType = rtype;
5345         this.fields = rtype.prototype.fields;
5346         delete this.snapshot;
5347         this.sortInfo = meta.sortInfo || this.sortInfo;
5348         this.modified = [];
5349         this.fireEvent('metachange', this, this.reader.meta);
5350     }
5351 });/*
5352  * Based on:
5353  * Ext JS Library 1.1.1
5354  * Copyright(c) 2006-2007, Ext JS, LLC.
5355  *
5356  * Originally Released Under LGPL - original licence link has changed is not relivant.
5357  *
5358  * Fork - LGPL
5359  * <script type="text/javascript">
5360  */
5361
5362 /**
5363  * @class Roo.data.SimpleStore
5364  * @extends Roo.data.Store
5365  * Small helper class to make creating Stores from Array data easier.
5366  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5367  * @cfg {Array} fields An array of field definition objects, or field name strings.
5368  * @cfg {Array} data The multi-dimensional array of data
5369  * @constructor
5370  * @param {Object} config
5371  */
5372 Roo.data.SimpleStore = function(config){
5373     Roo.data.SimpleStore.superclass.constructor.call(this, {
5374         isLocal : true,
5375         reader: new Roo.data.ArrayReader({
5376                 id: config.id
5377             },
5378             Roo.data.Record.create(config.fields)
5379         ),
5380         proxy : new Roo.data.MemoryProxy(config.data)
5381     });
5382     this.load();
5383 };
5384 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5385  * Based on:
5386  * Ext JS Library 1.1.1
5387  * Copyright(c) 2006-2007, Ext JS, LLC.
5388  *
5389  * Originally Released Under LGPL - original licence link has changed is not relivant.
5390  *
5391  * Fork - LGPL
5392  * <script type="text/javascript">
5393  */
5394
5395 /**
5396 /**
5397  * @extends Roo.data.Store
5398  * @class Roo.data.JsonStore
5399  * Small helper class to make creating Stores for JSON data easier. <br/>
5400 <pre><code>
5401 var store = new Roo.data.JsonStore({
5402     url: 'get-images.php',
5403     root: 'images',
5404     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5405 });
5406 </code></pre>
5407  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5408  * JsonReader and HttpProxy (unless inline data is provided).</b>
5409  * @cfg {Array} fields An array of field definition objects, or field name strings.
5410  * @constructor
5411  * @param {Object} config
5412  */
5413 Roo.data.JsonStore = function(c){
5414     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5415         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5416         reader: new Roo.data.JsonReader(c, c.fields)
5417     }));
5418 };
5419 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5420  * Based on:
5421  * Ext JS Library 1.1.1
5422  * Copyright(c) 2006-2007, Ext JS, LLC.
5423  *
5424  * Originally Released Under LGPL - original licence link has changed is not relivant.
5425  *
5426  * Fork - LGPL
5427  * <script type="text/javascript">
5428  */
5429
5430  
5431 Roo.data.Field = function(config){
5432     if(typeof config == "string"){
5433         config = {name: config};
5434     }
5435     Roo.apply(this, config);
5436     
5437     if(!this.type){
5438         this.type = "auto";
5439     }
5440     
5441     var st = Roo.data.SortTypes;
5442     // named sortTypes are supported, here we look them up
5443     if(typeof this.sortType == "string"){
5444         this.sortType = st[this.sortType];
5445     }
5446     
5447     // set default sortType for strings and dates
5448     if(!this.sortType){
5449         switch(this.type){
5450             case "string":
5451                 this.sortType = st.asUCString;
5452                 break;
5453             case "date":
5454                 this.sortType = st.asDate;
5455                 break;
5456             default:
5457                 this.sortType = st.none;
5458         }
5459     }
5460
5461     // define once
5462     var stripRe = /[\$,%]/g;
5463
5464     // prebuilt conversion function for this field, instead of
5465     // switching every time we're reading a value
5466     if(!this.convert){
5467         var cv, dateFormat = this.dateFormat;
5468         switch(this.type){
5469             case "":
5470             case "auto":
5471             case undefined:
5472                 cv = function(v){ return v; };
5473                 break;
5474             case "string":
5475                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5476                 break;
5477             case "int":
5478                 cv = function(v){
5479                     return v !== undefined && v !== null && v !== '' ?
5480                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5481                     };
5482                 break;
5483             case "float":
5484                 cv = function(v){
5485                     return v !== undefined && v !== null && v !== '' ?
5486                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5487                     };
5488                 break;
5489             case "bool":
5490             case "boolean":
5491                 cv = function(v){ return v === true || v === "true" || v == 1; };
5492                 break;
5493             case "date":
5494                 cv = function(v){
5495                     if(!v){
5496                         return '';
5497                     }
5498                     if(v instanceof Date){
5499                         return v;
5500                     }
5501                     if(dateFormat){
5502                         if(dateFormat == "timestamp"){
5503                             return new Date(v*1000);
5504                         }
5505                         return Date.parseDate(v, dateFormat);
5506                     }
5507                     var parsed = Date.parse(v);
5508                     return parsed ? new Date(parsed) : null;
5509                 };
5510              break;
5511             
5512         }
5513         this.convert = cv;
5514     }
5515 };
5516
5517 Roo.data.Field.prototype = {
5518     dateFormat: null,
5519     defaultValue: "",
5520     mapping: null,
5521     sortType : null,
5522     sortDir : "ASC"
5523 };/*
5524  * Based on:
5525  * Ext JS Library 1.1.1
5526  * Copyright(c) 2006-2007, Ext JS, LLC.
5527  *
5528  * Originally Released Under LGPL - original licence link has changed is not relivant.
5529  *
5530  * Fork - LGPL
5531  * <script type="text/javascript">
5532  */
5533  
5534 // Base class for reading structured data from a data source.  This class is intended to be
5535 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5536
5537 /**
5538  * @class Roo.data.DataReader
5539  * Base class for reading structured data from a data source.  This class is intended to be
5540  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5541  */
5542
5543 Roo.data.DataReader = function(meta, recordType){
5544     
5545     this.meta = meta;
5546     
5547     this.recordType = recordType instanceof Array ? 
5548         Roo.data.Record.create(recordType) : recordType;
5549 };
5550
5551 Roo.data.DataReader.prototype = {
5552      /**
5553      * Create an empty record
5554      * @param {Object} data (optional) - overlay some values
5555      * @return {Roo.data.Record} record created.
5556      */
5557     newRow :  function(d) {
5558         var da =  {};
5559         this.recordType.prototype.fields.each(function(c) {
5560             switch( c.type) {
5561                 case 'int' : da[c.name] = 0; break;
5562                 case 'date' : da[c.name] = new Date(); break;
5563                 case 'float' : da[c.name] = 0.0; break;
5564                 case 'boolean' : da[c.name] = false; break;
5565                 default : da[c.name] = ""; break;
5566             }
5567             
5568         });
5569         return new this.recordType(Roo.apply(da, d));
5570     }
5571     
5572 };/*
5573  * Based on:
5574  * Ext JS Library 1.1.1
5575  * Copyright(c) 2006-2007, Ext JS, LLC.
5576  *
5577  * Originally Released Under LGPL - original licence link has changed is not relivant.
5578  *
5579  * Fork - LGPL
5580  * <script type="text/javascript">
5581  */
5582
5583 /**
5584  * @class Roo.data.DataProxy
5585  * @extends Roo.data.Observable
5586  * This class is an abstract base class for implementations which provide retrieval of
5587  * unformatted data objects.<br>
5588  * <p>
5589  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5590  * (of the appropriate type which knows how to parse the data object) to provide a block of
5591  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5592  * <p>
5593  * Custom implementations must implement the load method as described in
5594  * {@link Roo.data.HttpProxy#load}.
5595  */
5596 Roo.data.DataProxy = function(){
5597     this.addEvents({
5598         /**
5599          * @event beforeload
5600          * Fires before a network request is made to retrieve a data object.
5601          * @param {Object} This DataProxy object.
5602          * @param {Object} params The params parameter to the load function.
5603          */
5604         beforeload : true,
5605         /**
5606          * @event load
5607          * Fires before the load method's callback is called.
5608          * @param {Object} This DataProxy object.
5609          * @param {Object} o The data object.
5610          * @param {Object} arg The callback argument object passed to the load function.
5611          */
5612         load : true,
5613         /**
5614          * @event loadexception
5615          * Fires if an Exception occurs during data retrieval.
5616          * @param {Object} This DataProxy object.
5617          * @param {Object} o The data object.
5618          * @param {Object} arg The callback argument object passed to the load function.
5619          * @param {Object} e The Exception.
5620          */
5621         loadexception : true
5622     });
5623     Roo.data.DataProxy.superclass.constructor.call(this);
5624 };
5625
5626 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5627
5628     /**
5629      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5630      */
5631 /*
5632  * Based on:
5633  * Ext JS Library 1.1.1
5634  * Copyright(c) 2006-2007, Ext JS, LLC.
5635  *
5636  * Originally Released Under LGPL - original licence link has changed is not relivant.
5637  *
5638  * Fork - LGPL
5639  * <script type="text/javascript">
5640  */
5641 /**
5642  * @class Roo.data.MemoryProxy
5643  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5644  * to the Reader when its load method is called.
5645  * @constructor
5646  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5647  */
5648 Roo.data.MemoryProxy = function(data){
5649     if (data.data) {
5650         data = data.data;
5651     }
5652     Roo.data.MemoryProxy.superclass.constructor.call(this);
5653     this.data = data;
5654 };
5655
5656 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5657     /**
5658      * Load data from the requested source (in this case an in-memory
5659      * data object passed to the constructor), read the data object into
5660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5661      * process that block using the passed callback.
5662      * @param {Object} params This parameter is not used by the MemoryProxy class.
5663      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5664      * object into a block of Roo.data.Records.
5665      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5666      * The function must be passed <ul>
5667      * <li>The Record block object</li>
5668      * <li>The "arg" argument from the load function</li>
5669      * <li>A boolean success indicator</li>
5670      * </ul>
5671      * @param {Object} scope The scope in which to call the callback
5672      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5673      */
5674     load : function(params, reader, callback, scope, arg){
5675         params = params || {};
5676         var result;
5677         try {
5678             result = reader.readRecords(this.data);
5679         }catch(e){
5680             this.fireEvent("loadexception", this, arg, null, e);
5681             callback.call(scope, null, arg, false);
5682             return;
5683         }
5684         callback.call(scope, result, arg, true);
5685     },
5686     
5687     // private
5688     update : function(params, records){
5689         
5690     }
5691 });/*
5692  * Based on:
5693  * Ext JS Library 1.1.1
5694  * Copyright(c) 2006-2007, Ext JS, LLC.
5695  *
5696  * Originally Released Under LGPL - original licence link has changed is not relivant.
5697  *
5698  * Fork - LGPL
5699  * <script type="text/javascript">
5700  */
5701 /**
5702  * @class Roo.data.HttpProxy
5703  * @extends Roo.data.DataProxy
5704  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5705  * configured to reference a certain URL.<br><br>
5706  * <p>
5707  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5708  * from which the running page was served.<br><br>
5709  * <p>
5710  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5711  * <p>
5712  * Be aware that to enable the browser to parse an XML document, the server must set
5713  * the Content-Type header in the HTTP response to "text/xml".
5714  * @constructor
5715  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5716  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5717  * will be used to make the request.
5718  */
5719 Roo.data.HttpProxy = function(conn){
5720     Roo.data.HttpProxy.superclass.constructor.call(this);
5721     // is conn a conn config or a real conn?
5722     this.conn = conn;
5723     this.useAjax = !conn || !conn.events;
5724   
5725 };
5726
5727 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5728     // thse are take from connection...
5729     
5730     /**
5731      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5732      */
5733     /**
5734      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5735      * extra parameters to each request made by this object. (defaults to undefined)
5736      */
5737     /**
5738      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5739      *  to each request made by this object. (defaults to undefined)
5740      */
5741     /**
5742      * @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)
5743      */
5744     /**
5745      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5746      */
5747      /**
5748      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5749      * @type Boolean
5750      */
5751   
5752
5753     /**
5754      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5755      * @type Boolean
5756      */
5757     /**
5758      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5759      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5760      * a finer-grained basis than the DataProxy events.
5761      */
5762     getConnection : function(){
5763         return this.useAjax ? Roo.Ajax : this.conn;
5764     },
5765
5766     /**
5767      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5768      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5769      * process that block using the passed callback.
5770      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5771      * for the request to the remote server.
5772      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5773      * object into a block of Roo.data.Records.
5774      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5775      * The function must be passed <ul>
5776      * <li>The Record block object</li>
5777      * <li>The "arg" argument from the load function</li>
5778      * <li>A boolean success indicator</li>
5779      * </ul>
5780      * @param {Object} scope The scope in which to call the callback
5781      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5782      */
5783     load : function(params, reader, callback, scope, arg){
5784         if(this.fireEvent("beforeload", this, params) !== false){
5785             var  o = {
5786                 params : params || {},
5787                 request: {
5788                     callback : callback,
5789                     scope : scope,
5790                     arg : arg
5791                 },
5792                 reader: reader,
5793                 callback : this.loadResponse,
5794                 scope: this
5795             };
5796             if(this.useAjax){
5797                 Roo.applyIf(o, this.conn);
5798                 if(this.activeRequest){
5799                     Roo.Ajax.abort(this.activeRequest);
5800                 }
5801                 this.activeRequest = Roo.Ajax.request(o);
5802             }else{
5803                 this.conn.request(o);
5804             }
5805         }else{
5806             callback.call(scope||this, null, arg, false);
5807         }
5808     },
5809
5810     // private
5811     loadResponse : function(o, success, response){
5812         delete this.activeRequest;
5813         if(!success){
5814             this.fireEvent("loadexception", this, o, response);
5815             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5816             return;
5817         }
5818         var result;
5819         try {
5820             result = o.reader.read(response);
5821         }catch(e){
5822             this.fireEvent("loadexception", this, o, response, e);
5823             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5824             return;
5825         }
5826         
5827         this.fireEvent("load", this, o, o.request.arg);
5828         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5829     },
5830
5831     // private
5832     update : function(dataSet){
5833
5834     },
5835
5836     // private
5837     updateResponse : function(dataSet){
5838
5839     }
5840 });/*
5841  * Based on:
5842  * Ext JS Library 1.1.1
5843  * Copyright(c) 2006-2007, Ext JS, LLC.
5844  *
5845  * Originally Released Under LGPL - original licence link has changed is not relivant.
5846  *
5847  * Fork - LGPL
5848  * <script type="text/javascript">
5849  */
5850
5851 /**
5852  * @class Roo.data.ScriptTagProxy
5853  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5854  * other than the originating domain of the running page.<br><br>
5855  * <p>
5856  * <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
5857  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5858  * <p>
5859  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5860  * source code that is used as the source inside a &lt;script> tag.<br><br>
5861  * <p>
5862  * In order for the browser to process the returned data, the server must wrap the data object
5863  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5864  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5865  * depending on whether the callback name was passed:
5866  * <p>
5867  * <pre><code>
5868 boolean scriptTag = false;
5869 String cb = request.getParameter("callback");
5870 if (cb != null) {
5871     scriptTag = true;
5872     response.setContentType("text/javascript");
5873 } else {
5874     response.setContentType("application/x-json");
5875 }
5876 Writer out = response.getWriter();
5877 if (scriptTag) {
5878     out.write(cb + "(");
5879 }
5880 out.print(dataBlock.toJsonString());
5881 if (scriptTag) {
5882     out.write(");");
5883 }
5884 </pre></code>
5885  *
5886  * @constructor
5887  * @param {Object} config A configuration object.
5888  */
5889 Roo.data.ScriptTagProxy = function(config){
5890     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5891     Roo.apply(this, config);
5892     this.head = document.getElementsByTagName("head")[0];
5893 };
5894
5895 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5896
5897 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5898     /**
5899      * @cfg {String} url The URL from which to request the data object.
5900      */
5901     /**
5902      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5903      */
5904     timeout : 30000,
5905     /**
5906      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5907      * the server the name of the callback function set up by the load call to process the returned data object.
5908      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5909      * javascript output which calls this named function passing the data object as its only parameter.
5910      */
5911     callbackParam : "callback",
5912     /**
5913      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5914      * name to the request.
5915      */
5916     nocache : true,
5917
5918     /**
5919      * Load data from the configured URL, read the data object into
5920      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5921      * process that block using the passed callback.
5922      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5923      * for the request to the remote server.
5924      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5925      * object into a block of Roo.data.Records.
5926      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5927      * The function must be passed <ul>
5928      * <li>The Record block object</li>
5929      * <li>The "arg" argument from the load function</li>
5930      * <li>A boolean success indicator</li>
5931      * </ul>
5932      * @param {Object} scope The scope in which to call the callback
5933      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5934      */
5935     load : function(params, reader, callback, scope, arg){
5936         if(this.fireEvent("beforeload", this, params) !== false){
5937
5938             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5939
5940             var url = this.url;
5941             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5942             if(this.nocache){
5943                 url += "&_dc=" + (new Date().getTime());
5944             }
5945             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5946             var trans = {
5947                 id : transId,
5948                 cb : "stcCallback"+transId,
5949                 scriptId : "stcScript"+transId,
5950                 params : params,
5951                 arg : arg,
5952                 url : url,
5953                 callback : callback,
5954                 scope : scope,
5955                 reader : reader
5956             };
5957             var conn = this;
5958
5959             window[trans.cb] = function(o){
5960                 conn.handleResponse(o, trans);
5961             };
5962
5963             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5964
5965             if(this.autoAbort !== false){
5966                 this.abort();
5967             }
5968
5969             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5970
5971             var script = document.createElement("script");
5972             script.setAttribute("src", url);
5973             script.setAttribute("type", "text/javascript");
5974             script.setAttribute("id", trans.scriptId);
5975             this.head.appendChild(script);
5976
5977             this.trans = trans;
5978         }else{
5979             callback.call(scope||this, null, arg, false);
5980         }
5981     },
5982
5983     // private
5984     isLoading : function(){
5985         return this.trans ? true : false;
5986     },
5987
5988     /**
5989      * Abort the current server request.
5990      */
5991     abort : function(){
5992         if(this.isLoading()){
5993             this.destroyTrans(this.trans);
5994         }
5995     },
5996
5997     // private
5998     destroyTrans : function(trans, isLoaded){
5999         this.head.removeChild(document.getElementById(trans.scriptId));
6000         clearTimeout(trans.timeoutId);
6001         if(isLoaded){
6002             window[trans.cb] = undefined;
6003             try{
6004                 delete window[trans.cb];
6005             }catch(e){}
6006         }else{
6007             // if hasn't been loaded, wait for load to remove it to prevent script error
6008             window[trans.cb] = function(){
6009                 window[trans.cb] = undefined;
6010                 try{
6011                     delete window[trans.cb];
6012                 }catch(e){}
6013             };
6014         }
6015     },
6016
6017     // private
6018     handleResponse : function(o, trans){
6019         this.trans = false;
6020         this.destroyTrans(trans, true);
6021         var result;
6022         try {
6023             result = trans.reader.readRecords(o);
6024         }catch(e){
6025             this.fireEvent("loadexception", this, o, trans.arg, e);
6026             trans.callback.call(trans.scope||window, null, trans.arg, false);
6027             return;
6028         }
6029         this.fireEvent("load", this, o, trans.arg);
6030         trans.callback.call(trans.scope||window, result, trans.arg, true);
6031     },
6032
6033     // private
6034     handleFailure : function(trans){
6035         this.trans = false;
6036         this.destroyTrans(trans, false);
6037         this.fireEvent("loadexception", this, null, trans.arg);
6038         trans.callback.call(trans.scope||window, null, trans.arg, false);
6039     }
6040 });/*
6041  * Based on:
6042  * Ext JS Library 1.1.1
6043  * Copyright(c) 2006-2007, Ext JS, LLC.
6044  *
6045  * Originally Released Under LGPL - original licence link has changed is not relivant.
6046  *
6047  * Fork - LGPL
6048  * <script type="text/javascript">
6049  */
6050
6051 /**
6052  * @class Roo.data.JsonReader
6053  * @extends Roo.data.DataReader
6054  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6055  * based on mappings in a provided Roo.data.Record constructor.
6056  * 
6057  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6058  * in the reply previously. 
6059  * 
6060  * <p>
6061  * Example code:
6062  * <pre><code>
6063 var RecordDef = Roo.data.Record.create([
6064     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6065     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6066 ]);
6067 var myReader = new Roo.data.JsonReader({
6068     totalProperty: "results",    // The property which contains the total dataset size (optional)
6069     root: "rows",                // The property which contains an Array of row objects
6070     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6071 }, RecordDef);
6072 </code></pre>
6073  * <p>
6074  * This would consume a JSON file like this:
6075  * <pre><code>
6076 { 'results': 2, 'rows': [
6077     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6078     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6079 }
6080 </code></pre>
6081  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6082  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6083  * paged from the remote server.
6084  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6085  * @cfg {String} root name of the property which contains the Array of row objects.
6086  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6087  * @constructor
6088  * Create a new JsonReader
6089  * @param {Object} meta Metadata configuration options
6090  * @param {Object} recordType Either an Array of field definition objects,
6091  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6092  */
6093 Roo.data.JsonReader = function(meta, recordType){
6094     
6095     meta = meta || {};
6096     // set some defaults:
6097     Roo.applyIf(meta, {
6098         totalProperty: 'total',
6099         successProperty : 'success',
6100         root : 'data',
6101         id : 'id'
6102     });
6103     
6104     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6105 };
6106 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6107     
6108     /**
6109      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6110      * Used by Store query builder to append _requestMeta to params.
6111      * 
6112      */
6113     metaFromRemote : false,
6114     /**
6115      * This method is only used by a DataProxy which has retrieved data from a remote server.
6116      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6117      * @return {Object} data A data block which is used by an Roo.data.Store object as
6118      * a cache of Roo.data.Records.
6119      */
6120     read : function(response){
6121         var json = response.responseText;
6122        
6123         var o = /* eval:var:o */ eval("("+json+")");
6124         if(!o) {
6125             throw {message: "JsonReader.read: Json object not found"};
6126         }
6127         
6128         if(o.metaData){
6129             
6130             delete this.ef;
6131             this.metaFromRemote = true;
6132             this.meta = o.metaData;
6133             this.recordType = Roo.data.Record.create(o.metaData.fields);
6134             this.onMetaChange(this.meta, this.recordType, o);
6135         }
6136         return this.readRecords(o);
6137     },
6138
6139     // private function a store will implement
6140     onMetaChange : function(meta, recordType, o){
6141
6142     },
6143
6144     /**
6145          * @ignore
6146          */
6147     simpleAccess: function(obj, subsc) {
6148         return obj[subsc];
6149     },
6150
6151         /**
6152          * @ignore
6153          */
6154     getJsonAccessor: function(){
6155         var re = /[\[\.]/;
6156         return function(expr) {
6157             try {
6158                 return(re.test(expr))
6159                     ? new Function("obj", "return obj." + expr)
6160                     : function(obj){
6161                         return obj[expr];
6162                     };
6163             } catch(e){}
6164             return Roo.emptyFn;
6165         };
6166     }(),
6167
6168     /**
6169      * Create a data block containing Roo.data.Records from an XML document.
6170      * @param {Object} o An object which contains an Array of row objects in the property specified
6171      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6172      * which contains the total size of the dataset.
6173      * @return {Object} data A data block which is used by an Roo.data.Store object as
6174      * a cache of Roo.data.Records.
6175      */
6176     readRecords : function(o){
6177         /**
6178          * After any data loads, the raw JSON data is available for further custom processing.
6179          * @type Object
6180          */
6181         this.jsonData = o;
6182         var s = this.meta, Record = this.recordType,
6183             f = Record.prototype.fields, fi = f.items, fl = f.length;
6184
6185 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6186         if (!this.ef) {
6187             if(s.totalProperty) {
6188                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6189                 }
6190                 if(s.successProperty) {
6191                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6192                 }
6193                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6194                 if (s.id) {
6195                         var g = this.getJsonAccessor(s.id);
6196                         this.getId = function(rec) {
6197                                 var r = g(rec);
6198                                 return (r === undefined || r === "") ? null : r;
6199                         };
6200                 } else {
6201                         this.getId = function(){return null;};
6202                 }
6203             this.ef = [];
6204             for(var jj = 0; jj < fl; jj++){
6205                 f = fi[jj];
6206                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6207                 this.ef[jj] = this.getJsonAccessor(map);
6208             }
6209         }
6210
6211         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6212         if(s.totalProperty){
6213             var vt = parseInt(this.getTotal(o), 10);
6214             if(!isNaN(vt)){
6215                 totalRecords = vt;
6216             }
6217         }
6218         if(s.successProperty){
6219             var vs = this.getSuccess(o);
6220             if(vs === false || vs === 'false'){
6221                 success = false;
6222             }
6223         }
6224         var records = [];
6225             for(var i = 0; i < c; i++){
6226                     var n = root[i];
6227                 var values = {};
6228                 var id = this.getId(n);
6229                 for(var j = 0; j < fl; j++){
6230                     f = fi[j];
6231                 var v = this.ef[j](n);
6232                 if (!f.convert) {
6233                     Roo.log('missing convert for ' + f.name);
6234                     Roo.log(f);
6235                     continue;
6236                 }
6237                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6238                 }
6239                 var record = new Record(values, id);
6240                 record.json = n;
6241                 records[i] = record;
6242             }
6243             return {
6244                 success : success,
6245                 records : records,
6246                 totalRecords : totalRecords
6247             };
6248     }
6249 });/*
6250  * Based on:
6251  * Ext JS Library 1.1.1
6252  * Copyright(c) 2006-2007, Ext JS, LLC.
6253  *
6254  * Originally Released Under LGPL - original licence link has changed is not relivant.
6255  *
6256  * Fork - LGPL
6257  * <script type="text/javascript">
6258  */
6259
6260 /**
6261  * @class Roo.data.XmlReader
6262  * @extends Roo.data.DataReader
6263  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6264  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6265  * <p>
6266  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6267  * header in the HTTP response must be set to "text/xml".</em>
6268  * <p>
6269  * Example code:
6270  * <pre><code>
6271 var RecordDef = Roo.data.Record.create([
6272    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6273    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6274 ]);
6275 var myReader = new Roo.data.XmlReader({
6276    totalRecords: "results", // The element which contains the total dataset size (optional)
6277    record: "row",           // The repeated element which contains row information
6278    id: "id"                 // The element within the row that provides an ID for the record (optional)
6279 }, RecordDef);
6280 </code></pre>
6281  * <p>
6282  * This would consume an XML file like this:
6283  * <pre><code>
6284 &lt;?xml?>
6285 &lt;dataset>
6286  &lt;results>2&lt;/results>
6287  &lt;row>
6288    &lt;id>1&lt;/id>
6289    &lt;name>Bill&lt;/name>
6290    &lt;occupation>Gardener&lt;/occupation>
6291  &lt;/row>
6292  &lt;row>
6293    &lt;id>2&lt;/id>
6294    &lt;name>Ben&lt;/name>
6295    &lt;occupation>Horticulturalist&lt;/occupation>
6296  &lt;/row>
6297 &lt;/dataset>
6298 </code></pre>
6299  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6300  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6301  * paged from the remote server.
6302  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6303  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6304  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6305  * a record identifier value.
6306  * @constructor
6307  * Create a new XmlReader
6308  * @param {Object} meta Metadata configuration options
6309  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6310  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6311  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6312  */
6313 Roo.data.XmlReader = function(meta, recordType){
6314     meta = meta || {};
6315     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6316 };
6317 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6318     /**
6319      * This method is only used by a DataProxy which has retrieved data from a remote server.
6320          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6321          * to contain a method called 'responseXML' that returns an XML document object.
6322      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6323      * a cache of Roo.data.Records.
6324      */
6325     read : function(response){
6326         var doc = response.responseXML;
6327         if(!doc) {
6328             throw {message: "XmlReader.read: XML Document not available"};
6329         }
6330         return this.readRecords(doc);
6331     },
6332
6333     /**
6334      * Create a data block containing Roo.data.Records from an XML document.
6335          * @param {Object} doc A parsed XML document.
6336      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6337      * a cache of Roo.data.Records.
6338      */
6339     readRecords : function(doc){
6340         /**
6341          * After any data loads/reads, the raw XML Document is available for further custom processing.
6342          * @type XMLDocument
6343          */
6344         this.xmlData = doc;
6345         var root = doc.documentElement || doc;
6346         var q = Roo.DomQuery;
6347         var recordType = this.recordType, fields = recordType.prototype.fields;
6348         var sid = this.meta.id;
6349         var totalRecords = 0, success = true;
6350         if(this.meta.totalRecords){
6351             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6352         }
6353         
6354         if(this.meta.success){
6355             var sv = q.selectValue(this.meta.success, root, true);
6356             success = sv !== false && sv !== 'false';
6357         }
6358         var records = [];
6359         var ns = q.select(this.meta.record, root);
6360         for(var i = 0, len = ns.length; i < len; i++) {
6361                 var n = ns[i];
6362                 var values = {};
6363                 var id = sid ? q.selectValue(sid, n) : undefined;
6364                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6365                     var f = fields.items[j];
6366                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6367                     v = f.convert(v);
6368                     values[f.name] = v;
6369                 }
6370                 var record = new recordType(values, id);
6371                 record.node = n;
6372                 records[records.length] = record;
6373             }
6374
6375             return {
6376                 success : success,
6377                 records : records,
6378                 totalRecords : totalRecords || records.length
6379             };
6380     }
6381 });/*
6382  * Based on:
6383  * Ext JS Library 1.1.1
6384  * Copyright(c) 2006-2007, Ext JS, LLC.
6385  *
6386  * Originally Released Under LGPL - original licence link has changed is not relivant.
6387  *
6388  * Fork - LGPL
6389  * <script type="text/javascript">
6390  */
6391
6392 /**
6393  * @class Roo.data.ArrayReader
6394  * @extends Roo.data.DataReader
6395  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6396  * Each element of that Array represents a row of data fields. The
6397  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6398  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6399  * <p>
6400  * Example code:.
6401  * <pre><code>
6402 var RecordDef = Roo.data.Record.create([
6403     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6404     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6405 ]);
6406 var myReader = new Roo.data.ArrayReader({
6407     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6408 }, RecordDef);
6409 </code></pre>
6410  * <p>
6411  * This would consume an Array like this:
6412  * <pre><code>
6413 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6414   </code></pre>
6415  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6416  * @constructor
6417  * Create a new JsonReader
6418  * @param {Object} meta Metadata configuration options.
6419  * @param {Object} recordType Either an Array of field definition objects
6420  * as specified to {@link Roo.data.Record#create},
6421  * or an {@link Roo.data.Record} object
6422  * created using {@link Roo.data.Record#create}.
6423  */
6424 Roo.data.ArrayReader = function(meta, recordType){
6425     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6426 };
6427
6428 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6429     /**
6430      * Create a data block containing Roo.data.Records from an XML document.
6431      * @param {Object} o An Array of row objects which represents the dataset.
6432      * @return {Object} data A data block which is used by an Roo.data.Store object as
6433      * a cache of Roo.data.Records.
6434      */
6435     readRecords : function(o){
6436         var sid = this.meta ? this.meta.id : null;
6437         var recordType = this.recordType, fields = recordType.prototype.fields;
6438         var records = [];
6439         var root = o;
6440             for(var i = 0; i < root.length; i++){
6441                     var n = root[i];
6442                 var values = {};
6443                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6444                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6445                 var f = fields.items[j];
6446                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6447                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6448                 v = f.convert(v);
6449                 values[f.name] = v;
6450             }
6451                 var record = new recordType(values, id);
6452                 record.json = n;
6453                 records[records.length] = record;
6454             }
6455             return {
6456                 records : records,
6457                 totalRecords : records.length
6458             };
6459     }
6460 });/*
6461  * Based on:
6462  * Ext JS Library 1.1.1
6463  * Copyright(c) 2006-2007, Ext JS, LLC.
6464  *
6465  * Originally Released Under LGPL - original licence link has changed is not relivant.
6466  *
6467  * Fork - LGPL
6468  * <script type="text/javascript">
6469  */
6470
6471
6472 /**
6473  * @class Roo.data.Tree
6474  * @extends Roo.util.Observable
6475  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6476  * in the tree have most standard DOM functionality.
6477  * @constructor
6478  * @param {Node} root (optional) The root node
6479  */
6480 Roo.data.Tree = function(root){
6481    this.nodeHash = {};
6482    /**
6483     * The root node for this tree
6484     * @type Node
6485     */
6486    this.root = null;
6487    if(root){
6488        this.setRootNode(root);
6489    }
6490    this.addEvents({
6491        /**
6492         * @event append
6493         * Fires when a new child node is appended to a node in this tree.
6494         * @param {Tree} tree The owner tree
6495         * @param {Node} parent The parent node
6496         * @param {Node} node The newly appended node
6497         * @param {Number} index The index of the newly appended node
6498         */
6499        "append" : true,
6500        /**
6501         * @event remove
6502         * Fires when a child node is removed from a node in this tree.
6503         * @param {Tree} tree The owner tree
6504         * @param {Node} parent The parent node
6505         * @param {Node} node The child node removed
6506         */
6507        "remove" : true,
6508        /**
6509         * @event move
6510         * Fires when a node is moved to a new location in the tree
6511         * @param {Tree} tree The owner tree
6512         * @param {Node} node The node moved
6513         * @param {Node} oldParent The old parent of this node
6514         * @param {Node} newParent The new parent of this node
6515         * @param {Number} index The index it was moved to
6516         */
6517        "move" : true,
6518        /**
6519         * @event insert
6520         * Fires when a new child node is inserted in a node in this tree.
6521         * @param {Tree} tree The owner tree
6522         * @param {Node} parent The parent node
6523         * @param {Node} node The child node inserted
6524         * @param {Node} refNode The child node the node was inserted before
6525         */
6526        "insert" : true,
6527        /**
6528         * @event beforeappend
6529         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6530         * @param {Tree} tree The owner tree
6531         * @param {Node} parent The parent node
6532         * @param {Node} node The child node to be appended
6533         */
6534        "beforeappend" : true,
6535        /**
6536         * @event beforeremove
6537         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6538         * @param {Tree} tree The owner tree
6539         * @param {Node} parent The parent node
6540         * @param {Node} node The child node to be removed
6541         */
6542        "beforeremove" : true,
6543        /**
6544         * @event beforemove
6545         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6546         * @param {Tree} tree The owner tree
6547         * @param {Node} node The node being moved
6548         * @param {Node} oldParent The parent of the node
6549         * @param {Node} newParent The new parent the node is moving to
6550         * @param {Number} index The index it is being moved to
6551         */
6552        "beforemove" : true,
6553        /**
6554         * @event beforeinsert
6555         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6556         * @param {Tree} tree The owner tree
6557         * @param {Node} parent The parent node
6558         * @param {Node} node The child node to be inserted
6559         * @param {Node} refNode The child node the node is being inserted before
6560         */
6561        "beforeinsert" : true
6562    });
6563
6564     Roo.data.Tree.superclass.constructor.call(this);
6565 };
6566
6567 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6568     pathSeparator: "/",
6569
6570     proxyNodeEvent : function(){
6571         return this.fireEvent.apply(this, arguments);
6572     },
6573
6574     /**
6575      * Returns the root node for this tree.
6576      * @return {Node}
6577      */
6578     getRootNode : function(){
6579         return this.root;
6580     },
6581
6582     /**
6583      * Sets the root node for this tree.
6584      * @param {Node} node
6585      * @return {Node}
6586      */
6587     setRootNode : function(node){
6588         this.root = node;
6589         node.ownerTree = this;
6590         node.isRoot = true;
6591         this.registerNode(node);
6592         return node;
6593     },
6594
6595     /**
6596      * Gets a node in this tree by its id.
6597      * @param {String} id
6598      * @return {Node}
6599      */
6600     getNodeById : function(id){
6601         return this.nodeHash[id];
6602     },
6603
6604     registerNode : function(node){
6605         this.nodeHash[node.id] = node;
6606     },
6607
6608     unregisterNode : function(node){
6609         delete this.nodeHash[node.id];
6610     },
6611
6612     toString : function(){
6613         return "[Tree"+(this.id?" "+this.id:"")+"]";
6614     }
6615 });
6616
6617 /**
6618  * @class Roo.data.Node
6619  * @extends Roo.util.Observable
6620  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6621  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6622  * @constructor
6623  * @param {Object} attributes The attributes/config for the node
6624  */
6625 Roo.data.Node = function(attributes){
6626     /**
6627      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6628      * @type {Object}
6629      */
6630     this.attributes = attributes || {};
6631     this.leaf = this.attributes.leaf;
6632     /**
6633      * The node id. @type String
6634      */
6635     this.id = this.attributes.id;
6636     if(!this.id){
6637         this.id = Roo.id(null, "ynode-");
6638         this.attributes.id = this.id;
6639     }
6640     /**
6641      * All child nodes of this node. @type Array
6642      */
6643     this.childNodes = [];
6644     if(!this.childNodes.indexOf){ // indexOf is a must
6645         this.childNodes.indexOf = function(o){
6646             for(var i = 0, len = this.length; i < len; i++){
6647                 if(this[i] == o) {
6648                     return i;
6649                 }
6650             }
6651             return -1;
6652         };
6653     }
6654     /**
6655      * The parent node for this node. @type Node
6656      */
6657     this.parentNode = null;
6658     /**
6659      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6660      */
6661     this.firstChild = null;
6662     /**
6663      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6664      */
6665     this.lastChild = null;
6666     /**
6667      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6668      */
6669     this.previousSibling = null;
6670     /**
6671      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6672      */
6673     this.nextSibling = null;
6674
6675     this.addEvents({
6676        /**
6677         * @event append
6678         * Fires when a new child node is appended
6679         * @param {Tree} tree The owner tree
6680         * @param {Node} this This node
6681         * @param {Node} node The newly appended node
6682         * @param {Number} index The index of the newly appended node
6683         */
6684        "append" : true,
6685        /**
6686         * @event remove
6687         * Fires when a child node is removed
6688         * @param {Tree} tree The owner tree
6689         * @param {Node} this This node
6690         * @param {Node} node The removed node
6691         */
6692        "remove" : true,
6693        /**
6694         * @event move
6695         * Fires when this node is moved to a new location in the tree
6696         * @param {Tree} tree The owner tree
6697         * @param {Node} this This node
6698         * @param {Node} oldParent The old parent of this node
6699         * @param {Node} newParent The new parent of this node
6700         * @param {Number} index The index it was moved to
6701         */
6702        "move" : true,
6703        /**
6704         * @event insert
6705         * Fires when a new child node is inserted.
6706         * @param {Tree} tree The owner tree
6707         * @param {Node} this This node
6708         * @param {Node} node The child node inserted
6709         * @param {Node} refNode The child node the node was inserted before
6710         */
6711        "insert" : true,
6712        /**
6713         * @event beforeappend
6714         * Fires before a new child is appended, return false to cancel the append.
6715         * @param {Tree} tree The owner tree
6716         * @param {Node} this This node
6717         * @param {Node} node The child node to be appended
6718         */
6719        "beforeappend" : true,
6720        /**
6721         * @event beforeremove
6722         * Fires before a child is removed, return false to cancel the remove.
6723         * @param {Tree} tree The owner tree
6724         * @param {Node} this This node
6725         * @param {Node} node The child node to be removed
6726         */
6727        "beforeremove" : true,
6728        /**
6729         * @event beforemove
6730         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6731         * @param {Tree} tree The owner tree
6732         * @param {Node} this This node
6733         * @param {Node} oldParent The parent of this node
6734         * @param {Node} newParent The new parent this node is moving to
6735         * @param {Number} index The index it is being moved to
6736         */
6737        "beforemove" : true,
6738        /**
6739         * @event beforeinsert
6740         * Fires before a new child is inserted, return false to cancel the insert.
6741         * @param {Tree} tree The owner tree
6742         * @param {Node} this This node
6743         * @param {Node} node The child node to be inserted
6744         * @param {Node} refNode The child node the node is being inserted before
6745         */
6746        "beforeinsert" : true
6747    });
6748     this.listeners = this.attributes.listeners;
6749     Roo.data.Node.superclass.constructor.call(this);
6750 };
6751
6752 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6753     fireEvent : function(evtName){
6754         // first do standard event for this node
6755         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6756             return false;
6757         }
6758         // then bubble it up to the tree if the event wasn't cancelled
6759         var ot = this.getOwnerTree();
6760         if(ot){
6761             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6762                 return false;
6763             }
6764         }
6765         return true;
6766     },
6767
6768     /**
6769      * Returns true if this node is a leaf
6770      * @return {Boolean}
6771      */
6772     isLeaf : function(){
6773         return this.leaf === true;
6774     },
6775
6776     // private
6777     setFirstChild : function(node){
6778         this.firstChild = node;
6779     },
6780
6781     //private
6782     setLastChild : function(node){
6783         this.lastChild = node;
6784     },
6785
6786
6787     /**
6788      * Returns true if this node is the last child of its parent
6789      * @return {Boolean}
6790      */
6791     isLast : function(){
6792        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6793     },
6794
6795     /**
6796      * Returns true if this node is the first child of its parent
6797      * @return {Boolean}
6798      */
6799     isFirst : function(){
6800        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6801     },
6802
6803     hasChildNodes : function(){
6804         return !this.isLeaf() && this.childNodes.length > 0;
6805     },
6806
6807     /**
6808      * Insert node(s) as the last child node of this node.
6809      * @param {Node/Array} node The node or Array of nodes to append
6810      * @return {Node} The appended node if single append, or null if an array was passed
6811      */
6812     appendChild : function(node){
6813         var multi = false;
6814         if(node instanceof Array){
6815             multi = node;
6816         }else if(arguments.length > 1){
6817             multi = arguments;
6818         }
6819         // if passed an array or multiple args do them one by one
6820         if(multi){
6821             for(var i = 0, len = multi.length; i < len; i++) {
6822                 this.appendChild(multi[i]);
6823             }
6824         }else{
6825             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6826                 return false;
6827             }
6828             var index = this.childNodes.length;
6829             var oldParent = node.parentNode;
6830             // it's a move, make sure we move it cleanly
6831             if(oldParent){
6832                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6833                     return false;
6834                 }
6835                 oldParent.removeChild(node);
6836             }
6837             index = this.childNodes.length;
6838             if(index == 0){
6839                 this.setFirstChild(node);
6840             }
6841             this.childNodes.push(node);
6842             node.parentNode = this;
6843             var ps = this.childNodes[index-1];
6844             if(ps){
6845                 node.previousSibling = ps;
6846                 ps.nextSibling = node;
6847             }else{
6848                 node.previousSibling = null;
6849             }
6850             node.nextSibling = null;
6851             this.setLastChild(node);
6852             node.setOwnerTree(this.getOwnerTree());
6853             this.fireEvent("append", this.ownerTree, this, node, index);
6854             if(oldParent){
6855                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6856             }
6857             return node;
6858         }
6859     },
6860
6861     /**
6862      * Removes a child node from this node.
6863      * @param {Node} node The node to remove
6864      * @return {Node} The removed node
6865      */
6866     removeChild : function(node){
6867         var index = this.childNodes.indexOf(node);
6868         if(index == -1){
6869             return false;
6870         }
6871         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6872             return false;
6873         }
6874
6875         // remove it from childNodes collection
6876         this.childNodes.splice(index, 1);
6877
6878         // update siblings
6879         if(node.previousSibling){
6880             node.previousSibling.nextSibling = node.nextSibling;
6881         }
6882         if(node.nextSibling){
6883             node.nextSibling.previousSibling = node.previousSibling;
6884         }
6885
6886         // update child refs
6887         if(this.firstChild == node){
6888             this.setFirstChild(node.nextSibling);
6889         }
6890         if(this.lastChild == node){
6891             this.setLastChild(node.previousSibling);
6892         }
6893
6894         node.setOwnerTree(null);
6895         // clear any references from the node
6896         node.parentNode = null;
6897         node.previousSibling = null;
6898         node.nextSibling = null;
6899         this.fireEvent("remove", this.ownerTree, this, node);
6900         return node;
6901     },
6902
6903     /**
6904      * Inserts the first node before the second node in this nodes childNodes collection.
6905      * @param {Node} node The node to insert
6906      * @param {Node} refNode The node to insert before (if null the node is appended)
6907      * @return {Node} The inserted node
6908      */
6909     insertBefore : function(node, refNode){
6910         if(!refNode){ // like standard Dom, refNode can be null for append
6911             return this.appendChild(node);
6912         }
6913         // nothing to do
6914         if(node == refNode){
6915             return false;
6916         }
6917
6918         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6919             return false;
6920         }
6921         var index = this.childNodes.indexOf(refNode);
6922         var oldParent = node.parentNode;
6923         var refIndex = index;
6924
6925         // when moving internally, indexes will change after remove
6926         if(oldParent == this && this.childNodes.indexOf(node) < index){
6927             refIndex--;
6928         }
6929
6930         // it's a move, make sure we move it cleanly
6931         if(oldParent){
6932             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6933                 return false;
6934             }
6935             oldParent.removeChild(node);
6936         }
6937         if(refIndex == 0){
6938             this.setFirstChild(node);
6939         }
6940         this.childNodes.splice(refIndex, 0, node);
6941         node.parentNode = this;
6942         var ps = this.childNodes[refIndex-1];
6943         if(ps){
6944             node.previousSibling = ps;
6945             ps.nextSibling = node;
6946         }else{
6947             node.previousSibling = null;
6948         }
6949         node.nextSibling = refNode;
6950         refNode.previousSibling = node;
6951         node.setOwnerTree(this.getOwnerTree());
6952         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6953         if(oldParent){
6954             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6955         }
6956         return node;
6957     },
6958
6959     /**
6960      * Returns the child node at the specified index.
6961      * @param {Number} index
6962      * @return {Node}
6963      */
6964     item : function(index){
6965         return this.childNodes[index];
6966     },
6967
6968     /**
6969      * Replaces one child node in this node with another.
6970      * @param {Node} newChild The replacement node
6971      * @param {Node} oldChild The node to replace
6972      * @return {Node} The replaced node
6973      */
6974     replaceChild : function(newChild, oldChild){
6975         this.insertBefore(newChild, oldChild);
6976         this.removeChild(oldChild);
6977         return oldChild;
6978     },
6979
6980     /**
6981      * Returns the index of a child node
6982      * @param {Node} node
6983      * @return {Number} The index of the node or -1 if it was not found
6984      */
6985     indexOf : function(child){
6986         return this.childNodes.indexOf(child);
6987     },
6988
6989     /**
6990      * Returns the tree this node is in.
6991      * @return {Tree}
6992      */
6993     getOwnerTree : function(){
6994         // if it doesn't have one, look for one
6995         if(!this.ownerTree){
6996             var p = this;
6997             while(p){
6998                 if(p.ownerTree){
6999                     this.ownerTree = p.ownerTree;
7000                     break;
7001                 }
7002                 p = p.parentNode;
7003             }
7004         }
7005         return this.ownerTree;
7006     },
7007
7008     /**
7009      * Returns depth of this node (the root node has a depth of 0)
7010      * @return {Number}
7011      */
7012     getDepth : function(){
7013         var depth = 0;
7014         var p = this;
7015         while(p.parentNode){
7016             ++depth;
7017             p = p.parentNode;
7018         }
7019         return depth;
7020     },
7021
7022     // private
7023     setOwnerTree : function(tree){
7024         // if it's move, we need to update everyone
7025         if(tree != this.ownerTree){
7026             if(this.ownerTree){
7027                 this.ownerTree.unregisterNode(this);
7028             }
7029             this.ownerTree = tree;
7030             var cs = this.childNodes;
7031             for(var i = 0, len = cs.length; i < len; i++) {
7032                 cs[i].setOwnerTree(tree);
7033             }
7034             if(tree){
7035                 tree.registerNode(this);
7036             }
7037         }
7038     },
7039
7040     /**
7041      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7042      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7043      * @return {String} The path
7044      */
7045     getPath : function(attr){
7046         attr = attr || "id";
7047         var p = this.parentNode;
7048         var b = [this.attributes[attr]];
7049         while(p){
7050             b.unshift(p.attributes[attr]);
7051             p = p.parentNode;
7052         }
7053         var sep = this.getOwnerTree().pathSeparator;
7054         return sep + b.join(sep);
7055     },
7056
7057     /**
7058      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7059      * function call will be the scope provided or the current node. The arguments to the function
7060      * will be the args provided or the current node. If the function returns false at any point,
7061      * the bubble is stopped.
7062      * @param {Function} fn The function to call
7063      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7064      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7065      */
7066     bubble : function(fn, scope, args){
7067         var p = this;
7068         while(p){
7069             if(fn.call(scope || p, args || p) === false){
7070                 break;
7071             }
7072             p = p.parentNode;
7073         }
7074     },
7075
7076     /**
7077      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7078      * function call will be the scope provided or the current node. The arguments to the function
7079      * will be the args provided or the current node. If the function returns false at any point,
7080      * the cascade is stopped on that branch.
7081      * @param {Function} fn The function to call
7082      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7083      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7084      */
7085     cascade : function(fn, scope, args){
7086         if(fn.call(scope || this, args || this) !== false){
7087             var cs = this.childNodes;
7088             for(var i = 0, len = cs.length; i < len; i++) {
7089                 cs[i].cascade(fn, scope, args);
7090             }
7091         }
7092     },
7093
7094     /**
7095      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7096      * function call will be the scope provided or the current node. The arguments to the function
7097      * will be the args provided or the current node. If the function returns false at any point,
7098      * the iteration stops.
7099      * @param {Function} fn The function to call
7100      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7101      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7102      */
7103     eachChild : function(fn, scope, args){
7104         var cs = this.childNodes;
7105         for(var i = 0, len = cs.length; i < len; i++) {
7106                 if(fn.call(scope || this, args || cs[i]) === false){
7107                     break;
7108                 }
7109         }
7110     },
7111
7112     /**
7113      * Finds the first child that has the attribute with the specified value.
7114      * @param {String} attribute The attribute name
7115      * @param {Mixed} value The value to search for
7116      * @return {Node} The found child or null if none was found
7117      */
7118     findChild : function(attribute, value){
7119         var cs = this.childNodes;
7120         for(var i = 0, len = cs.length; i < len; i++) {
7121                 if(cs[i].attributes[attribute] == value){
7122                     return cs[i];
7123                 }
7124         }
7125         return null;
7126     },
7127
7128     /**
7129      * Finds the first child by a custom function. The child matches if the function passed
7130      * returns true.
7131      * @param {Function} fn
7132      * @param {Object} scope (optional)
7133      * @return {Node} The found child or null if none was found
7134      */
7135     findChildBy : function(fn, scope){
7136         var cs = this.childNodes;
7137         for(var i = 0, len = cs.length; i < len; i++) {
7138                 if(fn.call(scope||cs[i], cs[i]) === true){
7139                     return cs[i];
7140                 }
7141         }
7142         return null;
7143     },
7144
7145     /**
7146      * Sorts this nodes children using the supplied sort function
7147      * @param {Function} fn
7148      * @param {Object} scope (optional)
7149      */
7150     sort : function(fn, scope){
7151         var cs = this.childNodes;
7152         var len = cs.length;
7153         if(len > 0){
7154             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7155             cs.sort(sortFn);
7156             for(var i = 0; i < len; i++){
7157                 var n = cs[i];
7158                 n.previousSibling = cs[i-1];
7159                 n.nextSibling = cs[i+1];
7160                 if(i == 0){
7161                     this.setFirstChild(n);
7162                 }
7163                 if(i == len-1){
7164                     this.setLastChild(n);
7165                 }
7166             }
7167         }
7168     },
7169
7170     /**
7171      * Returns true if this node is an ancestor (at any point) of the passed node.
7172      * @param {Node} node
7173      * @return {Boolean}
7174      */
7175     contains : function(node){
7176         return node.isAncestor(this);
7177     },
7178
7179     /**
7180      * Returns true if the passed node is an ancestor (at any point) of this node.
7181      * @param {Node} node
7182      * @return {Boolean}
7183      */
7184     isAncestor : function(node){
7185         var p = this.parentNode;
7186         while(p){
7187             if(p == node){
7188                 return true;
7189             }
7190             p = p.parentNode;
7191         }
7192         return false;
7193     },
7194
7195     toString : function(){
7196         return "[Node"+(this.id?" "+this.id:"")+"]";
7197     }
7198 });/*
7199  * Based on:
7200  * Ext JS Library 1.1.1
7201  * Copyright(c) 2006-2007, Ext JS, LLC.
7202  *
7203  * Originally Released Under LGPL - original licence link has changed is not relivant.
7204  *
7205  * Fork - LGPL
7206  * <script type="text/javascript">
7207  */
7208  
7209
7210 /**
7211  * @class Roo.ComponentMgr
7212  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7213  * @singleton
7214  */
7215 Roo.ComponentMgr = function(){
7216     var all = new Roo.util.MixedCollection();
7217
7218     return {
7219         /**
7220          * Registers a component.
7221          * @param {Roo.Component} c The component
7222          */
7223         register : function(c){
7224             all.add(c);
7225         },
7226
7227         /**
7228          * Unregisters a component.
7229          * @param {Roo.Component} c The component
7230          */
7231         unregister : function(c){
7232             all.remove(c);
7233         },
7234
7235         /**
7236          * Returns a component by id
7237          * @param {String} id The component id
7238          */
7239         get : function(id){
7240             return all.get(id);
7241         },
7242
7243         /**
7244          * Registers a function that will be called when a specified component is added to ComponentMgr
7245          * @param {String} id The component id
7246          * @param {Funtction} fn The callback function
7247          * @param {Object} scope The scope of the callback
7248          */
7249         onAvailable : function(id, fn, scope){
7250             all.on("add", function(index, o){
7251                 if(o.id == id){
7252                     fn.call(scope || o, o);
7253                     all.un("add", fn, scope);
7254                 }
7255             });
7256         }
7257     };
7258 }();/*
7259  * Based on:
7260  * Ext JS Library 1.1.1
7261  * Copyright(c) 2006-2007, Ext JS, LLC.
7262  *
7263  * Originally Released Under LGPL - original licence link has changed is not relivant.
7264  *
7265  * Fork - LGPL
7266  * <script type="text/javascript">
7267  */
7268  
7269 /**
7270  * @class Roo.Component
7271  * @extends Roo.util.Observable
7272  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7273  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7274  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7275  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7276  * All visual components (widgets) that require rendering into a layout should subclass Component.
7277  * @constructor
7278  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7279  * 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
7280  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7281  */
7282 Roo.Component = function(config){
7283     config = config || {};
7284     if(config.tagName || config.dom || typeof config == "string"){ // element object
7285         config = {el: config, id: config.id || config};
7286     }
7287     this.initialConfig = config;
7288
7289     Roo.apply(this, config);
7290     this.addEvents({
7291         /**
7292          * @event disable
7293          * Fires after the component is disabled.
7294              * @param {Roo.Component} this
7295              */
7296         disable : true,
7297         /**
7298          * @event enable
7299          * Fires after the component is enabled.
7300              * @param {Roo.Component} this
7301              */
7302         enable : true,
7303         /**
7304          * @event beforeshow
7305          * Fires before the component is shown.  Return false to stop the show.
7306              * @param {Roo.Component} this
7307              */
7308         beforeshow : true,
7309         /**
7310          * @event show
7311          * Fires after the component is shown.
7312              * @param {Roo.Component} this
7313              */
7314         show : true,
7315         /**
7316          * @event beforehide
7317          * Fires before the component is hidden. Return false to stop the hide.
7318              * @param {Roo.Component} this
7319              */
7320         beforehide : true,
7321         /**
7322          * @event hide
7323          * Fires after the component is hidden.
7324              * @param {Roo.Component} this
7325              */
7326         hide : true,
7327         /**
7328          * @event beforerender
7329          * Fires before the component is rendered. Return false to stop the render.
7330              * @param {Roo.Component} this
7331              */
7332         beforerender : true,
7333         /**
7334          * @event render
7335          * Fires after the component is rendered.
7336              * @param {Roo.Component} this
7337              */
7338         render : true,
7339         /**
7340          * @event beforedestroy
7341          * Fires before the component is destroyed. Return false to stop the destroy.
7342              * @param {Roo.Component} this
7343              */
7344         beforedestroy : true,
7345         /**
7346          * @event destroy
7347          * Fires after the component is destroyed.
7348              * @param {Roo.Component} this
7349              */
7350         destroy : true
7351     });
7352     if(!this.id){
7353         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7354     }
7355     Roo.ComponentMgr.register(this);
7356     Roo.Component.superclass.constructor.call(this);
7357     this.initComponent();
7358     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7359         this.render(this.renderTo);
7360         delete this.renderTo;
7361     }
7362 };
7363
7364 // private
7365 Roo.Component.AUTO_ID = 1000;
7366
7367 Roo.extend(Roo.Component, Roo.util.Observable, {
7368     /**
7369      * @property {Boolean} hidden
7370      * true if this component is hidden. Read-only.
7371      */
7372     hidden : false,
7373     /**
7374      * true if this component is disabled. Read-only.
7375      */
7376     disabled : false,
7377     /**
7378      * true if this component has been rendered. Read-only.
7379      */
7380     rendered : false,
7381     
7382     /** @cfg {String} disableClass
7383      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7384      */
7385     disabledClass : "x-item-disabled",
7386         /** @cfg {Boolean} allowDomMove
7387          * Whether the component can move the Dom node when rendering (defaults to true).
7388          */
7389     allowDomMove : true,
7390     /** @cfg {String} hideMode
7391      * How this component should hidden. Supported values are
7392      * "visibility" (css visibility), "offsets" (negative offset position) and
7393      * "display" (css display) - defaults to "display".
7394      */
7395     hideMode: 'display',
7396
7397     // private
7398     ctype : "Roo.Component",
7399
7400     /** @cfg {String} actionMode 
7401      * which property holds the element that used for  hide() / show() / disable() / enable()
7402      * default is 'el' 
7403      */
7404     actionMode : "el",
7405
7406     // private
7407     getActionEl : function(){
7408         return this[this.actionMode];
7409     },
7410
7411     initComponent : Roo.emptyFn,
7412     /**
7413      * If this is a lazy rendering component, render it to its container element.
7414      * @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.
7415      */
7416     render : function(container, position){
7417         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7418             if(!container && this.el){
7419                 this.el = Roo.get(this.el);
7420                 container = this.el.dom.parentNode;
7421                 this.allowDomMove = false;
7422             }
7423             this.container = Roo.get(container);
7424             this.rendered = true;
7425             if(position !== undefined){
7426                 if(typeof position == 'number'){
7427                     position = this.container.dom.childNodes[position];
7428                 }else{
7429                     position = Roo.getDom(position);
7430                 }
7431             }
7432             this.onRender(this.container, position || null);
7433             if(this.cls){
7434                 this.el.addClass(this.cls);
7435                 delete this.cls;
7436             }
7437             if(this.style){
7438                 this.el.applyStyles(this.style);
7439                 delete this.style;
7440             }
7441             this.fireEvent("render", this);
7442             this.afterRender(this.container);
7443             if(this.hidden){
7444                 this.hide();
7445             }
7446             if(this.disabled){
7447                 this.disable();
7448             }
7449         }
7450         return this;
7451     },
7452
7453     // private
7454     // default function is not really useful
7455     onRender : function(ct, position){
7456         if(this.el){
7457             this.el = Roo.get(this.el);
7458             if(this.allowDomMove !== false){
7459                 ct.dom.insertBefore(this.el.dom, position);
7460             }
7461         }
7462     },
7463
7464     // private
7465     getAutoCreate : function(){
7466         var cfg = typeof this.autoCreate == "object" ?
7467                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7468         if(this.id && !cfg.id){
7469             cfg.id = this.id;
7470         }
7471         return cfg;
7472     },
7473
7474     // private
7475     afterRender : Roo.emptyFn,
7476
7477     /**
7478      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7479      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7480      */
7481     destroy : function(){
7482         if(this.fireEvent("beforedestroy", this) !== false){
7483             this.purgeListeners();
7484             this.beforeDestroy();
7485             if(this.rendered){
7486                 this.el.removeAllListeners();
7487                 this.el.remove();
7488                 if(this.actionMode == "container"){
7489                     this.container.remove();
7490                 }
7491             }
7492             this.onDestroy();
7493             Roo.ComponentMgr.unregister(this);
7494             this.fireEvent("destroy", this);
7495         }
7496     },
7497
7498         // private
7499     beforeDestroy : function(){
7500
7501     },
7502
7503         // private
7504         onDestroy : function(){
7505
7506     },
7507
7508     /**
7509      * Returns the underlying {@link Roo.Element}.
7510      * @return {Roo.Element} The element
7511      */
7512     getEl : function(){
7513         return this.el;
7514     },
7515
7516     /**
7517      * Returns the id of this component.
7518      * @return {String}
7519      */
7520     getId : function(){
7521         return this.id;
7522     },
7523
7524     /**
7525      * Try to focus this component.
7526      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7527      * @return {Roo.Component} this
7528      */
7529     focus : function(selectText){
7530         if(this.rendered){
7531             this.el.focus();
7532             if(selectText === true){
7533                 this.el.dom.select();
7534             }
7535         }
7536         return this;
7537     },
7538
7539     // private
7540     blur : function(){
7541         if(this.rendered){
7542             this.el.blur();
7543         }
7544         return this;
7545     },
7546
7547     /**
7548      * Disable this component.
7549      * @return {Roo.Component} this
7550      */
7551     disable : function(){
7552         if(this.rendered){
7553             this.onDisable();
7554         }
7555         this.disabled = true;
7556         this.fireEvent("disable", this);
7557         return this;
7558     },
7559
7560         // private
7561     onDisable : function(){
7562         this.getActionEl().addClass(this.disabledClass);
7563         this.el.dom.disabled = true;
7564     },
7565
7566     /**
7567      * Enable this component.
7568      * @return {Roo.Component} this
7569      */
7570     enable : function(){
7571         if(this.rendered){
7572             this.onEnable();
7573         }
7574         this.disabled = false;
7575         this.fireEvent("enable", this);
7576         return this;
7577     },
7578
7579         // private
7580     onEnable : function(){
7581         this.getActionEl().removeClass(this.disabledClass);
7582         this.el.dom.disabled = false;
7583     },
7584
7585     /**
7586      * Convenience function for setting disabled/enabled by boolean.
7587      * @param {Boolean} disabled
7588      */
7589     setDisabled : function(disabled){
7590         this[disabled ? "disable" : "enable"]();
7591     },
7592
7593     /**
7594      * Show this component.
7595      * @return {Roo.Component} this
7596      */
7597     show: function(){
7598         if(this.fireEvent("beforeshow", this) !== false){
7599             this.hidden = false;
7600             if(this.rendered){
7601                 this.onShow();
7602             }
7603             this.fireEvent("show", this);
7604         }
7605         return this;
7606     },
7607
7608     // private
7609     onShow : function(){
7610         var ae = this.getActionEl();
7611         if(this.hideMode == 'visibility'){
7612             ae.dom.style.visibility = "visible";
7613         }else if(this.hideMode == 'offsets'){
7614             ae.removeClass('x-hidden');
7615         }else{
7616             ae.dom.style.display = "";
7617         }
7618     },
7619
7620     /**
7621      * Hide this component.
7622      * @return {Roo.Component} this
7623      */
7624     hide: function(){
7625         if(this.fireEvent("beforehide", this) !== false){
7626             this.hidden = true;
7627             if(this.rendered){
7628                 this.onHide();
7629             }
7630             this.fireEvent("hide", this);
7631         }
7632         return this;
7633     },
7634
7635     // private
7636     onHide : function(){
7637         var ae = this.getActionEl();
7638         if(this.hideMode == 'visibility'){
7639             ae.dom.style.visibility = "hidden";
7640         }else if(this.hideMode == 'offsets'){
7641             ae.addClass('x-hidden');
7642         }else{
7643             ae.dom.style.display = "none";
7644         }
7645     },
7646
7647     /**
7648      * Convenience function to hide or show this component by boolean.
7649      * @param {Boolean} visible True to show, false to hide
7650      * @return {Roo.Component} this
7651      */
7652     setVisible: function(visible){
7653         if(visible) {
7654             this.show();
7655         }else{
7656             this.hide();
7657         }
7658         return this;
7659     },
7660
7661     /**
7662      * Returns true if this component is visible.
7663      */
7664     isVisible : function(){
7665         return this.getActionEl().isVisible();
7666     },
7667
7668     cloneConfig : function(overrides){
7669         overrides = overrides || {};
7670         var id = overrides.id || Roo.id();
7671         var cfg = Roo.applyIf(overrides, this.initialConfig);
7672         cfg.id = id; // prevent dup id
7673         return new this.constructor(cfg);
7674     }
7675 });/*
7676  * Based on:
7677  * Ext JS Library 1.1.1
7678  * Copyright(c) 2006-2007, Ext JS, LLC.
7679  *
7680  * Originally Released Under LGPL - original licence link has changed is not relivant.
7681  *
7682  * Fork - LGPL
7683  * <script type="text/javascript">
7684  */
7685  (function(){ 
7686 /**
7687  * @class Roo.Layer
7688  * @extends Roo.Element
7689  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7690  * automatic maintaining of shadow/shim positions.
7691  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7692  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7693  * you can pass a string with a CSS class name. False turns off the shadow.
7694  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7695  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7696  * @cfg {String} cls CSS class to add to the element
7697  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7698  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7699  * @constructor
7700  * @param {Object} config An object with config options.
7701  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7702  */
7703
7704 Roo.Layer = function(config, existingEl){
7705     config = config || {};
7706     var dh = Roo.DomHelper;
7707     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7708     if(existingEl){
7709         this.dom = Roo.getDom(existingEl);
7710     }
7711     if(!this.dom){
7712         var o = config.dh || {tag: "div", cls: "x-layer"};
7713         this.dom = dh.append(pel, o);
7714     }
7715     if(config.cls){
7716         this.addClass(config.cls);
7717     }
7718     this.constrain = config.constrain !== false;
7719     this.visibilityMode = Roo.Element.VISIBILITY;
7720     if(config.id){
7721         this.id = this.dom.id = config.id;
7722     }else{
7723         this.id = Roo.id(this.dom);
7724     }
7725     this.zindex = config.zindex || this.getZIndex();
7726     this.position("absolute", this.zindex);
7727     if(config.shadow){
7728         this.shadowOffset = config.shadowOffset || 4;
7729         this.shadow = new Roo.Shadow({
7730             offset : this.shadowOffset,
7731             mode : config.shadow
7732         });
7733     }else{
7734         this.shadowOffset = 0;
7735     }
7736     this.useShim = config.shim !== false && Roo.useShims;
7737     this.useDisplay = config.useDisplay;
7738     this.hide();
7739 };
7740
7741 var supr = Roo.Element.prototype;
7742
7743 // shims are shared among layer to keep from having 100 iframes
7744 var shims = [];
7745
7746 Roo.extend(Roo.Layer, Roo.Element, {
7747
7748     getZIndex : function(){
7749         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7750     },
7751
7752     getShim : function(){
7753         if(!this.useShim){
7754             return null;
7755         }
7756         if(this.shim){
7757             return this.shim;
7758         }
7759         var shim = shims.shift();
7760         if(!shim){
7761             shim = this.createShim();
7762             shim.enableDisplayMode('block');
7763             shim.dom.style.display = 'none';
7764             shim.dom.style.visibility = 'visible';
7765         }
7766         var pn = this.dom.parentNode;
7767         if(shim.dom.parentNode != pn){
7768             pn.insertBefore(shim.dom, this.dom);
7769         }
7770         shim.setStyle('z-index', this.getZIndex()-2);
7771         this.shim = shim;
7772         return shim;
7773     },
7774
7775     hideShim : function(){
7776         if(this.shim){
7777             this.shim.setDisplayed(false);
7778             shims.push(this.shim);
7779             delete this.shim;
7780         }
7781     },
7782
7783     disableShadow : function(){
7784         if(this.shadow){
7785             this.shadowDisabled = true;
7786             this.shadow.hide();
7787             this.lastShadowOffset = this.shadowOffset;
7788             this.shadowOffset = 0;
7789         }
7790     },
7791
7792     enableShadow : function(show){
7793         if(this.shadow){
7794             this.shadowDisabled = false;
7795             this.shadowOffset = this.lastShadowOffset;
7796             delete this.lastShadowOffset;
7797             if(show){
7798                 this.sync(true);
7799             }
7800         }
7801     },
7802
7803     // private
7804     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7805     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7806     sync : function(doShow){
7807         var sw = this.shadow;
7808         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7809             var sh = this.getShim();
7810
7811             var w = this.getWidth(),
7812                 h = this.getHeight();
7813
7814             var l = this.getLeft(true),
7815                 t = this.getTop(true);
7816
7817             if(sw && !this.shadowDisabled){
7818                 if(doShow && !sw.isVisible()){
7819                     sw.show(this);
7820                 }else{
7821                     sw.realign(l, t, w, h);
7822                 }
7823                 if(sh){
7824                     if(doShow){
7825                        sh.show();
7826                     }
7827                     // fit the shim behind the shadow, so it is shimmed too
7828                     var a = sw.adjusts, s = sh.dom.style;
7829                     s.left = (Math.min(l, l+a.l))+"px";
7830                     s.top = (Math.min(t, t+a.t))+"px";
7831                     s.width = (w+a.w)+"px";
7832                     s.height = (h+a.h)+"px";
7833                 }
7834             }else if(sh){
7835                 if(doShow){
7836                    sh.show();
7837                 }
7838                 sh.setSize(w, h);
7839                 sh.setLeftTop(l, t);
7840             }
7841             
7842         }
7843     },
7844
7845     // private
7846     destroy : function(){
7847         this.hideShim();
7848         if(this.shadow){
7849             this.shadow.hide();
7850         }
7851         this.removeAllListeners();
7852         var pn = this.dom.parentNode;
7853         if(pn){
7854             pn.removeChild(this.dom);
7855         }
7856         Roo.Element.uncache(this.id);
7857     },
7858
7859     remove : function(){
7860         this.destroy();
7861     },
7862
7863     // private
7864     beginUpdate : function(){
7865         this.updating = true;
7866     },
7867
7868     // private
7869     endUpdate : function(){
7870         this.updating = false;
7871         this.sync(true);
7872     },
7873
7874     // private
7875     hideUnders : function(negOffset){
7876         if(this.shadow){
7877             this.shadow.hide();
7878         }
7879         this.hideShim();
7880     },
7881
7882     // private
7883     constrainXY : function(){
7884         if(this.constrain){
7885             var vw = Roo.lib.Dom.getViewWidth(),
7886                 vh = Roo.lib.Dom.getViewHeight();
7887             var s = Roo.get(document).getScroll();
7888
7889             var xy = this.getXY();
7890             var x = xy[0], y = xy[1];   
7891             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7892             // only move it if it needs it
7893             var moved = false;
7894             // first validate right/bottom
7895             if((x + w) > vw+s.left){
7896                 x = vw - w - this.shadowOffset;
7897                 moved = true;
7898             }
7899             if((y + h) > vh+s.top){
7900                 y = vh - h - this.shadowOffset;
7901                 moved = true;
7902             }
7903             // then make sure top/left isn't negative
7904             if(x < s.left){
7905                 x = s.left;
7906                 moved = true;
7907             }
7908             if(y < s.top){
7909                 y = s.top;
7910                 moved = true;
7911             }
7912             if(moved){
7913                 if(this.avoidY){
7914                     var ay = this.avoidY;
7915                     if(y <= ay && (y+h) >= ay){
7916                         y = ay-h-5;   
7917                     }
7918                 }
7919                 xy = [x, y];
7920                 this.storeXY(xy);
7921                 supr.setXY.call(this, xy);
7922                 this.sync();
7923             }
7924         }
7925     },
7926
7927     isVisible : function(){
7928         return this.visible;    
7929     },
7930
7931     // private
7932     showAction : function(){
7933         this.visible = true; // track visibility to prevent getStyle calls
7934         if(this.useDisplay === true){
7935             this.setDisplayed("");
7936         }else if(this.lastXY){
7937             supr.setXY.call(this, this.lastXY);
7938         }else if(this.lastLT){
7939             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7940         }
7941     },
7942
7943     // private
7944     hideAction : function(){
7945         this.visible = false;
7946         if(this.useDisplay === true){
7947             this.setDisplayed(false);
7948         }else{
7949             this.setLeftTop(-10000,-10000);
7950         }
7951     },
7952
7953     // overridden Element method
7954     setVisible : function(v, a, d, c, e){
7955         if(v){
7956             this.showAction();
7957         }
7958         if(a && v){
7959             var cb = function(){
7960                 this.sync(true);
7961                 if(c){
7962                     c();
7963                 }
7964             }.createDelegate(this);
7965             supr.setVisible.call(this, true, true, d, cb, e);
7966         }else{
7967             if(!v){
7968                 this.hideUnders(true);
7969             }
7970             var cb = c;
7971             if(a){
7972                 cb = function(){
7973                     this.hideAction();
7974                     if(c){
7975                         c();
7976                     }
7977                 }.createDelegate(this);
7978             }
7979             supr.setVisible.call(this, v, a, d, cb, e);
7980             if(v){
7981                 this.sync(true);
7982             }else if(!a){
7983                 this.hideAction();
7984             }
7985         }
7986     },
7987
7988     storeXY : function(xy){
7989         delete this.lastLT;
7990         this.lastXY = xy;
7991     },
7992
7993     storeLeftTop : function(left, top){
7994         delete this.lastXY;
7995         this.lastLT = [left, top];
7996     },
7997
7998     // private
7999     beforeFx : function(){
8000         this.beforeAction();
8001         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8002     },
8003
8004     // private
8005     afterFx : function(){
8006         Roo.Layer.superclass.afterFx.apply(this, arguments);
8007         this.sync(this.isVisible());
8008     },
8009
8010     // private
8011     beforeAction : function(){
8012         if(!this.updating && this.shadow){
8013             this.shadow.hide();
8014         }
8015     },
8016
8017     // overridden Element method
8018     setLeft : function(left){
8019         this.storeLeftTop(left, this.getTop(true));
8020         supr.setLeft.apply(this, arguments);
8021         this.sync();
8022     },
8023
8024     setTop : function(top){
8025         this.storeLeftTop(this.getLeft(true), top);
8026         supr.setTop.apply(this, arguments);
8027         this.sync();
8028     },
8029
8030     setLeftTop : function(left, top){
8031         this.storeLeftTop(left, top);
8032         supr.setLeftTop.apply(this, arguments);
8033         this.sync();
8034     },
8035
8036     setXY : function(xy, a, d, c, e){
8037         this.fixDisplay();
8038         this.beforeAction();
8039         this.storeXY(xy);
8040         var cb = this.createCB(c);
8041         supr.setXY.call(this, xy, a, d, cb, e);
8042         if(!a){
8043             cb();
8044         }
8045     },
8046
8047     // private
8048     createCB : function(c){
8049         var el = this;
8050         return function(){
8051             el.constrainXY();
8052             el.sync(true);
8053             if(c){
8054                 c();
8055             }
8056         };
8057     },
8058
8059     // overridden Element method
8060     setX : function(x, a, d, c, e){
8061         this.setXY([x, this.getY()], a, d, c, e);
8062     },
8063
8064     // overridden Element method
8065     setY : function(y, a, d, c, e){
8066         this.setXY([this.getX(), y], a, d, c, e);
8067     },
8068
8069     // overridden Element method
8070     setSize : function(w, h, a, d, c, e){
8071         this.beforeAction();
8072         var cb = this.createCB(c);
8073         supr.setSize.call(this, w, h, a, d, cb, e);
8074         if(!a){
8075             cb();
8076         }
8077     },
8078
8079     // overridden Element method
8080     setWidth : function(w, a, d, c, e){
8081         this.beforeAction();
8082         var cb = this.createCB(c);
8083         supr.setWidth.call(this, w, a, d, cb, e);
8084         if(!a){
8085             cb();
8086         }
8087     },
8088
8089     // overridden Element method
8090     setHeight : function(h, a, d, c, e){
8091         this.beforeAction();
8092         var cb = this.createCB(c);
8093         supr.setHeight.call(this, h, a, d, cb, e);
8094         if(!a){
8095             cb();
8096         }
8097     },
8098
8099     // overridden Element method
8100     setBounds : function(x, y, w, h, a, d, c, e){
8101         this.beforeAction();
8102         var cb = this.createCB(c);
8103         if(!a){
8104             this.storeXY([x, y]);
8105             supr.setXY.call(this, [x, y]);
8106             supr.setSize.call(this, w, h, a, d, cb, e);
8107             cb();
8108         }else{
8109             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8110         }
8111         return this;
8112     },
8113     
8114     /**
8115      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8116      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8117      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8118      * @param {Number} zindex The new z-index to set
8119      * @return {this} The Layer
8120      */
8121     setZIndex : function(zindex){
8122         this.zindex = zindex;
8123         this.setStyle("z-index", zindex + 2);
8124         if(this.shadow){
8125             this.shadow.setZIndex(zindex + 1);
8126         }
8127         if(this.shim){
8128             this.shim.setStyle("z-index", zindex);
8129         }
8130     }
8131 });
8132 })();/*
8133  * Based on:
8134  * Ext JS Library 1.1.1
8135  * Copyright(c) 2006-2007, Ext JS, LLC.
8136  *
8137  * Originally Released Under LGPL - original licence link has changed is not relivant.
8138  *
8139  * Fork - LGPL
8140  * <script type="text/javascript">
8141  */
8142
8143
8144 /**
8145  * @class Roo.Shadow
8146  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8147  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8148  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8149  * @constructor
8150  * Create a new Shadow
8151  * @param {Object} config The config object
8152  */
8153 Roo.Shadow = function(config){
8154     Roo.apply(this, config);
8155     if(typeof this.mode != "string"){
8156         this.mode = this.defaultMode;
8157     }
8158     var o = this.offset, a = {h: 0};
8159     var rad = Math.floor(this.offset/2);
8160     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8161         case "drop":
8162             a.w = 0;
8163             a.l = a.t = o;
8164             a.t -= 1;
8165             if(Roo.isIE){
8166                 a.l -= this.offset + rad;
8167                 a.t -= this.offset + rad;
8168                 a.w -= rad;
8169                 a.h -= rad;
8170                 a.t += 1;
8171             }
8172         break;
8173         case "sides":
8174             a.w = (o*2);
8175             a.l = -o;
8176             a.t = o-1;
8177             if(Roo.isIE){
8178                 a.l -= (this.offset - rad);
8179                 a.t -= this.offset + rad;
8180                 a.l += 1;
8181                 a.w -= (this.offset - rad)*2;
8182                 a.w -= rad + 1;
8183                 a.h -= 1;
8184             }
8185         break;
8186         case "frame":
8187             a.w = a.h = (o*2);
8188             a.l = a.t = -o;
8189             a.t += 1;
8190             a.h -= 2;
8191             if(Roo.isIE){
8192                 a.l -= (this.offset - rad);
8193                 a.t -= (this.offset - rad);
8194                 a.l += 1;
8195                 a.w -= (this.offset + rad + 1);
8196                 a.h -= (this.offset + rad);
8197                 a.h += 1;
8198             }
8199         break;
8200     };
8201
8202     this.adjusts = a;
8203 };
8204
8205 Roo.Shadow.prototype = {
8206     /**
8207      * @cfg {String} mode
8208      * The shadow display mode.  Supports the following options:<br />
8209      * sides: Shadow displays on both sides and bottom only<br />
8210      * frame: Shadow displays equally on all four sides<br />
8211      * drop: Traditional bottom-right drop shadow (default)
8212      */
8213     /**
8214      * @cfg {String} offset
8215      * The number of pixels to offset the shadow from the element (defaults to 4)
8216      */
8217     offset: 4,
8218
8219     // private
8220     defaultMode: "drop",
8221
8222     /**
8223      * Displays the shadow under the target element
8224      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8225      */
8226     show : function(target){
8227         target = Roo.get(target);
8228         if(!this.el){
8229             this.el = Roo.Shadow.Pool.pull();
8230             if(this.el.dom.nextSibling != target.dom){
8231                 this.el.insertBefore(target);
8232             }
8233         }
8234         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8235         if(Roo.isIE){
8236             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8237         }
8238         this.realign(
8239             target.getLeft(true),
8240             target.getTop(true),
8241             target.getWidth(),
8242             target.getHeight()
8243         );
8244         this.el.dom.style.display = "block";
8245     },
8246
8247     /**
8248      * Returns true if the shadow is visible, else false
8249      */
8250     isVisible : function(){
8251         return this.el ? true : false;  
8252     },
8253
8254     /**
8255      * Direct alignment when values are already available. Show must be called at least once before
8256      * calling this method to ensure it is initialized.
8257      * @param {Number} left The target element left position
8258      * @param {Number} top The target element top position
8259      * @param {Number} width The target element width
8260      * @param {Number} height The target element height
8261      */
8262     realign : function(l, t, w, h){
8263         if(!this.el){
8264             return;
8265         }
8266         var a = this.adjusts, d = this.el.dom, s = d.style;
8267         var iea = 0;
8268         s.left = (l+a.l)+"px";
8269         s.top = (t+a.t)+"px";
8270         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8271  
8272         if(s.width != sws || s.height != shs){
8273             s.width = sws;
8274             s.height = shs;
8275             if(!Roo.isIE){
8276                 var cn = d.childNodes;
8277                 var sww = Math.max(0, (sw-12))+"px";
8278                 cn[0].childNodes[1].style.width = sww;
8279                 cn[1].childNodes[1].style.width = sww;
8280                 cn[2].childNodes[1].style.width = sww;
8281                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8282             }
8283         }
8284     },
8285
8286     /**
8287      * Hides this shadow
8288      */
8289     hide : function(){
8290         if(this.el){
8291             this.el.dom.style.display = "none";
8292             Roo.Shadow.Pool.push(this.el);
8293             delete this.el;
8294         }
8295     },
8296
8297     /**
8298      * Adjust the z-index of this shadow
8299      * @param {Number} zindex The new z-index
8300      */
8301     setZIndex : function(z){
8302         this.zIndex = z;
8303         if(this.el){
8304             this.el.setStyle("z-index", z);
8305         }
8306     }
8307 };
8308
8309 // Private utility class that manages the internal Shadow cache
8310 Roo.Shadow.Pool = function(){
8311     var p = [];
8312     var markup = Roo.isIE ?
8313                  '<div class="x-ie-shadow"></div>' :
8314                  '<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>';
8315     return {
8316         pull : function(){
8317             var sh = p.shift();
8318             if(!sh){
8319                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8320                 sh.autoBoxAdjust = false;
8321             }
8322             return sh;
8323         },
8324
8325         push : function(sh){
8326             p.push(sh);
8327         }
8328     };
8329 }();/*
8330  * Based on:
8331  * Ext JS Library 1.1.1
8332  * Copyright(c) 2006-2007, Ext JS, LLC.
8333  *
8334  * Originally Released Under LGPL - original licence link has changed is not relivant.
8335  *
8336  * Fork - LGPL
8337  * <script type="text/javascript">
8338  */
8339
8340 /**
8341  * @class Roo.BoxComponent
8342  * @extends Roo.Component
8343  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8344  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8345  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8346  * layout containers.
8347  * @constructor
8348  * @param {Roo.Element/String/Object} config The configuration options.
8349  */
8350 Roo.BoxComponent = function(config){
8351     Roo.Component.call(this, config);
8352     this.addEvents({
8353         /**
8354          * @event resize
8355          * Fires after the component is resized.
8356              * @param {Roo.Component} this
8357              * @param {Number} adjWidth The box-adjusted width that was set
8358              * @param {Number} adjHeight The box-adjusted height that was set
8359              * @param {Number} rawWidth The width that was originally specified
8360              * @param {Number} rawHeight The height that was originally specified
8361              */
8362         resize : true,
8363         /**
8364          * @event move
8365          * Fires after the component is moved.
8366              * @param {Roo.Component} this
8367              * @param {Number} x The new x position
8368              * @param {Number} y The new y position
8369              */
8370         move : true
8371     });
8372 };
8373
8374 Roo.extend(Roo.BoxComponent, Roo.Component, {
8375     // private, set in afterRender to signify that the component has been rendered
8376     boxReady : false,
8377     // private, used to defer height settings to subclasses
8378     deferHeight: false,
8379     /** @cfg {Number} width
8380      * width (optional) size of component
8381      */
8382      /** @cfg {Number} height
8383      * height (optional) size of component
8384      */
8385      
8386     /**
8387      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8388      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8389      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8390      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8391      * @return {Roo.BoxComponent} this
8392      */
8393     setSize : function(w, h){
8394         // support for standard size objects
8395         if(typeof w == 'object'){
8396             h = w.height;
8397             w = w.width;
8398         }
8399         // not rendered
8400         if(!this.boxReady){
8401             this.width = w;
8402             this.height = h;
8403             return this;
8404         }
8405
8406         // prevent recalcs when not needed
8407         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8408             return this;
8409         }
8410         this.lastSize = {width: w, height: h};
8411
8412         var adj = this.adjustSize(w, h);
8413         var aw = adj.width, ah = adj.height;
8414         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8415             var rz = this.getResizeEl();
8416             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8417                 rz.setSize(aw, ah);
8418             }else if(!this.deferHeight && ah !== undefined){
8419                 rz.setHeight(ah);
8420             }else if(aw !== undefined){
8421                 rz.setWidth(aw);
8422             }
8423             this.onResize(aw, ah, w, h);
8424             this.fireEvent('resize', this, aw, ah, w, h);
8425         }
8426         return this;
8427     },
8428
8429     /**
8430      * Gets the current size of the component's underlying element.
8431      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8432      */
8433     getSize : function(){
8434         return this.el.getSize();
8435     },
8436
8437     /**
8438      * Gets the current XY position 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      * @return {Array} The XY position of the element (e.g., [100, 200])
8441      */
8442     getPosition : function(local){
8443         if(local === true){
8444             return [this.el.getLeft(true), this.el.getTop(true)];
8445         }
8446         return this.xy || this.el.getXY();
8447     },
8448
8449     /**
8450      * Gets the current box measurements of the component's underlying element.
8451      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8452      * @returns {Object} box An object in the format {x, y, width, height}
8453      */
8454     getBox : function(local){
8455         var s = this.el.getSize();
8456         if(local){
8457             s.x = this.el.getLeft(true);
8458             s.y = this.el.getTop(true);
8459         }else{
8460             var xy = this.xy || this.el.getXY();
8461             s.x = xy[0];
8462             s.y = xy[1];
8463         }
8464         return s;
8465     },
8466
8467     /**
8468      * Sets the current box measurements of the component's underlying element.
8469      * @param {Object} box An object in the format {x, y, width, height}
8470      * @returns {Roo.BoxComponent} this
8471      */
8472     updateBox : function(box){
8473         this.setSize(box.width, box.height);
8474         this.setPagePosition(box.x, box.y);
8475         return this;
8476     },
8477
8478     // protected
8479     getResizeEl : function(){
8480         return this.resizeEl || this.el;
8481     },
8482
8483     // protected
8484     getPositionEl : function(){
8485         return this.positionEl || this.el;
8486     },
8487
8488     /**
8489      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8490      * This method fires the move event.
8491      * @param {Number} left The new left
8492      * @param {Number} top The new top
8493      * @returns {Roo.BoxComponent} this
8494      */
8495     setPosition : function(x, y){
8496         this.x = x;
8497         this.y = y;
8498         if(!this.boxReady){
8499             return this;
8500         }
8501         var adj = this.adjustPosition(x, y);
8502         var ax = adj.x, ay = adj.y;
8503
8504         var el = this.getPositionEl();
8505         if(ax !== undefined || ay !== undefined){
8506             if(ax !== undefined && ay !== undefined){
8507                 el.setLeftTop(ax, ay);
8508             }else if(ax !== undefined){
8509                 el.setLeft(ax);
8510             }else if(ay !== undefined){
8511                 el.setTop(ay);
8512             }
8513             this.onPosition(ax, ay);
8514             this.fireEvent('move', this, ax, ay);
8515         }
8516         return this;
8517     },
8518
8519     /**
8520      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8521      * This method fires the move event.
8522      * @param {Number} x The new x position
8523      * @param {Number} y The new y position
8524      * @returns {Roo.BoxComponent} this
8525      */
8526     setPagePosition : function(x, y){
8527         this.pageX = x;
8528         this.pageY = y;
8529         if(!this.boxReady){
8530             return;
8531         }
8532         if(x === undefined || y === undefined){ // cannot translate undefined points
8533             return;
8534         }
8535         var p = this.el.translatePoints(x, y);
8536         this.setPosition(p.left, p.top);
8537         return this;
8538     },
8539
8540     // private
8541     onRender : function(ct, position){
8542         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8543         if(this.resizeEl){
8544             this.resizeEl = Roo.get(this.resizeEl);
8545         }
8546         if(this.positionEl){
8547             this.positionEl = Roo.get(this.positionEl);
8548         }
8549     },
8550
8551     // private
8552     afterRender : function(){
8553         Roo.BoxComponent.superclass.afterRender.call(this);
8554         this.boxReady = true;
8555         this.setSize(this.width, this.height);
8556         if(this.x || this.y){
8557             this.setPosition(this.x, this.y);
8558         }
8559         if(this.pageX || this.pageY){
8560             this.setPagePosition(this.pageX, this.pageY);
8561         }
8562     },
8563
8564     /**
8565      * Force the component's size to recalculate based on the underlying element's current height and width.
8566      * @returns {Roo.BoxComponent} this
8567      */
8568     syncSize : function(){
8569         delete this.lastSize;
8570         this.setSize(this.el.getWidth(), this.el.getHeight());
8571         return this;
8572     },
8573
8574     /**
8575      * Called after the component is resized, this method is empty by default but can be implemented by any
8576      * subclass that needs to perform custom logic after a resize occurs.
8577      * @param {Number} adjWidth The box-adjusted width that was set
8578      * @param {Number} adjHeight The box-adjusted height that was set
8579      * @param {Number} rawWidth The width that was originally specified
8580      * @param {Number} rawHeight The height that was originally specified
8581      */
8582     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8583
8584     },
8585
8586     /**
8587      * Called after the component is moved, this method is empty by default but can be implemented by any
8588      * subclass that needs to perform custom logic after a move occurs.
8589      * @param {Number} x The new x position
8590      * @param {Number} y The new y position
8591      */
8592     onPosition : function(x, y){
8593
8594     },
8595
8596     // private
8597     adjustSize : function(w, h){
8598         if(this.autoWidth){
8599             w = 'auto';
8600         }
8601         if(this.autoHeight){
8602             h = 'auto';
8603         }
8604         return {width : w, height: h};
8605     },
8606
8607     // private
8608     adjustPosition : function(x, y){
8609         return {x : x, y: y};
8610     }
8611 });/*
8612  * Based on:
8613  * Ext JS Library 1.1.1
8614  * Copyright(c) 2006-2007, Ext JS, LLC.
8615  *
8616  * Originally Released Under LGPL - original licence link has changed is not relivant.
8617  *
8618  * Fork - LGPL
8619  * <script type="text/javascript">
8620  */
8621
8622
8623 /**
8624  * @class Roo.SplitBar
8625  * @extends Roo.util.Observable
8626  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8627  * <br><br>
8628  * Usage:
8629  * <pre><code>
8630 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8631                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8632 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8633 split.minSize = 100;
8634 split.maxSize = 600;
8635 split.animate = true;
8636 split.on('moved', splitterMoved);
8637 </code></pre>
8638  * @constructor
8639  * Create a new SplitBar
8640  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8641  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8642  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8643  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8644                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8645                         position of the SplitBar).
8646  */
8647 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8648     
8649     /** @private */
8650     this.el = Roo.get(dragElement, true);
8651     this.el.dom.unselectable = "on";
8652     /** @private */
8653     this.resizingEl = Roo.get(resizingElement, true);
8654
8655     /**
8656      * @private
8657      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8658      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8659      * @type Number
8660      */
8661     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8662     
8663     /**
8664      * The minimum size of the resizing element. (Defaults to 0)
8665      * @type Number
8666      */
8667     this.minSize = 0;
8668     
8669     /**
8670      * The maximum size of the resizing element. (Defaults to 2000)
8671      * @type Number
8672      */
8673     this.maxSize = 2000;
8674     
8675     /**
8676      * Whether to animate the transition to the new size
8677      * @type Boolean
8678      */
8679     this.animate = false;
8680     
8681     /**
8682      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8683      * @type Boolean
8684      */
8685     this.useShim = false;
8686     
8687     /** @private */
8688     this.shim = null;
8689     
8690     if(!existingProxy){
8691         /** @private */
8692         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8693     }else{
8694         this.proxy = Roo.get(existingProxy).dom;
8695     }
8696     /** @private */
8697     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8698     
8699     /** @private */
8700     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8701     
8702     /** @private */
8703     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8704     
8705     /** @private */
8706     this.dragSpecs = {};
8707     
8708     /**
8709      * @private The adapter to use to positon and resize elements
8710      */
8711     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8712     this.adapter.init(this);
8713     
8714     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8715         /** @private */
8716         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8717         this.el.addClass("x-splitbar-h");
8718     }else{
8719         /** @private */
8720         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8721         this.el.addClass("x-splitbar-v");
8722     }
8723     
8724     this.addEvents({
8725         /**
8726          * @event resize
8727          * Fires when the splitter is moved (alias for {@link #event-moved})
8728          * @param {Roo.SplitBar} this
8729          * @param {Number} newSize the new width or height
8730          */
8731         "resize" : true,
8732         /**
8733          * @event moved
8734          * Fires when the splitter is moved
8735          * @param {Roo.SplitBar} this
8736          * @param {Number} newSize the new width or height
8737          */
8738         "moved" : true,
8739         /**
8740          * @event beforeresize
8741          * Fires before the splitter is dragged
8742          * @param {Roo.SplitBar} this
8743          */
8744         "beforeresize" : true,
8745
8746         "beforeapply" : true
8747     });
8748
8749     Roo.util.Observable.call(this);
8750 };
8751
8752 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8753     onStartProxyDrag : function(x, y){
8754         this.fireEvent("beforeresize", this);
8755         if(!this.overlay){
8756             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8757             o.unselectable();
8758             o.enableDisplayMode("block");
8759             // all splitbars share the same overlay
8760             Roo.SplitBar.prototype.overlay = o;
8761         }
8762         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8763         this.overlay.show();
8764         Roo.get(this.proxy).setDisplayed("block");
8765         var size = this.adapter.getElementSize(this);
8766         this.activeMinSize = this.getMinimumSize();;
8767         this.activeMaxSize = this.getMaximumSize();;
8768         var c1 = size - this.activeMinSize;
8769         var c2 = Math.max(this.activeMaxSize - size, 0);
8770         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8771             this.dd.resetConstraints();
8772             this.dd.setXConstraint(
8773                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8774                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8775             );
8776             this.dd.setYConstraint(0, 0);
8777         }else{
8778             this.dd.resetConstraints();
8779             this.dd.setXConstraint(0, 0);
8780             this.dd.setYConstraint(
8781                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8782                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8783             );
8784          }
8785         this.dragSpecs.startSize = size;
8786         this.dragSpecs.startPoint = [x, y];
8787         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8788     },
8789     
8790     /** 
8791      * @private Called after the drag operation by the DDProxy
8792      */
8793     onEndProxyDrag : function(e){
8794         Roo.get(this.proxy).setDisplayed(false);
8795         var endPoint = Roo.lib.Event.getXY(e);
8796         if(this.overlay){
8797             this.overlay.hide();
8798         }
8799         var newSize;
8800         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8801             newSize = this.dragSpecs.startSize + 
8802                 (this.placement == Roo.SplitBar.LEFT ?
8803                     endPoint[0] - this.dragSpecs.startPoint[0] :
8804                     this.dragSpecs.startPoint[0] - endPoint[0]
8805                 );
8806         }else{
8807             newSize = this.dragSpecs.startSize + 
8808                 (this.placement == Roo.SplitBar.TOP ?
8809                     endPoint[1] - this.dragSpecs.startPoint[1] :
8810                     this.dragSpecs.startPoint[1] - endPoint[1]
8811                 );
8812         }
8813         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8814         if(newSize != this.dragSpecs.startSize){
8815             if(this.fireEvent('beforeapply', this, newSize) !== false){
8816                 this.adapter.setElementSize(this, newSize);
8817                 this.fireEvent("moved", this, newSize);
8818                 this.fireEvent("resize", this, newSize);
8819             }
8820         }
8821     },
8822     
8823     /**
8824      * Get the adapter this SplitBar uses
8825      * @return The adapter object
8826      */
8827     getAdapter : function(){
8828         return this.adapter;
8829     },
8830     
8831     /**
8832      * Set the adapter this SplitBar uses
8833      * @param {Object} adapter A SplitBar adapter object
8834      */
8835     setAdapter : function(adapter){
8836         this.adapter = adapter;
8837         this.adapter.init(this);
8838     },
8839     
8840     /**
8841      * Gets the minimum size for the resizing element
8842      * @return {Number} The minimum size
8843      */
8844     getMinimumSize : function(){
8845         return this.minSize;
8846     },
8847     
8848     /**
8849      * Sets the minimum size for the resizing element
8850      * @param {Number} minSize The minimum size
8851      */
8852     setMinimumSize : function(minSize){
8853         this.minSize = minSize;
8854     },
8855     
8856     /**
8857      * Gets the maximum size for the resizing element
8858      * @return {Number} The maximum size
8859      */
8860     getMaximumSize : function(){
8861         return this.maxSize;
8862     },
8863     
8864     /**
8865      * Sets the maximum size for the resizing element
8866      * @param {Number} maxSize The maximum size
8867      */
8868     setMaximumSize : function(maxSize){
8869         this.maxSize = maxSize;
8870     },
8871     
8872     /**
8873      * Sets the initialize size for the resizing element
8874      * @param {Number} size The initial size
8875      */
8876     setCurrentSize : function(size){
8877         var oldAnimate = this.animate;
8878         this.animate = false;
8879         this.adapter.setElementSize(this, size);
8880         this.animate = oldAnimate;
8881     },
8882     
8883     /**
8884      * Destroy this splitbar. 
8885      * @param {Boolean} removeEl True to remove the element
8886      */
8887     destroy : function(removeEl){
8888         if(this.shim){
8889             this.shim.remove();
8890         }
8891         this.dd.unreg();
8892         this.proxy.parentNode.removeChild(this.proxy);
8893         if(removeEl){
8894             this.el.remove();
8895         }
8896     }
8897 });
8898
8899 /**
8900  * @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.
8901  */
8902 Roo.SplitBar.createProxy = function(dir){
8903     var proxy = new Roo.Element(document.createElement("div"));
8904     proxy.unselectable();
8905     var cls = 'x-splitbar-proxy';
8906     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8907     document.body.appendChild(proxy.dom);
8908     return proxy.dom;
8909 };
8910
8911 /** 
8912  * @class Roo.SplitBar.BasicLayoutAdapter
8913  * Default Adapter. It assumes the splitter and resizing element are not positioned
8914  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8915  */
8916 Roo.SplitBar.BasicLayoutAdapter = function(){
8917 };
8918
8919 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8920     // do nothing for now
8921     init : function(s){
8922     
8923     },
8924     /**
8925      * Called before drag operations to get the current size of the resizing element. 
8926      * @param {Roo.SplitBar} s The SplitBar using this adapter
8927      */
8928      getElementSize : function(s){
8929         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8930             return s.resizingEl.getWidth();
8931         }else{
8932             return s.resizingEl.getHeight();
8933         }
8934     },
8935     
8936     /**
8937      * Called after drag operations to set the size of the resizing element.
8938      * @param {Roo.SplitBar} s The SplitBar using this adapter
8939      * @param {Number} newSize The new size to set
8940      * @param {Function} onComplete A function to be invoked when resizing is complete
8941      */
8942     setElementSize : function(s, newSize, onComplete){
8943         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8944             if(!s.animate){
8945                 s.resizingEl.setWidth(newSize);
8946                 if(onComplete){
8947                     onComplete(s, newSize);
8948                 }
8949             }else{
8950                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8951             }
8952         }else{
8953             
8954             if(!s.animate){
8955                 s.resizingEl.setHeight(newSize);
8956                 if(onComplete){
8957                     onComplete(s, newSize);
8958                 }
8959             }else{
8960                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8961             }
8962         }
8963     }
8964 };
8965
8966 /** 
8967  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8968  * @extends Roo.SplitBar.BasicLayoutAdapter
8969  * Adapter that  moves the splitter element to align with the resized sizing element. 
8970  * Used with an absolute positioned SplitBar.
8971  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8972  * document.body, make sure you assign an id to the body element.
8973  */
8974 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8975     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8976     this.container = Roo.get(container);
8977 };
8978
8979 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8980     init : function(s){
8981         this.basic.init(s);
8982     },
8983     
8984     getElementSize : function(s){
8985         return this.basic.getElementSize(s);
8986     },
8987     
8988     setElementSize : function(s, newSize, onComplete){
8989         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8990     },
8991     
8992     moveSplitter : function(s){
8993         var yes = Roo.SplitBar;
8994         switch(s.placement){
8995             case yes.LEFT:
8996                 s.el.setX(s.resizingEl.getRight());
8997                 break;
8998             case yes.RIGHT:
8999                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9000                 break;
9001             case yes.TOP:
9002                 s.el.setY(s.resizingEl.getBottom());
9003                 break;
9004             case yes.BOTTOM:
9005                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9006                 break;
9007         }
9008     }
9009 };
9010
9011 /**
9012  * Orientation constant - Create a vertical SplitBar
9013  * @static
9014  * @type Number
9015  */
9016 Roo.SplitBar.VERTICAL = 1;
9017
9018 /**
9019  * Orientation constant - Create a horizontal SplitBar
9020  * @static
9021  * @type Number
9022  */
9023 Roo.SplitBar.HORIZONTAL = 2;
9024
9025 /**
9026  * Placement constant - The resizing element is to the left of the splitter element
9027  * @static
9028  * @type Number
9029  */
9030 Roo.SplitBar.LEFT = 1;
9031
9032 /**
9033  * Placement constant - The resizing element is to the right of the splitter element
9034  * @static
9035  * @type Number
9036  */
9037 Roo.SplitBar.RIGHT = 2;
9038
9039 /**
9040  * Placement constant - The resizing element is positioned above the splitter element
9041  * @static
9042  * @type Number
9043  */
9044 Roo.SplitBar.TOP = 3;
9045
9046 /**
9047  * Placement constant - The resizing element is positioned under splitter element
9048  * @static
9049  * @type Number
9050  */
9051 Roo.SplitBar.BOTTOM = 4;
9052 /*
9053  * Based on:
9054  * Ext JS Library 1.1.1
9055  * Copyright(c) 2006-2007, Ext JS, LLC.
9056  *
9057  * Originally Released Under LGPL - original licence link has changed is not relivant.
9058  *
9059  * Fork - LGPL
9060  * <script type="text/javascript">
9061  */
9062
9063 /**
9064  * @class Roo.View
9065  * @extends Roo.util.Observable
9066  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9067  * This class also supports single and multi selection modes. <br>
9068  * Create a data model bound view:
9069  <pre><code>
9070  var store = new Roo.data.Store(...);
9071
9072  var view = new Roo.View({
9073     el : "my-element",
9074     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9075  
9076     singleSelect: true,
9077     selectedClass: "ydataview-selected",
9078     store: store
9079  });
9080
9081  // listen for node click?
9082  view.on("click", function(vw, index, node, e){
9083  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9084  });
9085
9086  // load XML data
9087  dataModel.load("foobar.xml");
9088  </code></pre>
9089  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9090  * <br><br>
9091  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9092  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9093  * 
9094  * Note: old style constructor is still suported (container, template, config)
9095  * 
9096  * @constructor
9097  * Create a new View
9098  * @param {Object} config The config object
9099  * 
9100  */
9101 Roo.View = function(config, depreciated_tpl, depreciated_config){
9102     
9103     if (typeof(depreciated_tpl) == 'undefined') {
9104         // new way.. - universal constructor.
9105         Roo.apply(this, config);
9106         this.el  = Roo.get(this.el);
9107     } else {
9108         // old format..
9109         this.el  = Roo.get(config);
9110         this.tpl = depreciated_tpl;
9111         Roo.apply(this, depreciated_config);
9112     }
9113      
9114     
9115     if(typeof(this.tpl) == "string"){
9116         this.tpl = new Roo.Template(this.tpl);
9117     } else {
9118         // support xtype ctors..
9119         this.tpl = new Roo.factory(this.tpl, Roo);
9120     }
9121     
9122     
9123     this.tpl.compile();
9124    
9125
9126      
9127     /** @private */
9128     this.addEvents({
9129     /**
9130      * @event beforeclick
9131      * Fires before a click is processed. Returns false to cancel the default action.
9132      * @param {Roo.View} this
9133      * @param {Number} index The index of the target node
9134      * @param {HTMLElement} node The target node
9135      * @param {Roo.EventObject} e The raw event object
9136      */
9137         "beforeclick" : true,
9138     /**
9139      * @event click
9140      * Fires when a template node is clicked.
9141      * @param {Roo.View} this
9142      * @param {Number} index The index of the target node
9143      * @param {HTMLElement} node The target node
9144      * @param {Roo.EventObject} e The raw event object
9145      */
9146         "click" : true,
9147     /**
9148      * @event dblclick
9149      * Fires when a template node is double clicked.
9150      * @param {Roo.View} this
9151      * @param {Number} index The index of the target node
9152      * @param {HTMLElement} node The target node
9153      * @param {Roo.EventObject} e The raw event object
9154      */
9155         "dblclick" : true,
9156     /**
9157      * @event contextmenu
9158      * Fires when a template node is right clicked.
9159      * @param {Roo.View} this
9160      * @param {Number} index The index of the target node
9161      * @param {HTMLElement} node The target node
9162      * @param {Roo.EventObject} e The raw event object
9163      */
9164         "contextmenu" : true,
9165     /**
9166      * @event selectionchange
9167      * Fires when the selected nodes change.
9168      * @param {Roo.View} this
9169      * @param {Array} selections Array of the selected nodes
9170      */
9171         "selectionchange" : true,
9172
9173     /**
9174      * @event beforeselect
9175      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9176      * @param {Roo.View} this
9177      * @param {HTMLElement} node The node to be selected
9178      * @param {Array} selections Array of currently selected nodes
9179      */
9180         "beforeselect" : true
9181     });
9182
9183     this.el.on({
9184         "click": this.onClick,
9185         "dblclick": this.onDblClick,
9186         "contextmenu": this.onContextMenu,
9187         scope:this
9188     });
9189
9190     this.selections = [];
9191     this.nodes = [];
9192     this.cmp = new Roo.CompositeElementLite([]);
9193     if(this.store){
9194         this.store = Roo.factory(this.store, Roo.data);
9195         this.setStore(this.store, true);
9196     }
9197     Roo.View.superclass.constructor.call(this);
9198 };
9199
9200 Roo.extend(Roo.View, Roo.util.Observable, {
9201     
9202      /**
9203      * @cfg {Roo.data.Store} store Data store to load data from.
9204      */
9205     store : false,
9206     
9207     /**
9208      * @cfg {String|Roo.Element} el The container element.
9209      */
9210     el : '',
9211     
9212     /**
9213      * @cfg {String|Roo.Template} tpl The template used by this View 
9214      */
9215     tpl : false,
9216     
9217     /**
9218      * @cfg {String} selectedClass The css class to add to selected nodes
9219      */
9220     selectedClass : "x-view-selected",
9221      /**
9222      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9223      */
9224     emptyText : "",
9225     /**
9226      * @cfg {Boolean} multiSelect Allow multiple selection
9227      */
9228     
9229     multiSelect : false,
9230     /**
9231      * @cfg {Boolean} singleSelect Allow single selection
9232      */
9233     singleSelect:  false,
9234     
9235     /**
9236      * Returns the element this view is bound to.
9237      * @return {Roo.Element}
9238      */
9239     getEl : function(){
9240         return this.el;
9241     },
9242
9243     /**
9244      * Refreshes the view.
9245      */
9246     refresh : function(){
9247         var t = this.tpl;
9248         this.clearSelections();
9249         this.el.update("");
9250         var html = [];
9251         var records = this.store.getRange();
9252         if(records.length < 1){
9253             this.el.update(this.emptyText);
9254             return;
9255         }
9256         for(var i = 0, len = records.length; i < len; i++){
9257             var data = this.prepareData(records[i].data, i, records[i]);
9258             html[html.length] = t.apply(data);
9259         }
9260         this.el.update(html.join(""));
9261         this.nodes = this.el.dom.childNodes;
9262         this.updateIndexes(0);
9263     },
9264
9265     /**
9266      * Function to override to reformat the data that is sent to
9267      * the template for each node.
9268      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9269      * a JSON object for an UpdateManager bound view).
9270      */
9271     prepareData : function(data){
9272         return data;
9273     },
9274
9275     onUpdate : function(ds, record){
9276         this.clearSelections();
9277         var index = this.store.indexOf(record);
9278         var n = this.nodes[index];
9279         this.tpl.insertBefore(n, this.prepareData(record.data));
9280         n.parentNode.removeChild(n);
9281         this.updateIndexes(index, index);
9282     },
9283
9284     onAdd : function(ds, records, index){
9285         this.clearSelections();
9286         if(this.nodes.length == 0){
9287             this.refresh();
9288             return;
9289         }
9290         var n = this.nodes[index];
9291         for(var i = 0, len = records.length; i < len; i++){
9292             var d = this.prepareData(records[i].data);
9293             if(n){
9294                 this.tpl.insertBefore(n, d);
9295             }else{
9296                 this.tpl.append(this.el, d);
9297             }
9298         }
9299         this.updateIndexes(index);
9300     },
9301
9302     onRemove : function(ds, record, index){
9303         this.clearSelections();
9304         this.el.dom.removeChild(this.nodes[index]);
9305         this.updateIndexes(index);
9306     },
9307
9308     /**
9309      * Refresh an individual node.
9310      * @param {Number} index
9311      */
9312     refreshNode : function(index){
9313         this.onUpdate(this.store, this.store.getAt(index));
9314     },
9315
9316     updateIndexes : function(startIndex, endIndex){
9317         var ns = this.nodes;
9318         startIndex = startIndex || 0;
9319         endIndex = endIndex || ns.length - 1;
9320         for(var i = startIndex; i <= endIndex; i++){
9321             ns[i].nodeIndex = i;
9322         }
9323     },
9324
9325     /**
9326      * Changes the data store this view uses and refresh the view.
9327      * @param {Store} store
9328      */
9329     setStore : function(store, initial){
9330         if(!initial && this.store){
9331             this.store.un("datachanged", this.refresh);
9332             this.store.un("add", this.onAdd);
9333             this.store.un("remove", this.onRemove);
9334             this.store.un("update", this.onUpdate);
9335             this.store.un("clear", this.refresh);
9336         }
9337         if(store){
9338           
9339             store.on("datachanged", this.refresh, this);
9340             store.on("add", this.onAdd, this);
9341             store.on("remove", this.onRemove, this);
9342             store.on("update", this.onUpdate, this);
9343             store.on("clear", this.refresh, this);
9344         }
9345         
9346         if(store){
9347             this.refresh();
9348         }
9349     },
9350
9351     /**
9352      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9353      * @param {HTMLElement} node
9354      * @return {HTMLElement} The template node
9355      */
9356     findItemFromChild : function(node){
9357         var el = this.el.dom;
9358         if(!node || node.parentNode == el){
9359                     return node;
9360             }
9361             var p = node.parentNode;
9362             while(p && p != el){
9363             if(p.parentNode == el){
9364                 return p;
9365             }
9366             p = p.parentNode;
9367         }
9368             return null;
9369     },
9370
9371     /** @ignore */
9372     onClick : function(e){
9373         var item = this.findItemFromChild(e.getTarget());
9374         if(item){
9375             var index = this.indexOf(item);
9376             if(this.onItemClick(item, index, e) !== false){
9377                 this.fireEvent("click", this, index, item, e);
9378             }
9379         }else{
9380             this.clearSelections();
9381         }
9382     },
9383
9384     /** @ignore */
9385     onContextMenu : function(e){
9386         var item = this.findItemFromChild(e.getTarget());
9387         if(item){
9388             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9389         }
9390     },
9391
9392     /** @ignore */
9393     onDblClick : function(e){
9394         var item = this.findItemFromChild(e.getTarget());
9395         if(item){
9396             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9397         }
9398     },
9399
9400     onItemClick : function(item, index, e){
9401         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9402             return false;
9403         }
9404         if(this.multiSelect || this.singleSelect){
9405             if(this.multiSelect && e.shiftKey && this.lastSelection){
9406                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9407             }else{
9408                 this.select(item, this.multiSelect && e.ctrlKey);
9409                 this.lastSelection = item;
9410             }
9411             e.preventDefault();
9412         }
9413         return true;
9414     },
9415
9416     /**
9417      * Get the number of selected nodes.
9418      * @return {Number}
9419      */
9420     getSelectionCount : function(){
9421         return this.selections.length;
9422     },
9423
9424     /**
9425      * Get the currently selected nodes.
9426      * @return {Array} An array of HTMLElements
9427      */
9428     getSelectedNodes : function(){
9429         return this.selections;
9430     },
9431
9432     /**
9433      * Get the indexes of the selected nodes.
9434      * @return {Array}
9435      */
9436     getSelectedIndexes : function(){
9437         var indexes = [], s = this.selections;
9438         for(var i = 0, len = s.length; i < len; i++){
9439             indexes.push(s[i].nodeIndex);
9440         }
9441         return indexes;
9442     },
9443
9444     /**
9445      * Clear all selections
9446      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9447      */
9448     clearSelections : function(suppressEvent){
9449         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9450             this.cmp.elements = this.selections;
9451             this.cmp.removeClass(this.selectedClass);
9452             this.selections = [];
9453             if(!suppressEvent){
9454                 this.fireEvent("selectionchange", this, this.selections);
9455             }
9456         }
9457     },
9458
9459     /**
9460      * Returns true if the passed node is selected
9461      * @param {HTMLElement/Number} node The node or node index
9462      * @return {Boolean}
9463      */
9464     isSelected : function(node){
9465         var s = this.selections;
9466         if(s.length < 1){
9467             return false;
9468         }
9469         node = this.getNode(node);
9470         return s.indexOf(node) !== -1;
9471     },
9472
9473     /**
9474      * Selects nodes.
9475      * @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
9476      * @param {Boolean} keepExisting (optional) true to keep existing selections
9477      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9478      */
9479     select : function(nodeInfo, keepExisting, suppressEvent){
9480         if(nodeInfo instanceof Array){
9481             if(!keepExisting){
9482                 this.clearSelections(true);
9483             }
9484             for(var i = 0, len = nodeInfo.length; i < len; i++){
9485                 this.select(nodeInfo[i], true, true);
9486             }
9487         } else{
9488             var node = this.getNode(nodeInfo);
9489             if(node && !this.isSelected(node)){
9490                 if(!keepExisting){
9491                     this.clearSelections(true);
9492                 }
9493                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9494                     Roo.fly(node).addClass(this.selectedClass);
9495                     this.selections.push(node);
9496                     if(!suppressEvent){
9497                         this.fireEvent("selectionchange", this, this.selections);
9498                     }
9499                 }
9500             }
9501         }
9502     },
9503
9504     /**
9505      * Gets a template node.
9506      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9507      * @return {HTMLElement} The node or null if it wasn't found
9508      */
9509     getNode : function(nodeInfo){
9510         if(typeof nodeInfo == "string"){
9511             return document.getElementById(nodeInfo);
9512         }else if(typeof nodeInfo == "number"){
9513             return this.nodes[nodeInfo];
9514         }
9515         return nodeInfo;
9516     },
9517
9518     /**
9519      * Gets a range template nodes.
9520      * @param {Number} startIndex
9521      * @param {Number} endIndex
9522      * @return {Array} An array of nodes
9523      */
9524     getNodes : function(start, end){
9525         var ns = this.nodes;
9526         start = start || 0;
9527         end = typeof end == "undefined" ? ns.length - 1 : end;
9528         var nodes = [];
9529         if(start <= end){
9530             for(var i = start; i <= end; i++){
9531                 nodes.push(ns[i]);
9532             }
9533         } else{
9534             for(var i = start; i >= end; i--){
9535                 nodes.push(ns[i]);
9536             }
9537         }
9538         return nodes;
9539     },
9540
9541     /**
9542      * Finds the index of the passed node
9543      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9544      * @return {Number} The index of the node or -1
9545      */
9546     indexOf : function(node){
9547         node = this.getNode(node);
9548         if(typeof node.nodeIndex == "number"){
9549             return node.nodeIndex;
9550         }
9551         var ns = this.nodes;
9552         for(var i = 0, len = ns.length; i < len; i++){
9553             if(ns[i] == node){
9554                 return i;
9555             }
9556         }
9557         return -1;
9558     }
9559 });
9560 /*
9561  * Based on:
9562  * Ext JS Library 1.1.1
9563  * Copyright(c) 2006-2007, Ext JS, LLC.
9564  *
9565  * Originally Released Under LGPL - original licence link has changed is not relivant.
9566  *
9567  * Fork - LGPL
9568  * <script type="text/javascript">
9569  */
9570
9571 /**
9572  * @class Roo.JsonView
9573  * @extends Roo.View
9574  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9575 <pre><code>
9576 var view = new Roo.JsonView({
9577     container: "my-element",
9578     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9579     multiSelect: true, 
9580     jsonRoot: "data" 
9581 });
9582
9583 // listen for node click?
9584 view.on("click", function(vw, index, node, e){
9585     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9586 });
9587
9588 // direct load of JSON data
9589 view.load("foobar.php");
9590
9591 // Example from my blog list
9592 var tpl = new Roo.Template(
9593     '&lt;div class="entry"&gt;' +
9594     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9595     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9596     "&lt;/div&gt;&lt;hr /&gt;"
9597 );
9598
9599 var moreView = new Roo.JsonView({
9600     container :  "entry-list", 
9601     template : tpl,
9602     jsonRoot: "posts"
9603 });
9604 moreView.on("beforerender", this.sortEntries, this);
9605 moreView.load({
9606     url: "/blog/get-posts.php",
9607     params: "allposts=true",
9608     text: "Loading Blog Entries..."
9609 });
9610 </code></pre>
9611
9612 * Note: old code is supported with arguments : (container, template, config)
9613
9614
9615  * @constructor
9616  * Create a new JsonView
9617  * 
9618  * @param {Object} config The config object
9619  * 
9620  */
9621 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9622     
9623     
9624     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9625
9626     var um = this.el.getUpdateManager();
9627     um.setRenderer(this);
9628     um.on("update", this.onLoad, this);
9629     um.on("failure", this.onLoadException, this);
9630
9631     /**
9632      * @event beforerender
9633      * Fires before rendering of the downloaded JSON data.
9634      * @param {Roo.JsonView} this
9635      * @param {Object} data The JSON data loaded
9636      */
9637     /**
9638      * @event load
9639      * Fires when data is loaded.
9640      * @param {Roo.JsonView} this
9641      * @param {Object} data The JSON data loaded
9642      * @param {Object} response The raw Connect response object
9643      */
9644     /**
9645      * @event loadexception
9646      * Fires when loading fails.
9647      * @param {Roo.JsonView} this
9648      * @param {Object} response The raw Connect response object
9649      */
9650     this.addEvents({
9651         'beforerender' : true,
9652         'load' : true,
9653         'loadexception' : true
9654     });
9655 };
9656 Roo.extend(Roo.JsonView, Roo.View, {
9657     /**
9658      * @type {String} The root property in the loaded JSON object that contains the data
9659      */
9660     jsonRoot : "",
9661
9662     /**
9663      * Refreshes the view.
9664      */
9665     refresh : function(){
9666         this.clearSelections();
9667         this.el.update("");
9668         var html = [];
9669         var o = this.jsonData;
9670         if(o && o.length > 0){
9671             for(var i = 0, len = o.length; i < len; i++){
9672                 var data = this.prepareData(o[i], i, o);
9673                 html[html.length] = this.tpl.apply(data);
9674             }
9675         }else{
9676             html.push(this.emptyText);
9677         }
9678         this.el.update(html.join(""));
9679         this.nodes = this.el.dom.childNodes;
9680         this.updateIndexes(0);
9681     },
9682
9683     /**
9684      * 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.
9685      * @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:
9686      <pre><code>
9687      view.load({
9688          url: "your-url.php",
9689          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9690          callback: yourFunction,
9691          scope: yourObject, //(optional scope)
9692          discardUrl: false,
9693          nocache: false,
9694          text: "Loading...",
9695          timeout: 30,
9696          scripts: false
9697      });
9698      </code></pre>
9699      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9700      * 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.
9701      * @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}
9702      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9703      * @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.
9704      */
9705     load : function(){
9706         var um = this.el.getUpdateManager();
9707         um.update.apply(um, arguments);
9708     },
9709
9710     render : function(el, response){
9711         this.clearSelections();
9712         this.el.update("");
9713         var o;
9714         try{
9715             o = Roo.util.JSON.decode(response.responseText);
9716             if(this.jsonRoot){
9717                 
9718                 o = o[this.jsonRoot];
9719             }
9720         } catch(e){
9721         }
9722         /**
9723          * The current JSON data or null
9724          */
9725         this.jsonData = o;
9726         this.beforeRender();
9727         this.refresh();
9728     },
9729
9730 /**
9731  * Get the number of records in the current JSON dataset
9732  * @return {Number}
9733  */
9734     getCount : function(){
9735         return this.jsonData ? this.jsonData.length : 0;
9736     },
9737
9738 /**
9739  * Returns the JSON object for the specified node(s)
9740  * @param {HTMLElement/Array} node The node or an array of nodes
9741  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9742  * you get the JSON object for the node
9743  */
9744     getNodeData : function(node){
9745         if(node instanceof Array){
9746             var data = [];
9747             for(var i = 0, len = node.length; i < len; i++){
9748                 data.push(this.getNodeData(node[i]));
9749             }
9750             return data;
9751         }
9752         return this.jsonData[this.indexOf(node)] || null;
9753     },
9754
9755     beforeRender : function(){
9756         this.snapshot = this.jsonData;
9757         if(this.sortInfo){
9758             this.sort.apply(this, this.sortInfo);
9759         }
9760         this.fireEvent("beforerender", this, this.jsonData);
9761     },
9762
9763     onLoad : function(el, o){
9764         this.fireEvent("load", this, this.jsonData, o);
9765     },
9766
9767     onLoadException : function(el, o){
9768         this.fireEvent("loadexception", this, o);
9769     },
9770
9771 /**
9772  * Filter the data by a specific property.
9773  * @param {String} property A property on your JSON objects
9774  * @param {String/RegExp} value Either string that the property values
9775  * should start with, or a RegExp to test against the property
9776  */
9777     filter : function(property, value){
9778         if(this.jsonData){
9779             var data = [];
9780             var ss = this.snapshot;
9781             if(typeof value == "string"){
9782                 var vlen = value.length;
9783                 if(vlen == 0){
9784                     this.clearFilter();
9785                     return;
9786                 }
9787                 value = value.toLowerCase();
9788                 for(var i = 0, len = ss.length; i < len; i++){
9789                     var o = ss[i];
9790                     if(o[property].substr(0, vlen).toLowerCase() == value){
9791                         data.push(o);
9792                     }
9793                 }
9794             } else if(value.exec){ // regex?
9795                 for(var i = 0, len = ss.length; i < len; i++){
9796                     var o = ss[i];
9797                     if(value.test(o[property])){
9798                         data.push(o);
9799                     }
9800                 }
9801             } else{
9802                 return;
9803             }
9804             this.jsonData = data;
9805             this.refresh();
9806         }
9807     },
9808
9809 /**
9810  * Filter by a function. The passed function will be called with each
9811  * object in the current dataset. If the function returns true the value is kept,
9812  * otherwise it is filtered.
9813  * @param {Function} fn
9814  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9815  */
9816     filterBy : function(fn, scope){
9817         if(this.jsonData){
9818             var data = [];
9819             var ss = this.snapshot;
9820             for(var i = 0, len = ss.length; i < len; i++){
9821                 var o = ss[i];
9822                 if(fn.call(scope || this, o)){
9823                     data.push(o);
9824                 }
9825             }
9826             this.jsonData = data;
9827             this.refresh();
9828         }
9829     },
9830
9831 /**
9832  * Clears the current filter.
9833  */
9834     clearFilter : function(){
9835         if(this.snapshot && this.jsonData != this.snapshot){
9836             this.jsonData = this.snapshot;
9837             this.refresh();
9838         }
9839     },
9840
9841
9842 /**
9843  * Sorts the data for this view and refreshes it.
9844  * @param {String} property A property on your JSON objects to sort on
9845  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9846  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9847  */
9848     sort : function(property, dir, sortType){
9849         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9850         if(this.jsonData){
9851             var p = property;
9852             var dsc = dir && dir.toLowerCase() == "desc";
9853             var f = function(o1, o2){
9854                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9855                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9856                 ;
9857                 if(v1 < v2){
9858                     return dsc ? +1 : -1;
9859                 } else if(v1 > v2){
9860                     return dsc ? -1 : +1;
9861                 } else{
9862                     return 0;
9863                 }
9864             };
9865             this.jsonData.sort(f);
9866             this.refresh();
9867             if(this.jsonData != this.snapshot){
9868                 this.snapshot.sort(f);
9869             }
9870         }
9871     }
9872 });/*
9873  * Based on:
9874  * Ext JS Library 1.1.1
9875  * Copyright(c) 2006-2007, Ext JS, LLC.
9876  *
9877  * Originally Released Under LGPL - original licence link has changed is not relivant.
9878  *
9879  * Fork - LGPL
9880  * <script type="text/javascript">
9881  */
9882  
9883
9884 /**
9885  * @class Roo.ColorPalette
9886  * @extends Roo.Component
9887  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9888  * Here's an example of typical usage:
9889  * <pre><code>
9890 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9891 cp.render('my-div');
9892
9893 cp.on('select', function(palette, selColor){
9894     // do something with selColor
9895 });
9896 </code></pre>
9897  * @constructor
9898  * Create a new ColorPalette
9899  * @param {Object} config The config object
9900  */
9901 Roo.ColorPalette = function(config){
9902     Roo.ColorPalette.superclass.constructor.call(this, config);
9903     this.addEvents({
9904         /**
9905              * @event select
9906              * Fires when a color is selected
9907              * @param {ColorPalette} this
9908              * @param {String} color The 6-digit color hex code (without the # symbol)
9909              */
9910         select: true
9911     });
9912
9913     if(this.handler){
9914         this.on("select", this.handler, this.scope, true);
9915     }
9916 };
9917 Roo.extend(Roo.ColorPalette, Roo.Component, {
9918     /**
9919      * @cfg {String} itemCls
9920      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9921      */
9922     itemCls : "x-color-palette",
9923     /**
9924      * @cfg {String} value
9925      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9926      * the hex codes are case-sensitive.
9927      */
9928     value : null,
9929     clickEvent:'click',
9930     // private
9931     ctype: "Roo.ColorPalette",
9932
9933     /**
9934      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9935      */
9936     allowReselect : false,
9937
9938     /**
9939      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9940      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9941      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9942      * of colors with the width setting until the box is symmetrical.</p>
9943      * <p>You can override individual colors if needed:</p>
9944      * <pre><code>
9945 var cp = new Roo.ColorPalette();
9946 cp.colors[0] = "FF0000";  // change the first box to red
9947 </code></pre>
9948
9949 Or you can provide a custom array of your own for complete control:
9950 <pre><code>
9951 var cp = new Roo.ColorPalette();
9952 cp.colors = ["000000", "993300", "333300"];
9953 </code></pre>
9954      * @type Array
9955      */
9956     colors : [
9957         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9958         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9959         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9960         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9961         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9962     ],
9963
9964     // private
9965     onRender : function(container, position){
9966         var t = new Roo.MasterTemplate(
9967             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9968         );
9969         var c = this.colors;
9970         for(var i = 0, len = c.length; i < len; i++){
9971             t.add([c[i]]);
9972         }
9973         var el = document.createElement("div");
9974         el.className = this.itemCls;
9975         t.overwrite(el);
9976         container.dom.insertBefore(el, position);
9977         this.el = Roo.get(el);
9978         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9979         if(this.clickEvent != 'click'){
9980             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9981         }
9982     },
9983
9984     // private
9985     afterRender : function(){
9986         Roo.ColorPalette.superclass.afterRender.call(this);
9987         if(this.value){
9988             var s = this.value;
9989             this.value = null;
9990             this.select(s);
9991         }
9992     },
9993
9994     // private
9995     handleClick : function(e, t){
9996         e.preventDefault();
9997         if(!this.disabled){
9998             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9999             this.select(c.toUpperCase());
10000         }
10001     },
10002
10003     /**
10004      * Selects the specified color in the palette (fires the select event)
10005      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10006      */
10007     select : function(color){
10008         color = color.replace("#", "");
10009         if(color != this.value || this.allowReselect){
10010             var el = this.el;
10011             if(this.value){
10012                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10013             }
10014             el.child("a.color-"+color).addClass("x-color-palette-sel");
10015             this.value = color;
10016             this.fireEvent("select", this, color);
10017         }
10018     }
10019 });/*
10020  * Based on:
10021  * Ext JS Library 1.1.1
10022  * Copyright(c) 2006-2007, Ext JS, LLC.
10023  *
10024  * Originally Released Under LGPL - original licence link has changed is not relivant.
10025  *
10026  * Fork - LGPL
10027  * <script type="text/javascript">
10028  */
10029  
10030 /**
10031  * @class Roo.DatePicker
10032  * @extends Roo.Component
10033  * Simple date picker class.
10034  * @constructor
10035  * Create a new DatePicker
10036  * @param {Object} config The config object
10037  */
10038 Roo.DatePicker = function(config){
10039     Roo.DatePicker.superclass.constructor.call(this, config);
10040
10041     this.value = config && config.value ?
10042                  config.value.clearTime() : new Date().clearTime();
10043
10044     this.addEvents({
10045         /**
10046              * @event select
10047              * Fires when a date is selected
10048              * @param {DatePicker} this
10049              * @param {Date} date The selected date
10050              */
10051         select: true
10052     });
10053
10054     if(this.handler){
10055         this.on("select", this.handler,  this.scope || this);
10056     }
10057     // build the disabledDatesRE
10058     if(!this.disabledDatesRE && this.disabledDates){
10059         var dd = this.disabledDates;
10060         var re = "(?:";
10061         for(var i = 0; i < dd.length; i++){
10062             re += dd[i];
10063             if(i != dd.length-1) re += "|";
10064         }
10065         this.disabledDatesRE = new RegExp(re + ")");
10066     }
10067 };
10068
10069 Roo.extend(Roo.DatePicker, Roo.Component, {
10070     /**
10071      * @cfg {String} todayText
10072      * The text to display on the button that selects the current date (defaults to "Today")
10073      */
10074     todayText : "Today",
10075     /**
10076      * @cfg {String} okText
10077      * The text to display on the ok button
10078      */
10079     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10080     /**
10081      * @cfg {String} cancelText
10082      * The text to display on the cancel button
10083      */
10084     cancelText : "Cancel",
10085     /**
10086      * @cfg {String} todayTip
10087      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10088      */
10089     todayTip : "{0} (Spacebar)",
10090     /**
10091      * @cfg {Date} minDate
10092      * Minimum allowable date (JavaScript date object, defaults to null)
10093      */
10094     minDate : null,
10095     /**
10096      * @cfg {Date} maxDate
10097      * Maximum allowable date (JavaScript date object, defaults to null)
10098      */
10099     maxDate : null,
10100     /**
10101      * @cfg {String} minText
10102      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10103      */
10104     minText : "This date is before the minimum date",
10105     /**
10106      * @cfg {String} maxText
10107      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10108      */
10109     maxText : "This date is after the maximum date",
10110     /**
10111      * @cfg {String} format
10112      * The default date format string which can be overriden for localization support.  The format must be
10113      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10114      */
10115     format : "m/d/y",
10116     /**
10117      * @cfg {Array} disabledDays
10118      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10119      */
10120     disabledDays : null,
10121     /**
10122      * @cfg {String} disabledDaysText
10123      * The tooltip to display when the date falls on a disabled day (defaults to "")
10124      */
10125     disabledDaysText : "",
10126     /**
10127      * @cfg {RegExp} disabledDatesRE
10128      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10129      */
10130     disabledDatesRE : null,
10131     /**
10132      * @cfg {String} disabledDatesText
10133      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10134      */
10135     disabledDatesText : "",
10136     /**
10137      * @cfg {Boolean} constrainToViewport
10138      * True to constrain the date picker to the viewport (defaults to true)
10139      */
10140     constrainToViewport : true,
10141     /**
10142      * @cfg {Array} monthNames
10143      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10144      */
10145     monthNames : Date.monthNames,
10146     /**
10147      * @cfg {Array} dayNames
10148      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10149      */
10150     dayNames : Date.dayNames,
10151     /**
10152      * @cfg {String} nextText
10153      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10154      */
10155     nextText: 'Next Month (Control+Right)',
10156     /**
10157      * @cfg {String} prevText
10158      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10159      */
10160     prevText: 'Previous Month (Control+Left)',
10161     /**
10162      * @cfg {String} monthYearText
10163      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10164      */
10165     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10166     /**
10167      * @cfg {Number} startDay
10168      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10169      */
10170     startDay : 0,
10171     /**
10172      * @cfg {Bool} showClear
10173      * Show a clear button (usefull for date form elements that can be blank.)
10174      */
10175     
10176     showClear: false,
10177     
10178     /**
10179      * Sets the value of the date field
10180      * @param {Date} value The date to set
10181      */
10182     setValue : function(value){
10183         var old = this.value;
10184         this.value = value.clearTime(true);
10185         if(this.el){
10186             this.update(this.value);
10187         }
10188     },
10189
10190     /**
10191      * Gets the current selected value of the date field
10192      * @return {Date} The selected date
10193      */
10194     getValue : function(){
10195         return this.value;
10196     },
10197
10198     // private
10199     focus : function(){
10200         if(this.el){
10201             this.update(this.activeDate);
10202         }
10203     },
10204
10205     // private
10206     onRender : function(container, position){
10207         var m = [
10208              '<table cellspacing="0">',
10209                 '<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>',
10210                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10211         var dn = this.dayNames;
10212         for(var i = 0; i < 7; i++){
10213             var d = this.startDay+i;
10214             if(d > 6){
10215                 d = d-7;
10216             }
10217             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10218         }
10219         m[m.length] = "</tr></thead><tbody><tr>";
10220         for(var i = 0; i < 42; i++) {
10221             if(i % 7 == 0 && i != 0){
10222                 m[m.length] = "</tr><tr>";
10223             }
10224             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10225         }
10226         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10227             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10228
10229         var el = document.createElement("div");
10230         el.className = "x-date-picker";
10231         el.innerHTML = m.join("");
10232
10233         container.dom.insertBefore(el, position);
10234
10235         this.el = Roo.get(el);
10236         this.eventEl = Roo.get(el.firstChild);
10237
10238         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10239             handler: this.showPrevMonth,
10240             scope: this,
10241             preventDefault:true,
10242             stopDefault:true
10243         });
10244
10245         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10246             handler: this.showNextMonth,
10247             scope: this,
10248             preventDefault:true,
10249             stopDefault:true
10250         });
10251
10252         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10253
10254         this.monthPicker = this.el.down('div.x-date-mp');
10255         this.monthPicker.enableDisplayMode('block');
10256         
10257         var kn = new Roo.KeyNav(this.eventEl, {
10258             "left" : function(e){
10259                 e.ctrlKey ?
10260                     this.showPrevMonth() :
10261                     this.update(this.activeDate.add("d", -1));
10262             },
10263
10264             "right" : function(e){
10265                 e.ctrlKey ?
10266                     this.showNextMonth() :
10267                     this.update(this.activeDate.add("d", 1));
10268             },
10269
10270             "up" : function(e){
10271                 e.ctrlKey ?
10272                     this.showNextYear() :
10273                     this.update(this.activeDate.add("d", -7));
10274             },
10275
10276             "down" : function(e){
10277                 e.ctrlKey ?
10278                     this.showPrevYear() :
10279                     this.update(this.activeDate.add("d", 7));
10280             },
10281
10282             "pageUp" : function(e){
10283                 this.showNextMonth();
10284             },
10285
10286             "pageDown" : function(e){
10287                 this.showPrevMonth();
10288             },
10289
10290             "enter" : function(e){
10291                 e.stopPropagation();
10292                 return true;
10293             },
10294
10295             scope : this
10296         });
10297
10298         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10299
10300         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10301
10302         this.el.unselectable();
10303         
10304         this.cells = this.el.select("table.x-date-inner tbody td");
10305         this.textNodes = this.el.query("table.x-date-inner tbody span");
10306
10307         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10308             text: "&#160;",
10309             tooltip: this.monthYearText
10310         });
10311
10312         this.mbtn.on('click', this.showMonthPicker, this);
10313         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10314
10315
10316         var today = (new Date()).dateFormat(this.format);
10317         
10318         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10319         if (this.showClear) {
10320             baseTb.add( new Roo.Toolbar.Fill());
10321         }
10322         baseTb.add({
10323             text: String.format(this.todayText, today),
10324             tooltip: String.format(this.todayTip, today),
10325             handler: this.selectToday,
10326             scope: this
10327         });
10328         
10329         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10330             
10331         //});
10332         if (this.showClear) {
10333             
10334             baseTb.add( new Roo.Toolbar.Fill());
10335             baseTb.add({
10336                 text: '&#160;',
10337                 cls: 'x-btn-icon x-btn-clear',
10338                 handler: function() {
10339                     //this.value = '';
10340                     this.fireEvent("select", this, '');
10341                 },
10342                 scope: this
10343             });
10344         }
10345         
10346         
10347         if(Roo.isIE){
10348             this.el.repaint();
10349         }
10350         this.update(this.value);
10351     },
10352
10353     createMonthPicker : function(){
10354         if(!this.monthPicker.dom.firstChild){
10355             var buf = ['<table border="0" cellspacing="0">'];
10356             for(var i = 0; i < 6; i++){
10357                 buf.push(
10358                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10359                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10360                     i == 0 ?
10361                     '<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>' :
10362                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10363                 );
10364             }
10365             buf.push(
10366                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10367                     this.okText,
10368                     '</button><button type="button" class="x-date-mp-cancel">',
10369                     this.cancelText,
10370                     '</button></td></tr>',
10371                 '</table>'
10372             );
10373             this.monthPicker.update(buf.join(''));
10374             this.monthPicker.on('click', this.onMonthClick, this);
10375             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10376
10377             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10378             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10379
10380             this.mpMonths.each(function(m, a, i){
10381                 i += 1;
10382                 if((i%2) == 0){
10383                     m.dom.xmonth = 5 + Math.round(i * .5);
10384                 }else{
10385                     m.dom.xmonth = Math.round((i-1) * .5);
10386                 }
10387             });
10388         }
10389     },
10390
10391     showMonthPicker : function(){
10392         this.createMonthPicker();
10393         var size = this.el.getSize();
10394         this.monthPicker.setSize(size);
10395         this.monthPicker.child('table').setSize(size);
10396
10397         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10398         this.updateMPMonth(this.mpSelMonth);
10399         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10400         this.updateMPYear(this.mpSelYear);
10401
10402         this.monthPicker.slideIn('t', {duration:.2});
10403     },
10404
10405     updateMPYear : function(y){
10406         this.mpyear = y;
10407         var ys = this.mpYears.elements;
10408         for(var i = 1; i <= 10; i++){
10409             var td = ys[i-1], y2;
10410             if((i%2) == 0){
10411                 y2 = y + Math.round(i * .5);
10412                 td.firstChild.innerHTML = y2;
10413                 td.xyear = y2;
10414             }else{
10415                 y2 = y - (5-Math.round(i * .5));
10416                 td.firstChild.innerHTML = y2;
10417                 td.xyear = y2;
10418             }
10419             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10420         }
10421     },
10422
10423     updateMPMonth : function(sm){
10424         this.mpMonths.each(function(m, a, i){
10425             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10426         });
10427     },
10428
10429     selectMPMonth: function(m){
10430         
10431     },
10432
10433     onMonthClick : function(e, t){
10434         e.stopEvent();
10435         var el = new Roo.Element(t), pn;
10436         if(el.is('button.x-date-mp-cancel')){
10437             this.hideMonthPicker();
10438         }
10439         else if(el.is('button.x-date-mp-ok')){
10440             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10441             this.hideMonthPicker();
10442         }
10443         else if(pn = el.up('td.x-date-mp-month', 2)){
10444             this.mpMonths.removeClass('x-date-mp-sel');
10445             pn.addClass('x-date-mp-sel');
10446             this.mpSelMonth = pn.dom.xmonth;
10447         }
10448         else if(pn = el.up('td.x-date-mp-year', 2)){
10449             this.mpYears.removeClass('x-date-mp-sel');
10450             pn.addClass('x-date-mp-sel');
10451             this.mpSelYear = pn.dom.xyear;
10452         }
10453         else if(el.is('a.x-date-mp-prev')){
10454             this.updateMPYear(this.mpyear-10);
10455         }
10456         else if(el.is('a.x-date-mp-next')){
10457             this.updateMPYear(this.mpyear+10);
10458         }
10459     },
10460
10461     onMonthDblClick : function(e, t){
10462         e.stopEvent();
10463         var el = new Roo.Element(t), pn;
10464         if(pn = el.up('td.x-date-mp-month', 2)){
10465             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10466             this.hideMonthPicker();
10467         }
10468         else if(pn = el.up('td.x-date-mp-year', 2)){
10469             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10470             this.hideMonthPicker();
10471         }
10472     },
10473
10474     hideMonthPicker : function(disableAnim){
10475         if(this.monthPicker){
10476             if(disableAnim === true){
10477                 this.monthPicker.hide();
10478             }else{
10479                 this.monthPicker.slideOut('t', {duration:.2});
10480             }
10481         }
10482     },
10483
10484     // private
10485     showPrevMonth : function(e){
10486         this.update(this.activeDate.add("mo", -1));
10487     },
10488
10489     // private
10490     showNextMonth : function(e){
10491         this.update(this.activeDate.add("mo", 1));
10492     },
10493
10494     // private
10495     showPrevYear : function(){
10496         this.update(this.activeDate.add("y", -1));
10497     },
10498
10499     // private
10500     showNextYear : function(){
10501         this.update(this.activeDate.add("y", 1));
10502     },
10503
10504     // private
10505     handleMouseWheel : function(e){
10506         var delta = e.getWheelDelta();
10507         if(delta > 0){
10508             this.showPrevMonth();
10509             e.stopEvent();
10510         } else if(delta < 0){
10511             this.showNextMonth();
10512             e.stopEvent();
10513         }
10514     },
10515
10516     // private
10517     handleDateClick : function(e, t){
10518         e.stopEvent();
10519         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10520             this.setValue(new Date(t.dateValue));
10521             this.fireEvent("select", this, this.value);
10522         }
10523     },
10524
10525     // private
10526     selectToday : function(){
10527         this.setValue(new Date().clearTime());
10528         this.fireEvent("select", this, this.value);
10529     },
10530
10531     // private
10532     update : function(date){
10533         var vd = this.activeDate;
10534         this.activeDate = date;
10535         if(vd && this.el){
10536             var t = date.getTime();
10537             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10538                 this.cells.removeClass("x-date-selected");
10539                 this.cells.each(function(c){
10540                    if(c.dom.firstChild.dateValue == t){
10541                        c.addClass("x-date-selected");
10542                        setTimeout(function(){
10543                             try{c.dom.firstChild.focus();}catch(e){}
10544                        }, 50);
10545                        return false;
10546                    }
10547                 });
10548                 return;
10549             }
10550         }
10551         var days = date.getDaysInMonth();
10552         var firstOfMonth = date.getFirstDateOfMonth();
10553         var startingPos = firstOfMonth.getDay()-this.startDay;
10554
10555         if(startingPos <= this.startDay){
10556             startingPos += 7;
10557         }
10558
10559         var pm = date.add("mo", -1);
10560         var prevStart = pm.getDaysInMonth()-startingPos;
10561
10562         var cells = this.cells.elements;
10563         var textEls = this.textNodes;
10564         days += startingPos;
10565
10566         // convert everything to numbers so it's fast
10567         var day = 86400000;
10568         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10569         var today = new Date().clearTime().getTime();
10570         var sel = date.clearTime().getTime();
10571         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10572         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10573         var ddMatch = this.disabledDatesRE;
10574         var ddText = this.disabledDatesText;
10575         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10576         var ddaysText = this.disabledDaysText;
10577         var format = this.format;
10578
10579         var setCellClass = function(cal, cell){
10580             cell.title = "";
10581             var t = d.getTime();
10582             cell.firstChild.dateValue = t;
10583             if(t == today){
10584                 cell.className += " x-date-today";
10585                 cell.title = cal.todayText;
10586             }
10587             if(t == sel){
10588                 cell.className += " x-date-selected";
10589                 setTimeout(function(){
10590                     try{cell.firstChild.focus();}catch(e){}
10591                 }, 50);
10592             }
10593             // disabling
10594             if(t < min) {
10595                 cell.className = " x-date-disabled";
10596                 cell.title = cal.minText;
10597                 return;
10598             }
10599             if(t > max) {
10600                 cell.className = " x-date-disabled";
10601                 cell.title = cal.maxText;
10602                 return;
10603             }
10604             if(ddays){
10605                 if(ddays.indexOf(d.getDay()) != -1){
10606                     cell.title = ddaysText;
10607                     cell.className = " x-date-disabled";
10608                 }
10609             }
10610             if(ddMatch && format){
10611                 var fvalue = d.dateFormat(format);
10612                 if(ddMatch.test(fvalue)){
10613                     cell.title = ddText.replace("%0", fvalue);
10614                     cell.className = " x-date-disabled";
10615                 }
10616             }
10617         };
10618
10619         var i = 0;
10620         for(; i < startingPos; i++) {
10621             textEls[i].innerHTML = (++prevStart);
10622             d.setDate(d.getDate()+1);
10623             cells[i].className = "x-date-prevday";
10624             setCellClass(this, cells[i]);
10625         }
10626         for(; i < days; i++){
10627             intDay = i - startingPos + 1;
10628             textEls[i].innerHTML = (intDay);
10629             d.setDate(d.getDate()+1);
10630             cells[i].className = "x-date-active";
10631             setCellClass(this, cells[i]);
10632         }
10633         var extraDays = 0;
10634         for(; i < 42; i++) {
10635              textEls[i].innerHTML = (++extraDays);
10636              d.setDate(d.getDate()+1);
10637              cells[i].className = "x-date-nextday";
10638              setCellClass(this, cells[i]);
10639         }
10640
10641         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10642
10643         if(!this.internalRender){
10644             var main = this.el.dom.firstChild;
10645             var w = main.offsetWidth;
10646             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10647             Roo.fly(main).setWidth(w);
10648             this.internalRender = true;
10649             // opera does not respect the auto grow header center column
10650             // then, after it gets a width opera refuses to recalculate
10651             // without a second pass
10652             if(Roo.isOpera && !this.secondPass){
10653                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10654                 this.secondPass = true;
10655                 this.update.defer(10, this, [date]);
10656             }
10657         }
10658     }
10659 });/*
10660  * Based on:
10661  * Ext JS Library 1.1.1
10662  * Copyright(c) 2006-2007, Ext JS, LLC.
10663  *
10664  * Originally Released Under LGPL - original licence link has changed is not relivant.
10665  *
10666  * Fork - LGPL
10667  * <script type="text/javascript">
10668  */
10669 /**
10670  * @class Roo.TabPanel
10671  * @extends Roo.util.Observable
10672  * A lightweight tab container.
10673  * <br><br>
10674  * Usage:
10675  * <pre><code>
10676 // basic tabs 1, built from existing content
10677 var tabs = new Roo.TabPanel("tabs1");
10678 tabs.addTab("script", "View Script");
10679 tabs.addTab("markup", "View Markup");
10680 tabs.activate("script");
10681
10682 // more advanced tabs, built from javascript
10683 var jtabs = new Roo.TabPanel("jtabs");
10684 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10685
10686 // set up the UpdateManager
10687 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10688 var updater = tab2.getUpdateManager();
10689 updater.setDefaultUrl("ajax1.htm");
10690 tab2.on('activate', updater.refresh, updater, true);
10691
10692 // Use setUrl for Ajax loading
10693 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10694 tab3.setUrl("ajax2.htm", null, true);
10695
10696 // Disabled tab
10697 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10698 tab4.disable();
10699
10700 jtabs.activate("jtabs-1");
10701  * </code></pre>
10702  * @constructor
10703  * Create a new TabPanel.
10704  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10705  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10706  */
10707 Roo.TabPanel = function(container, config){
10708     /**
10709     * The container element for this TabPanel.
10710     * @type Roo.Element
10711     */
10712     this.el = Roo.get(container, true);
10713     if(config){
10714         if(typeof config == "boolean"){
10715             this.tabPosition = config ? "bottom" : "top";
10716         }else{
10717             Roo.apply(this, config);
10718         }
10719     }
10720     if(this.tabPosition == "bottom"){
10721         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10722         this.el.addClass("x-tabs-bottom");
10723     }
10724     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10725     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10726     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10727     if(Roo.isIE){
10728         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10729     }
10730     if(this.tabPosition != "bottom"){
10731     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10732      * @type Roo.Element
10733      */
10734       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10735       this.el.addClass("x-tabs-top");
10736     }
10737     this.items = [];
10738
10739     this.bodyEl.setStyle("position", "relative");
10740
10741     this.active = null;
10742     this.activateDelegate = this.activate.createDelegate(this);
10743
10744     this.addEvents({
10745         /**
10746          * @event tabchange
10747          * Fires when the active tab changes
10748          * @param {Roo.TabPanel} this
10749          * @param {Roo.TabPanelItem} activePanel The new active tab
10750          */
10751         "tabchange": true,
10752         /**
10753          * @event beforetabchange
10754          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10755          * @param {Roo.TabPanel} this
10756          * @param {Object} e Set cancel to true on this object to cancel the tab change
10757          * @param {Roo.TabPanelItem} tab The tab being changed to
10758          */
10759         "beforetabchange" : true
10760     });
10761
10762     Roo.EventManager.onWindowResize(this.onResize, this);
10763     this.cpad = this.el.getPadding("lr");
10764     this.hiddenCount = 0;
10765
10766     Roo.TabPanel.superclass.constructor.call(this);
10767 };
10768
10769 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10770         /*
10771          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10772          */
10773     tabPosition : "top",
10774         /*
10775          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10776          */
10777     currentTabWidth : 0,
10778         /*
10779          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10780          */
10781     minTabWidth : 40,
10782         /*
10783          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10784          */
10785     maxTabWidth : 250,
10786         /*
10787          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10788          */
10789     preferredTabWidth : 175,
10790         /*
10791          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10792          */
10793     resizeTabs : false,
10794         /*
10795          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10796          */
10797     monitorResize : true,
10798
10799     /**
10800      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10801      * @param {String} id The id of the div to use <b>or create</b>
10802      * @param {String} text The text for the tab
10803      * @param {String} content (optional) Content to put in the TabPanelItem body
10804      * @param {Boolean} closable (optional) True to create a close icon on the tab
10805      * @return {Roo.TabPanelItem} The created TabPanelItem
10806      */
10807     addTab : function(id, text, content, closable){
10808         var item = new Roo.TabPanelItem(this, id, text, closable);
10809         this.addTabItem(item);
10810         if(content){
10811             item.setContent(content);
10812         }
10813         return item;
10814     },
10815
10816     /**
10817      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10818      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10819      * @return {Roo.TabPanelItem}
10820      */
10821     getTab : function(id){
10822         return this.items[id];
10823     },
10824
10825     /**
10826      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10827      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10828      */
10829     hideTab : function(id){
10830         var t = this.items[id];
10831         if(!t.isHidden()){
10832            t.setHidden(true);
10833            this.hiddenCount++;
10834            this.autoSizeTabs();
10835         }
10836     },
10837
10838     /**
10839      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10840      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10841      */
10842     unhideTab : function(id){
10843         var t = this.items[id];
10844         if(t.isHidden()){
10845            t.setHidden(false);
10846            this.hiddenCount--;
10847            this.autoSizeTabs();
10848         }
10849     },
10850
10851     /**
10852      * Adds an existing {@link Roo.TabPanelItem}.
10853      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10854      */
10855     addTabItem : function(item){
10856         this.items[item.id] = item;
10857         this.items.push(item);
10858         if(this.resizeTabs){
10859            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10860            this.autoSizeTabs();
10861         }else{
10862             item.autoSize();
10863         }
10864     },
10865
10866     /**
10867      * Removes a {@link Roo.TabPanelItem}.
10868      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10869      */
10870     removeTab : function(id){
10871         var items = this.items;
10872         var tab = items[id];
10873         if(!tab) { return; }
10874         var index = items.indexOf(tab);
10875         if(this.active == tab && items.length > 1){
10876             var newTab = this.getNextAvailable(index);
10877             if(newTab) {
10878                 newTab.activate();
10879             }
10880         }
10881         this.stripEl.dom.removeChild(tab.pnode.dom);
10882         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10883             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10884         }
10885         items.splice(index, 1);
10886         delete this.items[tab.id];
10887         tab.fireEvent("close", tab);
10888         tab.purgeListeners();
10889         this.autoSizeTabs();
10890     },
10891
10892     getNextAvailable : function(start){
10893         var items = this.items;
10894         var index = start;
10895         // look for a next tab that will slide over to
10896         // replace the one being removed
10897         while(index < items.length){
10898             var item = items[++index];
10899             if(item && !item.isHidden()){
10900                 return item;
10901             }
10902         }
10903         // if one isn't found select the previous tab (on the left)
10904         index = start;
10905         while(index >= 0){
10906             var item = items[--index];
10907             if(item && !item.isHidden()){
10908                 return item;
10909             }
10910         }
10911         return null;
10912     },
10913
10914     /**
10915      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10916      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10917      */
10918     disableTab : function(id){
10919         var tab = this.items[id];
10920         if(tab && this.active != tab){
10921             tab.disable();
10922         }
10923     },
10924
10925     /**
10926      * Enables a {@link Roo.TabPanelItem} that is disabled.
10927      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10928      */
10929     enableTab : function(id){
10930         var tab = this.items[id];
10931         tab.enable();
10932     },
10933
10934     /**
10935      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10936      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10937      * @return {Roo.TabPanelItem} The TabPanelItem.
10938      */
10939     activate : function(id){
10940         var tab = this.items[id];
10941         if(!tab){
10942             return null;
10943         }
10944         if(tab == this.active || tab.disabled){
10945             return tab;
10946         }
10947         var e = {};
10948         this.fireEvent("beforetabchange", this, e, tab);
10949         if(e.cancel !== true && !tab.disabled){
10950             if(this.active){
10951                 this.active.hide();
10952             }
10953             this.active = this.items[id];
10954             this.active.show();
10955             this.fireEvent("tabchange", this, this.active);
10956         }
10957         return tab;
10958     },
10959
10960     /**
10961      * Gets the active {@link Roo.TabPanelItem}.
10962      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10963      */
10964     getActiveTab : function(){
10965         return this.active;
10966     },
10967
10968     /**
10969      * Updates the tab body element to fit the height of the container element
10970      * for overflow scrolling
10971      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10972      */
10973     syncHeight : function(targetHeight){
10974         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10975         var bm = this.bodyEl.getMargins();
10976         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10977         this.bodyEl.setHeight(newHeight);
10978         return newHeight;
10979     },
10980
10981     onResize : function(){
10982         if(this.monitorResize){
10983             this.autoSizeTabs();
10984         }
10985     },
10986
10987     /**
10988      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10989      */
10990     beginUpdate : function(){
10991         this.updating = true;
10992     },
10993
10994     /**
10995      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10996      */
10997     endUpdate : function(){
10998         this.updating = false;
10999         this.autoSizeTabs();
11000     },
11001
11002     /**
11003      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11004      */
11005     autoSizeTabs : function(){
11006         var count = this.items.length;
11007         var vcount = count - this.hiddenCount;
11008         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11009         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11010         var availWidth = Math.floor(w / vcount);
11011         var b = this.stripBody;
11012         if(b.getWidth() > w){
11013             var tabs = this.items;
11014             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11015             if(availWidth < this.minTabWidth){
11016                 /*if(!this.sleft){    // incomplete scrolling code
11017                     this.createScrollButtons();
11018                 }
11019                 this.showScroll();
11020                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11021             }
11022         }else{
11023             if(this.currentTabWidth < this.preferredTabWidth){
11024                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11025             }
11026         }
11027     },
11028
11029     /**
11030      * Returns the number of tabs in this TabPanel.
11031      * @return {Number}
11032      */
11033      getCount : function(){
11034          return this.items.length;
11035      },
11036
11037     /**
11038      * Resizes all the tabs to the passed width
11039      * @param {Number} The new width
11040      */
11041     setTabWidth : function(width){
11042         this.currentTabWidth = width;
11043         for(var i = 0, len = this.items.length; i < len; i++) {
11044                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11045         }
11046     },
11047
11048     /**
11049      * Destroys this TabPanel
11050      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11051      */
11052     destroy : function(removeEl){
11053         Roo.EventManager.removeResizeListener(this.onResize, this);
11054         for(var i = 0, len = this.items.length; i < len; i++){
11055             this.items[i].purgeListeners();
11056         }
11057         if(removeEl === true){
11058             this.el.update("");
11059             this.el.remove();
11060         }
11061     }
11062 });
11063
11064 /**
11065  * @class Roo.TabPanelItem
11066  * @extends Roo.util.Observable
11067  * Represents an individual item (tab plus body) in a TabPanel.
11068  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11069  * @param {String} id The id of this TabPanelItem
11070  * @param {String} text The text for the tab of this TabPanelItem
11071  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11072  */
11073 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11074     /**
11075      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11076      * @type Roo.TabPanel
11077      */
11078     this.tabPanel = tabPanel;
11079     /**
11080      * The id for this TabPanelItem
11081      * @type String
11082      */
11083     this.id = id;
11084     /** @private */
11085     this.disabled = false;
11086     /** @private */
11087     this.text = text;
11088     /** @private */
11089     this.loaded = false;
11090     this.closable = closable;
11091
11092     /**
11093      * The body element for this TabPanelItem.
11094      * @type Roo.Element
11095      */
11096     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11097     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11098     this.bodyEl.setStyle("display", "block");
11099     this.bodyEl.setStyle("zoom", "1");
11100     this.hideAction();
11101
11102     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11103     /** @private */
11104     this.el = Roo.get(els.el, true);
11105     this.inner = Roo.get(els.inner, true);
11106     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11107     this.pnode = Roo.get(els.el.parentNode, true);
11108     this.el.on("mousedown", this.onTabMouseDown, this);
11109     this.el.on("click", this.onTabClick, this);
11110     /** @private */
11111     if(closable){
11112         var c = Roo.get(els.close, true);
11113         c.dom.title = this.closeText;
11114         c.addClassOnOver("close-over");
11115         c.on("click", this.closeClick, this);
11116      }
11117
11118     this.addEvents({
11119          /**
11120          * @event activate
11121          * Fires when this tab becomes the active tab.
11122          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11123          * @param {Roo.TabPanelItem} this
11124          */
11125         "activate": true,
11126         /**
11127          * @event beforeclose
11128          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11129          * @param {Roo.TabPanelItem} this
11130          * @param {Object} e Set cancel to true on this object to cancel the close.
11131          */
11132         "beforeclose": true,
11133         /**
11134          * @event close
11135          * Fires when this tab is closed.
11136          * @param {Roo.TabPanelItem} this
11137          */
11138          "close": true,
11139         /**
11140          * @event deactivate
11141          * Fires when this tab is no longer the active tab.
11142          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11143          * @param {Roo.TabPanelItem} this
11144          */
11145          "deactivate" : true
11146     });
11147     this.hidden = false;
11148
11149     Roo.TabPanelItem.superclass.constructor.call(this);
11150 };
11151
11152 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11153     purgeListeners : function(){
11154        Roo.util.Observable.prototype.purgeListeners.call(this);
11155        this.el.removeAllListeners();
11156     },
11157     /**
11158      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11159      */
11160     show : function(){
11161         this.pnode.addClass("on");
11162         this.showAction();
11163         if(Roo.isOpera){
11164             this.tabPanel.stripWrap.repaint();
11165         }
11166         this.fireEvent("activate", this.tabPanel, this);
11167     },
11168
11169     /**
11170      * Returns true if this tab is the active tab.
11171      * @return {Boolean}
11172      */
11173     isActive : function(){
11174         return this.tabPanel.getActiveTab() == this;
11175     },
11176
11177     /**
11178      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11179      */
11180     hide : function(){
11181         this.pnode.removeClass("on");
11182         this.hideAction();
11183         this.fireEvent("deactivate", this.tabPanel, this);
11184     },
11185
11186     hideAction : function(){
11187         this.bodyEl.hide();
11188         this.bodyEl.setStyle("position", "absolute");
11189         this.bodyEl.setLeft("-20000px");
11190         this.bodyEl.setTop("-20000px");
11191     },
11192
11193     showAction : function(){
11194         this.bodyEl.setStyle("position", "relative");
11195         this.bodyEl.setTop("");
11196         this.bodyEl.setLeft("");
11197         this.bodyEl.show();
11198     },
11199
11200     /**
11201      * Set the tooltip for the tab.
11202      * @param {String} tooltip The tab's tooltip
11203      */
11204     setTooltip : function(text){
11205         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11206             this.textEl.dom.qtip = text;
11207             this.textEl.dom.removeAttribute('title');
11208         }else{
11209             this.textEl.dom.title = text;
11210         }
11211     },
11212
11213     onTabClick : function(e){
11214         e.preventDefault();
11215         this.tabPanel.activate(this.id);
11216     },
11217
11218     onTabMouseDown : function(e){
11219         e.preventDefault();
11220         this.tabPanel.activate(this.id);
11221     },
11222
11223     getWidth : function(){
11224         return this.inner.getWidth();
11225     },
11226
11227     setWidth : function(width){
11228         var iwidth = width - this.pnode.getPadding("lr");
11229         this.inner.setWidth(iwidth);
11230         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11231         this.pnode.setWidth(width);
11232     },
11233
11234     /**
11235      * Show or hide the tab
11236      * @param {Boolean} hidden True to hide or false to show.
11237      */
11238     setHidden : function(hidden){
11239         this.hidden = hidden;
11240         this.pnode.setStyle("display", hidden ? "none" : "");
11241     },
11242
11243     /**
11244      * Returns true if this tab is "hidden"
11245      * @return {Boolean}
11246      */
11247     isHidden : function(){
11248         return this.hidden;
11249     },
11250
11251     /**
11252      * Returns the text for this tab
11253      * @return {String}
11254      */
11255     getText : function(){
11256         return this.text;
11257     },
11258
11259     autoSize : function(){
11260         //this.el.beginMeasure();
11261         this.textEl.setWidth(1);
11262         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11263         //this.el.endMeasure();
11264     },
11265
11266     /**
11267      * Sets the text for the tab (Note: this also sets the tooltip text)
11268      * @param {String} text The tab's text and tooltip
11269      */
11270     setText : function(text){
11271         this.text = text;
11272         this.textEl.update(text);
11273         this.setTooltip(text);
11274         if(!this.tabPanel.resizeTabs){
11275             this.autoSize();
11276         }
11277     },
11278     /**
11279      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11280      */
11281     activate : function(){
11282         this.tabPanel.activate(this.id);
11283     },
11284
11285     /**
11286      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11287      */
11288     disable : function(){
11289         if(this.tabPanel.active != this){
11290             this.disabled = true;
11291             this.pnode.addClass("disabled");
11292         }
11293     },
11294
11295     /**
11296      * Enables this TabPanelItem if it was previously disabled.
11297      */
11298     enable : function(){
11299         this.disabled = false;
11300         this.pnode.removeClass("disabled");
11301     },
11302
11303     /**
11304      * Sets the content for this TabPanelItem.
11305      * @param {String} content The content
11306      * @param {Boolean} loadScripts true to look for and load scripts
11307      */
11308     setContent : function(content, loadScripts){
11309         this.bodyEl.update(content, loadScripts);
11310     },
11311
11312     /**
11313      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11314      * @return {Roo.UpdateManager} The UpdateManager
11315      */
11316     getUpdateManager : function(){
11317         return this.bodyEl.getUpdateManager();
11318     },
11319
11320     /**
11321      * Set a URL to be used to load the content for this TabPanelItem.
11322      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11323      * @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)
11324      * @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)
11325      * @return {Roo.UpdateManager} The UpdateManager
11326      */
11327     setUrl : function(url, params, loadOnce){
11328         if(this.refreshDelegate){
11329             this.un('activate', this.refreshDelegate);
11330         }
11331         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11332         this.on("activate", this.refreshDelegate);
11333         return this.bodyEl.getUpdateManager();
11334     },
11335
11336     /** @private */
11337     _handleRefresh : function(url, params, loadOnce){
11338         if(!loadOnce || !this.loaded){
11339             var updater = this.bodyEl.getUpdateManager();
11340             updater.update(url, params, this._setLoaded.createDelegate(this));
11341         }
11342     },
11343
11344     /**
11345      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11346      *   Will fail silently if the setUrl method has not been called.
11347      *   This does not activate the panel, just updates its content.
11348      */
11349     refresh : function(){
11350         if(this.refreshDelegate){
11351            this.loaded = false;
11352            this.refreshDelegate();
11353         }
11354     },
11355
11356     /** @private */
11357     _setLoaded : function(){
11358         this.loaded = true;
11359     },
11360
11361     /** @private */
11362     closeClick : function(e){
11363         var o = {};
11364         e.stopEvent();
11365         this.fireEvent("beforeclose", this, o);
11366         if(o.cancel !== true){
11367             this.tabPanel.removeTab(this.id);
11368         }
11369     },
11370     /**
11371      * The text displayed in the tooltip for the close icon.
11372      * @type String
11373      */
11374     closeText : "Close this tab"
11375 });
11376
11377 /** @private */
11378 Roo.TabPanel.prototype.createStrip = function(container){
11379     var strip = document.createElement("div");
11380     strip.className = "x-tabs-wrap";
11381     container.appendChild(strip);
11382     return strip;
11383 };
11384 /** @private */
11385 Roo.TabPanel.prototype.createStripList = function(strip){
11386     // div wrapper for retard IE
11387     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>';
11388     return strip.firstChild.firstChild.firstChild.firstChild;
11389 };
11390 /** @private */
11391 Roo.TabPanel.prototype.createBody = function(container){
11392     var body = document.createElement("div");
11393     Roo.id(body, "tab-body");
11394     Roo.fly(body).addClass("x-tabs-body");
11395     container.appendChild(body);
11396     return body;
11397 };
11398 /** @private */
11399 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11400     var body = Roo.getDom(id);
11401     if(!body){
11402         body = document.createElement("div");
11403         body.id = id;
11404     }
11405     Roo.fly(body).addClass("x-tabs-item-body");
11406     bodyEl.insertBefore(body, bodyEl.firstChild);
11407     return body;
11408 };
11409 /** @private */
11410 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11411     var td = document.createElement("td");
11412     stripEl.appendChild(td);
11413     if(closable){
11414         td.className = "x-tabs-closable";
11415         if(!this.closeTpl){
11416             this.closeTpl = 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>' +
11419                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11420             );
11421         }
11422         var el = this.closeTpl.overwrite(td, {"text": text});
11423         var close = el.getElementsByTagName("div")[0];
11424         var inner = el.getElementsByTagName("em")[0];
11425         return {"el": el, "close": close, "inner": inner};
11426     } else {
11427         if(!this.tabTpl){
11428             this.tabTpl = new Roo.Template(
11429                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11430                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11431             );
11432         }
11433         var el = this.tabTpl.overwrite(td, {"text": text});
11434         var inner = el.getElementsByTagName("em")[0];
11435         return {"el": el, "inner": inner};
11436     }
11437 };/*
11438  * Based on:
11439  * Ext JS Library 1.1.1
11440  * Copyright(c) 2006-2007, Ext JS, LLC.
11441  *
11442  * Originally Released Under LGPL - original licence link has changed is not relivant.
11443  *
11444  * Fork - LGPL
11445  * <script type="text/javascript">
11446  */
11447
11448 /**
11449  * @class Roo.Button
11450  * @extends Roo.util.Observable
11451  * Simple Button class
11452  * @cfg {String} text The button text
11453  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11454  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11455  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11456  * @cfg {Object} scope The scope of the handler
11457  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11458  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11459  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11460  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11461  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11462  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11463    applies if enableToggle = true)
11464  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11465  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11466   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11467  * @constructor
11468  * Create a new button
11469  * @param {Object} config The config object
11470  */
11471 Roo.Button = function(renderTo, config)
11472 {
11473     if (!config) {
11474         config = renderTo;
11475         renderTo = config.renderTo || false;
11476     }
11477     
11478     Roo.apply(this, config);
11479     this.addEvents({
11480         /**
11481              * @event click
11482              * Fires when this button is clicked
11483              * @param {Button} this
11484              * @param {EventObject} e The click event
11485              */
11486             "click" : true,
11487         /**
11488              * @event toggle
11489              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11490              * @param {Button} this
11491              * @param {Boolean} pressed
11492              */
11493             "toggle" : true,
11494         /**
11495              * @event mouseover
11496              * Fires when the mouse hovers over the button
11497              * @param {Button} this
11498              * @param {Event} e The event object
11499              */
11500         'mouseover' : true,
11501         /**
11502              * @event mouseout
11503              * Fires when the mouse exits the button
11504              * @param {Button} this
11505              * @param {Event} e The event object
11506              */
11507         'mouseout': true,
11508          /**
11509              * @event render
11510              * Fires when the button is rendered
11511              * @param {Button} this
11512              */
11513         'render': true
11514     });
11515     if(this.menu){
11516         this.menu = Roo.menu.MenuMgr.get(this.menu);
11517     }
11518     // register listeners first!!  - so render can be captured..
11519     Roo.util.Observable.call(this);
11520     if(renderTo){
11521         this.render(renderTo);
11522     }
11523     
11524   
11525 };
11526
11527 Roo.extend(Roo.Button, Roo.util.Observable, {
11528     /**
11529      * 
11530      */
11531     
11532     /**
11533      * Read-only. True if this button is hidden
11534      * @type Boolean
11535      */
11536     hidden : false,
11537     /**
11538      * Read-only. True if this button is disabled
11539      * @type Boolean
11540      */
11541     disabled : false,
11542     /**
11543      * Read-only. True if this button is pressed (only if enableToggle = true)
11544      * @type Boolean
11545      */
11546     pressed : false,
11547
11548     /**
11549      * @cfg {Number} tabIndex 
11550      * The DOM tabIndex for this button (defaults to undefined)
11551      */
11552     tabIndex : undefined,
11553
11554     /**
11555      * @cfg {Boolean} enableToggle
11556      * True to enable pressed/not pressed toggling (defaults to false)
11557      */
11558     enableToggle: false,
11559     /**
11560      * @cfg {Mixed} menu
11561      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11562      */
11563     menu : undefined,
11564     /**
11565      * @cfg {String} menuAlign
11566      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11567      */
11568     menuAlign : "tl-bl?",
11569
11570     /**
11571      * @cfg {String} iconCls
11572      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11573      */
11574     iconCls : undefined,
11575     /**
11576      * @cfg {String} type
11577      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11578      */
11579     type : 'button',
11580
11581     // private
11582     menuClassTarget: 'tr',
11583
11584     /**
11585      * @cfg {String} clickEvent
11586      * The type of event to map to the button's event handler (defaults to 'click')
11587      */
11588     clickEvent : 'click',
11589
11590     /**
11591      * @cfg {Boolean} handleMouseEvents
11592      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11593      */
11594     handleMouseEvents : true,
11595
11596     /**
11597      * @cfg {String} tooltipType
11598      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11599      */
11600     tooltipType : 'qtip',
11601
11602     /**
11603      * @cfg {String} cls
11604      * A CSS class to apply to the button's main element.
11605      */
11606     
11607     /**
11608      * @cfg {Roo.Template} template (Optional)
11609      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11610      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11611      * require code modifications if required elements (e.g. a button) aren't present.
11612      */
11613
11614     // private
11615     render : function(renderTo){
11616         var btn;
11617         if(this.hideParent){
11618             this.parentEl = Roo.get(renderTo);
11619         }
11620         if(!this.dhconfig){
11621             if(!this.template){
11622                 if(!Roo.Button.buttonTemplate){
11623                     // hideous table template
11624                     Roo.Button.buttonTemplate = new Roo.Template(
11625                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11626                         '<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>',
11627                         "</tr></tbody></table>");
11628                 }
11629                 this.template = Roo.Button.buttonTemplate;
11630             }
11631             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11632             var btnEl = btn.child("button:first");
11633             btnEl.on('focus', this.onFocus, this);
11634             btnEl.on('blur', this.onBlur, this);
11635             if(this.cls){
11636                 btn.addClass(this.cls);
11637             }
11638             if(this.icon){
11639                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11640             }
11641             if(this.iconCls){
11642                 btnEl.addClass(this.iconCls);
11643                 if(!this.cls){
11644                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11645                 }
11646             }
11647             if(this.tabIndex !== undefined){
11648                 btnEl.dom.tabIndex = this.tabIndex;
11649             }
11650             if(this.tooltip){
11651                 if(typeof this.tooltip == 'object'){
11652                     Roo.QuickTips.tips(Roo.apply({
11653                           target: btnEl.id
11654                     }, this.tooltip));
11655                 } else {
11656                     btnEl.dom[this.tooltipType] = this.tooltip;
11657                 }
11658             }
11659         }else{
11660             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11661         }
11662         this.el = btn;
11663         if(this.id){
11664             this.el.dom.id = this.el.id = this.id;
11665         }
11666         if(this.menu){
11667             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11668             this.menu.on("show", this.onMenuShow, this);
11669             this.menu.on("hide", this.onMenuHide, this);
11670         }
11671         btn.addClass("x-btn");
11672         if(Roo.isIE && !Roo.isIE7){
11673             this.autoWidth.defer(1, this);
11674         }else{
11675             this.autoWidth();
11676         }
11677         if(this.handleMouseEvents){
11678             btn.on("mouseover", this.onMouseOver, this);
11679             btn.on("mouseout", this.onMouseOut, this);
11680             btn.on("mousedown", this.onMouseDown, this);
11681         }
11682         btn.on(this.clickEvent, this.onClick, this);
11683         //btn.on("mouseup", this.onMouseUp, this);
11684         if(this.hidden){
11685             this.hide();
11686         }
11687         if(this.disabled){
11688             this.disable();
11689         }
11690         Roo.ButtonToggleMgr.register(this);
11691         if(this.pressed){
11692             this.el.addClass("x-btn-pressed");
11693         }
11694         if(this.repeat){
11695             var repeater = new Roo.util.ClickRepeater(btn,
11696                 typeof this.repeat == "object" ? this.repeat : {}
11697             );
11698             repeater.on("click", this.onClick,  this);
11699         }
11700         
11701         this.fireEvent('render', this);
11702         
11703     },
11704     /**
11705      * Returns the button's underlying element
11706      * @return {Roo.Element} The element
11707      */
11708     getEl : function(){
11709         return this.el;  
11710     },
11711     
11712     /**
11713      * Destroys this Button and removes any listeners.
11714      */
11715     destroy : function(){
11716         Roo.ButtonToggleMgr.unregister(this);
11717         this.el.removeAllListeners();
11718         this.purgeListeners();
11719         this.el.remove();
11720     },
11721
11722     // private
11723     autoWidth : function(){
11724         if(this.el){
11725             this.el.setWidth("auto");
11726             if(Roo.isIE7 && Roo.isStrict){
11727                 var ib = this.el.child('button');
11728                 if(ib && ib.getWidth() > 20){
11729                     ib.clip();
11730                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11731                 }
11732             }
11733             if(this.minWidth){
11734                 if(this.hidden){
11735                     this.el.beginMeasure();
11736                 }
11737                 if(this.el.getWidth() < this.minWidth){
11738                     this.el.setWidth(this.minWidth);
11739                 }
11740                 if(this.hidden){
11741                     this.el.endMeasure();
11742                 }
11743             }
11744         }
11745     },
11746
11747     /**
11748      * Assigns this button's click handler
11749      * @param {Function} handler The function to call when the button is clicked
11750      * @param {Object} scope (optional) Scope for the function passed in
11751      */
11752     setHandler : function(handler, scope){
11753         this.handler = handler;
11754         this.scope = scope;  
11755     },
11756     
11757     /**
11758      * Sets this button's text
11759      * @param {String} text The button text
11760      */
11761     setText : function(text){
11762         this.text = text;
11763         if(this.el){
11764             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11765         }
11766         this.autoWidth();
11767     },
11768     
11769     /**
11770      * Gets the text for this button
11771      * @return {String} The button text
11772      */
11773     getText : function(){
11774         return this.text;  
11775     },
11776     
11777     /**
11778      * Show this button
11779      */
11780     show: function(){
11781         this.hidden = false;
11782         if(this.el){
11783             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11784         }
11785     },
11786     
11787     /**
11788      * Hide this button
11789      */
11790     hide: function(){
11791         this.hidden = true;
11792         if(this.el){
11793             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11794         }
11795     },
11796     
11797     /**
11798      * Convenience function for boolean show/hide
11799      * @param {Boolean} visible True to show, false to hide
11800      */
11801     setVisible: function(visible){
11802         if(visible) {
11803             this.show();
11804         }else{
11805             this.hide();
11806         }
11807     },
11808     
11809     /**
11810      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11811      * @param {Boolean} state (optional) Force a particular state
11812      */
11813     toggle : function(state){
11814         state = state === undefined ? !this.pressed : state;
11815         if(state != this.pressed){
11816             if(state){
11817                 this.el.addClass("x-btn-pressed");
11818                 this.pressed = true;
11819                 this.fireEvent("toggle", this, true);
11820             }else{
11821                 this.el.removeClass("x-btn-pressed");
11822                 this.pressed = false;
11823                 this.fireEvent("toggle", this, false);
11824             }
11825             if(this.toggleHandler){
11826                 this.toggleHandler.call(this.scope || this, this, state);
11827             }
11828         }
11829     },
11830     
11831     /**
11832      * Focus the button
11833      */
11834     focus : function(){
11835         this.el.child('button:first').focus();
11836     },
11837     
11838     /**
11839      * Disable this button
11840      */
11841     disable : function(){
11842         if(this.el){
11843             this.el.addClass("x-btn-disabled");
11844         }
11845         this.disabled = true;
11846     },
11847     
11848     /**
11849      * Enable this button
11850      */
11851     enable : function(){
11852         if(this.el){
11853             this.el.removeClass("x-btn-disabled");
11854         }
11855         this.disabled = false;
11856     },
11857
11858     /**
11859      * Convenience function for boolean enable/disable
11860      * @param {Boolean} enabled True to enable, false to disable
11861      */
11862     setDisabled : function(v){
11863         this[v !== true ? "enable" : "disable"]();
11864     },
11865
11866     // private
11867     onClick : function(e){
11868         if(e){
11869             e.preventDefault();
11870         }
11871         if(e.button != 0){
11872             return;
11873         }
11874         if(!this.disabled){
11875             if(this.enableToggle){
11876                 this.toggle();
11877             }
11878             if(this.menu && !this.menu.isVisible()){
11879                 this.menu.show(this.el, this.menuAlign);
11880             }
11881             this.fireEvent("click", this, e);
11882             if(this.handler){
11883                 this.el.removeClass("x-btn-over");
11884                 this.handler.call(this.scope || this, this, e);
11885             }
11886         }
11887     },
11888     // private
11889     onMouseOver : function(e){
11890         if(!this.disabled){
11891             this.el.addClass("x-btn-over");
11892             this.fireEvent('mouseover', this, e);
11893         }
11894     },
11895     // private
11896     onMouseOut : function(e){
11897         if(!e.within(this.el,  true)){
11898             this.el.removeClass("x-btn-over");
11899             this.fireEvent('mouseout', this, e);
11900         }
11901     },
11902     // private
11903     onFocus : function(e){
11904         if(!this.disabled){
11905             this.el.addClass("x-btn-focus");
11906         }
11907     },
11908     // private
11909     onBlur : function(e){
11910         this.el.removeClass("x-btn-focus");
11911     },
11912     // private
11913     onMouseDown : function(e){
11914         if(!this.disabled && e.button == 0){
11915             this.el.addClass("x-btn-click");
11916             Roo.get(document).on('mouseup', this.onMouseUp, this);
11917         }
11918     },
11919     // private
11920     onMouseUp : function(e){
11921         if(e.button == 0){
11922             this.el.removeClass("x-btn-click");
11923             Roo.get(document).un('mouseup', this.onMouseUp, this);
11924         }
11925     },
11926     // private
11927     onMenuShow : function(e){
11928         this.el.addClass("x-btn-menu-active");
11929     },
11930     // private
11931     onMenuHide : function(e){
11932         this.el.removeClass("x-btn-menu-active");
11933     }   
11934 });
11935
11936 // Private utility class used by Button
11937 Roo.ButtonToggleMgr = function(){
11938    var groups = {};
11939    
11940    function toggleGroup(btn, state){
11941        if(state){
11942            var g = groups[btn.toggleGroup];
11943            for(var i = 0, l = g.length; i < l; i++){
11944                if(g[i] != btn){
11945                    g[i].toggle(false);
11946                }
11947            }
11948        }
11949    }
11950    
11951    return {
11952        register : function(btn){
11953            if(!btn.toggleGroup){
11954                return;
11955            }
11956            var g = groups[btn.toggleGroup];
11957            if(!g){
11958                g = groups[btn.toggleGroup] = [];
11959            }
11960            g.push(btn);
11961            btn.on("toggle", toggleGroup);
11962        },
11963        
11964        unregister : function(btn){
11965            if(!btn.toggleGroup){
11966                return;
11967            }
11968            var g = groups[btn.toggleGroup];
11969            if(g){
11970                g.remove(btn);
11971                btn.un("toggle", toggleGroup);
11972            }
11973        }
11974    };
11975 }();/*
11976  * Based on:
11977  * Ext JS Library 1.1.1
11978  * Copyright(c) 2006-2007, Ext JS, LLC.
11979  *
11980  * Originally Released Under LGPL - original licence link has changed is not relivant.
11981  *
11982  * Fork - LGPL
11983  * <script type="text/javascript">
11984  */
11985  
11986 /**
11987  * @class Roo.SplitButton
11988  * @extends Roo.Button
11989  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11990  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11991  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11992  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11993  * @cfg {String} arrowTooltip The title attribute of the arrow
11994  * @constructor
11995  * Create a new menu button
11996  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11997  * @param {Object} config The config object
11998  */
11999 Roo.SplitButton = function(renderTo, config){
12000     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12001     /**
12002      * @event arrowclick
12003      * Fires when this button's arrow is clicked
12004      * @param {SplitButton} this
12005      * @param {EventObject} e The click event
12006      */
12007     this.addEvents({"arrowclick":true});
12008 };
12009
12010 Roo.extend(Roo.SplitButton, Roo.Button, {
12011     render : function(renderTo){
12012         // this is one sweet looking template!
12013         var tpl = new Roo.Template(
12014             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12015             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12016             '<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>',
12017             "</tbody></table></td><td>",
12018             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12019             '<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>',
12020             "</tbody></table></td></tr></table>"
12021         );
12022         var btn = tpl.append(renderTo, [this.text, this.type], true);
12023         var btnEl = btn.child("button");
12024         if(this.cls){
12025             btn.addClass(this.cls);
12026         }
12027         if(this.icon){
12028             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12029         }
12030         if(this.iconCls){
12031             btnEl.addClass(this.iconCls);
12032             if(!this.cls){
12033                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12034             }
12035         }
12036         this.el = btn;
12037         if(this.handleMouseEvents){
12038             btn.on("mouseover", this.onMouseOver, this);
12039             btn.on("mouseout", this.onMouseOut, this);
12040             btn.on("mousedown", this.onMouseDown, this);
12041             btn.on("mouseup", this.onMouseUp, this);
12042         }
12043         btn.on(this.clickEvent, this.onClick, this);
12044         if(this.tooltip){
12045             if(typeof this.tooltip == 'object'){
12046                 Roo.QuickTips.tips(Roo.apply({
12047                       target: btnEl.id
12048                 }, this.tooltip));
12049             } else {
12050                 btnEl.dom[this.tooltipType] = this.tooltip;
12051             }
12052         }
12053         if(this.arrowTooltip){
12054             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12055         }
12056         if(this.hidden){
12057             this.hide();
12058         }
12059         if(this.disabled){
12060             this.disable();
12061         }
12062         if(this.pressed){
12063             this.el.addClass("x-btn-pressed");
12064         }
12065         if(Roo.isIE && !Roo.isIE7){
12066             this.autoWidth.defer(1, this);
12067         }else{
12068             this.autoWidth();
12069         }
12070         if(this.menu){
12071             this.menu.on("show", this.onMenuShow, this);
12072             this.menu.on("hide", this.onMenuHide, this);
12073         }
12074         this.fireEvent('render', this);
12075     },
12076
12077     // private
12078     autoWidth : function(){
12079         if(this.el){
12080             var tbl = this.el.child("table:first");
12081             var tbl2 = this.el.child("table:last");
12082             this.el.setWidth("auto");
12083             tbl.setWidth("auto");
12084             if(Roo.isIE7 && Roo.isStrict){
12085                 var ib = this.el.child('button:first');
12086                 if(ib && ib.getWidth() > 20){
12087                     ib.clip();
12088                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12089                 }
12090             }
12091             if(this.minWidth){
12092                 if(this.hidden){
12093                     this.el.beginMeasure();
12094                 }
12095                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12096                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12097                 }
12098                 if(this.hidden){
12099                     this.el.endMeasure();
12100                 }
12101             }
12102             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12103         } 
12104     },
12105     /**
12106      * Sets this button's click handler
12107      * @param {Function} handler The function to call when the button is clicked
12108      * @param {Object} scope (optional) Scope for the function passed above
12109      */
12110     setHandler : function(handler, scope){
12111         this.handler = handler;
12112         this.scope = scope;  
12113     },
12114     
12115     /**
12116      * Sets this button's arrow click handler
12117      * @param {Function} handler The function to call when the arrow is clicked
12118      * @param {Object} scope (optional) Scope for the function passed above
12119      */
12120     setArrowHandler : function(handler, scope){
12121         this.arrowHandler = handler;
12122         this.scope = scope;  
12123     },
12124     
12125     /**
12126      * Focus the button
12127      */
12128     focus : function(){
12129         if(this.el){
12130             this.el.child("button:first").focus();
12131         }
12132     },
12133
12134     // private
12135     onClick : function(e){
12136         e.preventDefault();
12137         if(!this.disabled){
12138             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12139                 if(this.menu && !this.menu.isVisible()){
12140                     this.menu.show(this.el, this.menuAlign);
12141                 }
12142                 this.fireEvent("arrowclick", this, e);
12143                 if(this.arrowHandler){
12144                     this.arrowHandler.call(this.scope || this, this, e);
12145                 }
12146             }else{
12147                 this.fireEvent("click", this, e);
12148                 if(this.handler){
12149                     this.handler.call(this.scope || this, this, e);
12150                 }
12151             }
12152         }
12153     },
12154     // private
12155     onMouseDown : function(e){
12156         if(!this.disabled){
12157             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12158         }
12159     },
12160     // private
12161     onMouseUp : function(e){
12162         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12163     }   
12164 });
12165
12166
12167 // backwards compat
12168 Roo.MenuButton = Roo.SplitButton;/*
12169  * Based on:
12170  * Ext JS Library 1.1.1
12171  * Copyright(c) 2006-2007, Ext JS, LLC.
12172  *
12173  * Originally Released Under LGPL - original licence link has changed is not relivant.
12174  *
12175  * Fork - LGPL
12176  * <script type="text/javascript">
12177  */
12178
12179 /**
12180  * @class Roo.Toolbar
12181  * Basic Toolbar class.
12182  * @constructor
12183  * Creates a new Toolbar
12184  * @param {Object} config The config object
12185  */ 
12186 Roo.Toolbar = function(container, buttons, config)
12187 {
12188     /// old consturctor format still supported..
12189     if(container instanceof Array){ // omit the container for later rendering
12190         buttons = container;
12191         config = buttons;
12192         container = null;
12193     }
12194     if (typeof(container) == 'object' && container.xtype) {
12195         config = container;
12196         container = config.container;
12197         buttons = config.buttons; // not really - use items!!
12198     }
12199     var xitems = [];
12200     if (config && config.items) {
12201         xitems = config.items;
12202         delete config.items;
12203     }
12204     Roo.apply(this, config);
12205     this.buttons = buttons;
12206     
12207     if(container){
12208         this.render(container);
12209     }
12210     Roo.each(xitems, function(b) {
12211         this.add(b);
12212     }, this);
12213     
12214 };
12215
12216 Roo.Toolbar.prototype = {
12217     /**
12218      * @cfg {Roo.data.Store} items
12219      * array of button configs or elements to add
12220      */
12221     
12222     /**
12223      * @cfg {String/HTMLElement/Element} container
12224      * The id or element that will contain the toolbar
12225      */
12226     // private
12227     render : function(ct){
12228         this.el = Roo.get(ct);
12229         if(this.cls){
12230             this.el.addClass(this.cls);
12231         }
12232         // using a table allows for vertical alignment
12233         // 100% width is needed by Safari...
12234         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12235         this.tr = this.el.child("tr", true);
12236         var autoId = 0;
12237         this.items = new Roo.util.MixedCollection(false, function(o){
12238             return o.id || ("item" + (++autoId));
12239         });
12240         if(this.buttons){
12241             this.add.apply(this, this.buttons);
12242             delete this.buttons;
12243         }
12244     },
12245
12246     /**
12247      * Adds element(s) to the toolbar -- this function takes a variable number of 
12248      * arguments of mixed type and adds them to the toolbar.
12249      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12250      * <ul>
12251      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12252      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12253      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12254      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12255      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12256      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12257      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12258      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12259      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12260      * </ul>
12261      * @param {Mixed} arg2
12262      * @param {Mixed} etc.
12263      */
12264     add : function(){
12265         var a = arguments, l = a.length;
12266         for(var i = 0; i < l; i++){
12267             this._add(a[i]);
12268         }
12269     },
12270     // private..
12271     _add : function(el) {
12272         
12273         if (el.xtype) {
12274             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12275         }
12276         
12277         if (el.applyTo){ // some kind of form field
12278             return this.addField(el);
12279         } 
12280         if (el.render){ // some kind of Toolbar.Item
12281             return this.addItem(el);
12282         }
12283         if (typeof el == "string"){ // string
12284             if(el == "separator" || el == "-"){
12285                 return this.addSeparator();
12286             }
12287             if (el == " "){
12288                 return this.addSpacer();
12289             }
12290             if(el == "->"){
12291                 return this.addFill();
12292             }
12293             return this.addText(el);
12294             
12295         }
12296         if(el.tagName){ // element
12297             return this.addElement(el);
12298         }
12299         if(typeof el == "object"){ // must be button config?
12300             return this.addButton(el);
12301         }
12302         // and now what?!?!
12303         return false;
12304         
12305     },
12306     
12307     /**
12308      * Add an Xtype element
12309      * @param {Object} xtype Xtype Object
12310      * @return {Object} created Object
12311      */
12312     addxtype : function(e){
12313         return this.add(e);  
12314     },
12315     
12316     /**
12317      * Returns the Element for this toolbar.
12318      * @return {Roo.Element}
12319      */
12320     getEl : function(){
12321         return this.el;  
12322     },
12323     
12324     /**
12325      * Adds a separator
12326      * @return {Roo.Toolbar.Item} The separator item
12327      */
12328     addSeparator : function(){
12329         return this.addItem(new Roo.Toolbar.Separator());
12330     },
12331
12332     /**
12333      * Adds a spacer element
12334      * @return {Roo.Toolbar.Spacer} The spacer item
12335      */
12336     addSpacer : function(){
12337         return this.addItem(new Roo.Toolbar.Spacer());
12338     },
12339
12340     /**
12341      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12342      * @return {Roo.Toolbar.Fill} The fill item
12343      */
12344     addFill : function(){
12345         return this.addItem(new Roo.Toolbar.Fill());
12346     },
12347
12348     /**
12349      * Adds any standard HTML element to the toolbar
12350      * @param {String/HTMLElement/Element} el The element or id of the element to add
12351      * @return {Roo.Toolbar.Item} The element's item
12352      */
12353     addElement : function(el){
12354         return this.addItem(new Roo.Toolbar.Item(el));
12355     },
12356     /**
12357      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12358      * @type Roo.util.MixedCollection  
12359      */
12360     items : false,
12361      
12362     /**
12363      * Adds any Toolbar.Item or subclass
12364      * @param {Roo.Toolbar.Item} item
12365      * @return {Roo.Toolbar.Item} The item
12366      */
12367     addItem : function(item){
12368         var td = this.nextBlock();
12369         item.render(td);
12370         this.items.add(item);
12371         return item;
12372     },
12373     
12374     /**
12375      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12376      * @param {Object/Array} config A button config or array of configs
12377      * @return {Roo.Toolbar.Button/Array}
12378      */
12379     addButton : function(config){
12380         if(config instanceof Array){
12381             var buttons = [];
12382             for(var i = 0, len = config.length; i < len; i++) {
12383                 buttons.push(this.addButton(config[i]));
12384             }
12385             return buttons;
12386         }
12387         var b = config;
12388         if(!(config instanceof Roo.Toolbar.Button)){
12389             b = config.split ?
12390                 new Roo.Toolbar.SplitButton(config) :
12391                 new Roo.Toolbar.Button(config);
12392         }
12393         var td = this.nextBlock();
12394         b.render(td);
12395         this.items.add(b);
12396         return b;
12397     },
12398     
12399     /**
12400      * Adds text to the toolbar
12401      * @param {String} text The text to add
12402      * @return {Roo.Toolbar.Item} The element's item
12403      */
12404     addText : function(text){
12405         return this.addItem(new Roo.Toolbar.TextItem(text));
12406     },
12407     
12408     /**
12409      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12410      * @param {Number} index The index where the item is to be inserted
12411      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12412      * @return {Roo.Toolbar.Button/Item}
12413      */
12414     insertButton : function(index, item){
12415         if(item instanceof Array){
12416             var buttons = [];
12417             for(var i = 0, len = item.length; i < len; i++) {
12418                buttons.push(this.insertButton(index + i, item[i]));
12419             }
12420             return buttons;
12421         }
12422         if (!(item instanceof Roo.Toolbar.Button)){
12423            item = new Roo.Toolbar.Button(item);
12424         }
12425         var td = document.createElement("td");
12426         this.tr.insertBefore(td, this.tr.childNodes[index]);
12427         item.render(td);
12428         this.items.insert(index, item);
12429         return item;
12430     },
12431     
12432     /**
12433      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12434      * @param {Object} config
12435      * @return {Roo.Toolbar.Item} The element's item
12436      */
12437     addDom : function(config, returnEl){
12438         var td = this.nextBlock();
12439         Roo.DomHelper.overwrite(td, config);
12440         var ti = new Roo.Toolbar.Item(td.firstChild);
12441         ti.render(td);
12442         this.items.add(ti);
12443         return ti;
12444     },
12445
12446     /**
12447      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12448      * @type Roo.util.MixedCollection  
12449      */
12450     fields : false,
12451     
12452     /**
12453      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12454      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12455      * @param {Roo.form.Field} field
12456      * @return {Roo.ToolbarItem}
12457      */
12458      
12459       
12460     addField : function(field) {
12461         if (!this.fields) {
12462             var autoId = 0;
12463             this.fields = new Roo.util.MixedCollection(false, function(o){
12464                 return o.id || ("item" + (++autoId));
12465             });
12466
12467         }
12468         
12469         var td = this.nextBlock();
12470         field.render(td);
12471         var ti = new Roo.Toolbar.Item(td.firstChild);
12472         ti.render(td);
12473         this.items.add(ti);
12474         this.fields.add(field);
12475         return ti;
12476     },
12477     /**
12478      * Hide the toolbar
12479      * @method hide
12480      */
12481      
12482       
12483     hide : function()
12484     {
12485         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12486         this.el.child('div').hide();
12487     },
12488     /**
12489      * Show the toolbar
12490      * @method show
12491      */
12492     show : function()
12493     {
12494         this.el.child('div').show();
12495     },
12496       
12497     // private
12498     nextBlock : function(){
12499         var td = document.createElement("td");
12500         this.tr.appendChild(td);
12501         return td;
12502     },
12503
12504     // private
12505     destroy : function(){
12506         if(this.items){ // rendered?
12507             Roo.destroy.apply(Roo, this.items.items);
12508         }
12509         if(this.fields){ // rendered?
12510             Roo.destroy.apply(Roo, this.fields.items);
12511         }
12512         Roo.Element.uncache(this.el, this.tr);
12513     }
12514 };
12515
12516 /**
12517  * @class Roo.Toolbar.Item
12518  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12519  * @constructor
12520  * Creates a new Item
12521  * @param {HTMLElement} el 
12522  */
12523 Roo.Toolbar.Item = function(el){
12524     this.el = Roo.getDom(el);
12525     this.id = Roo.id(this.el);
12526     this.hidden = false;
12527 };
12528
12529 Roo.Toolbar.Item.prototype = {
12530     
12531     /**
12532      * Get this item's HTML Element
12533      * @return {HTMLElement}
12534      */
12535     getEl : function(){
12536        return this.el;  
12537     },
12538
12539     // private
12540     render : function(td){
12541         this.td = td;
12542         td.appendChild(this.el);
12543     },
12544     
12545     /**
12546      * Removes and destroys this item.
12547      */
12548     destroy : function(){
12549         this.td.parentNode.removeChild(this.td);
12550     },
12551     
12552     /**
12553      * Shows this item.
12554      */
12555     show: function(){
12556         this.hidden = false;
12557         this.td.style.display = "";
12558     },
12559     
12560     /**
12561      * Hides this item.
12562      */
12563     hide: function(){
12564         this.hidden = true;
12565         this.td.style.display = "none";
12566     },
12567     
12568     /**
12569      * Convenience function for boolean show/hide.
12570      * @param {Boolean} visible true to show/false to hide
12571      */
12572     setVisible: function(visible){
12573         if(visible) {
12574             this.show();
12575         }else{
12576             this.hide();
12577         }
12578     },
12579     
12580     /**
12581      * Try to focus this item.
12582      */
12583     focus : function(){
12584         Roo.fly(this.el).focus();
12585     },
12586     
12587     /**
12588      * Disables this item.
12589      */
12590     disable : function(){
12591         Roo.fly(this.td).addClass("x-item-disabled");
12592         this.disabled = true;
12593         this.el.disabled = true;
12594     },
12595     
12596     /**
12597      * Enables this item.
12598      */
12599     enable : function(){
12600         Roo.fly(this.td).removeClass("x-item-disabled");
12601         this.disabled = false;
12602         this.el.disabled = false;
12603     }
12604 };
12605
12606
12607 /**
12608  * @class Roo.Toolbar.Separator
12609  * @extends Roo.Toolbar.Item
12610  * A simple toolbar separator class
12611  * @constructor
12612  * Creates a new Separator
12613  */
12614 Roo.Toolbar.Separator = function(){
12615     var s = document.createElement("span");
12616     s.className = "ytb-sep";
12617     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12618 };
12619 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12620     enable:Roo.emptyFn,
12621     disable:Roo.emptyFn,
12622     focus:Roo.emptyFn
12623 });
12624
12625 /**
12626  * @class Roo.Toolbar.Spacer
12627  * @extends Roo.Toolbar.Item
12628  * A simple element that adds extra horizontal space to a toolbar.
12629  * @constructor
12630  * Creates a new Spacer
12631  */
12632 Roo.Toolbar.Spacer = function(){
12633     var s = document.createElement("div");
12634     s.className = "ytb-spacer";
12635     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12636 };
12637 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12638     enable:Roo.emptyFn,
12639     disable:Roo.emptyFn,
12640     focus:Roo.emptyFn
12641 });
12642
12643 /**
12644  * @class Roo.Toolbar.Fill
12645  * @extends Roo.Toolbar.Spacer
12646  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12647  * @constructor
12648  * Creates a new Spacer
12649  */
12650 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12651     // private
12652     render : function(td){
12653         td.style.width = '100%';
12654         Roo.Toolbar.Fill.superclass.render.call(this, td);
12655     }
12656 });
12657
12658 /**
12659  * @class Roo.Toolbar.TextItem
12660  * @extends Roo.Toolbar.Item
12661  * A simple class that renders text directly into a toolbar.
12662  * @constructor
12663  * Creates a new TextItem
12664  * @param {String} text
12665  */
12666 Roo.Toolbar.TextItem = function(text){
12667     if (typeof(text) == 'object') {
12668         text = text.text;
12669     }
12670     var s = document.createElement("span");
12671     s.className = "ytb-text";
12672     s.innerHTML = text;
12673     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12674 };
12675 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12676     enable:Roo.emptyFn,
12677     disable:Roo.emptyFn,
12678     focus:Roo.emptyFn
12679 });
12680
12681 /**
12682  * @class Roo.Toolbar.Button
12683  * @extends Roo.Button
12684  * A button that renders into a toolbar.
12685  * @constructor
12686  * Creates a new Button
12687  * @param {Object} config A standard {@link Roo.Button} config object
12688  */
12689 Roo.Toolbar.Button = function(config){
12690     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12691 };
12692 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12693     render : function(td){
12694         this.td = td;
12695         Roo.Toolbar.Button.superclass.render.call(this, td);
12696     },
12697     
12698     /**
12699      * Removes and destroys this button
12700      */
12701     destroy : function(){
12702         Roo.Toolbar.Button.superclass.destroy.call(this);
12703         this.td.parentNode.removeChild(this.td);
12704     },
12705     
12706     /**
12707      * Shows this button
12708      */
12709     show: function(){
12710         this.hidden = false;
12711         this.td.style.display = "";
12712     },
12713     
12714     /**
12715      * Hides this button
12716      */
12717     hide: function(){
12718         this.hidden = true;
12719         this.td.style.display = "none";
12720     },
12721
12722     /**
12723      * Disables this item
12724      */
12725     disable : function(){
12726         Roo.fly(this.td).addClass("x-item-disabled");
12727         this.disabled = true;
12728     },
12729
12730     /**
12731      * Enables this item
12732      */
12733     enable : function(){
12734         Roo.fly(this.td).removeClass("x-item-disabled");
12735         this.disabled = false;
12736     }
12737 });
12738 // backwards compat
12739 Roo.ToolbarButton = Roo.Toolbar.Button;
12740
12741 /**
12742  * @class Roo.Toolbar.SplitButton
12743  * @extends Roo.SplitButton
12744  * A menu button that renders into a toolbar.
12745  * @constructor
12746  * Creates a new SplitButton
12747  * @param {Object} config A standard {@link Roo.SplitButton} config object
12748  */
12749 Roo.Toolbar.SplitButton = function(config){
12750     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12751 };
12752 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12753     render : function(td){
12754         this.td = td;
12755         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12756     },
12757     
12758     /**
12759      * Removes and destroys this button
12760      */
12761     destroy : function(){
12762         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12763         this.td.parentNode.removeChild(this.td);
12764     },
12765     
12766     /**
12767      * Shows this button
12768      */
12769     show: function(){
12770         this.hidden = false;
12771         this.td.style.display = "";
12772     },
12773     
12774     /**
12775      * Hides this button
12776      */
12777     hide: function(){
12778         this.hidden = true;
12779         this.td.style.display = "none";
12780     }
12781 });
12782
12783 // backwards compat
12784 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12785  * Based on:
12786  * Ext JS Library 1.1.1
12787  * Copyright(c) 2006-2007, Ext JS, LLC.
12788  *
12789  * Originally Released Under LGPL - original licence link has changed is not relivant.
12790  *
12791  * Fork - LGPL
12792  * <script type="text/javascript">
12793  */
12794  
12795 /**
12796  * @class Roo.PagingToolbar
12797  * @extends Roo.Toolbar
12798  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12799  * @constructor
12800  * Create a new PagingToolbar
12801  * @param {Object} config The config object
12802  */
12803 Roo.PagingToolbar = function(el, ds, config)
12804 {
12805     // old args format still supported... - xtype is prefered..
12806     if (typeof(el) == 'object' && el.xtype) {
12807         // created from xtype...
12808         config = el;
12809         ds = el.dataSource;
12810         el = config.container;
12811     }
12812     var items = [];
12813     if (config.items) {
12814         items = config.items;
12815         config.items = [];
12816     }
12817     
12818     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12819     this.ds = ds;
12820     this.cursor = 0;
12821     this.renderButtons(this.el);
12822     this.bind(ds);
12823     
12824     // supprot items array.
12825    
12826     Roo.each(items, function(e) {
12827         this.add(Roo.factory(e));
12828     },this);
12829     
12830 };
12831
12832 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12833     /**
12834      * @cfg {Roo.data.Store} dataSource
12835      * The underlying data store providing the paged data
12836      */
12837     /**
12838      * @cfg {String/HTMLElement/Element} container
12839      * container The id or element that will contain the toolbar
12840      */
12841     /**
12842      * @cfg {Boolean} displayInfo
12843      * True to display the displayMsg (defaults to false)
12844      */
12845     /**
12846      * @cfg {Number} pageSize
12847      * The number of records to display per page (defaults to 20)
12848      */
12849     pageSize: 20,
12850     /**
12851      * @cfg {String} displayMsg
12852      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12853      */
12854     displayMsg : 'Displaying {0} - {1} of {2}',
12855     /**
12856      * @cfg {String} emptyMsg
12857      * The message to display when no records are found (defaults to "No data to display")
12858      */
12859     emptyMsg : 'No data to display',
12860     /**
12861      * Customizable piece of the default paging text (defaults to "Page")
12862      * @type String
12863      */
12864     beforePageText : "Page",
12865     /**
12866      * Customizable piece of the default paging text (defaults to "of %0")
12867      * @type String
12868      */
12869     afterPageText : "of {0}",
12870     /**
12871      * Customizable piece of the default paging text (defaults to "First Page")
12872      * @type String
12873      */
12874     firstText : "First Page",
12875     /**
12876      * Customizable piece of the default paging text (defaults to "Previous Page")
12877      * @type String
12878      */
12879     prevText : "Previous Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "Next Page")
12882      * @type String
12883      */
12884     nextText : "Next Page",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "Last Page")
12887      * @type String
12888      */
12889     lastText : "Last Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Refresh")
12892      * @type String
12893      */
12894     refreshText : "Refresh",
12895
12896     // private
12897     renderButtons : function(el){
12898         Roo.PagingToolbar.superclass.render.call(this, el);
12899         this.first = this.addButton({
12900             tooltip: this.firstText,
12901             cls: "x-btn-icon x-grid-page-first",
12902             disabled: true,
12903             handler: this.onClick.createDelegate(this, ["first"])
12904         });
12905         this.prev = this.addButton({
12906             tooltip: this.prevText,
12907             cls: "x-btn-icon x-grid-page-prev",
12908             disabled: true,
12909             handler: this.onClick.createDelegate(this, ["prev"])
12910         });
12911         //this.addSeparator();
12912         this.add(this.beforePageText);
12913         this.field = Roo.get(this.addDom({
12914            tag: "input",
12915            type: "text",
12916            size: "3",
12917            value: "1",
12918            cls: "x-grid-page-number"
12919         }).el);
12920         this.field.on("keydown", this.onPagingKeydown, this);
12921         this.field.on("focus", function(){this.dom.select();});
12922         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12923         this.field.setHeight(18);
12924         //this.addSeparator();
12925         this.next = this.addButton({
12926             tooltip: this.nextText,
12927             cls: "x-btn-icon x-grid-page-next",
12928             disabled: true,
12929             handler: this.onClick.createDelegate(this, ["next"])
12930         });
12931         this.last = this.addButton({
12932             tooltip: this.lastText,
12933             cls: "x-btn-icon x-grid-page-last",
12934             disabled: true,
12935             handler: this.onClick.createDelegate(this, ["last"])
12936         });
12937         //this.addSeparator();
12938         this.loading = this.addButton({
12939             tooltip: this.refreshText,
12940             cls: "x-btn-icon x-grid-loading",
12941             handler: this.onClick.createDelegate(this, ["refresh"])
12942         });
12943
12944         if(this.displayInfo){
12945             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12946         }
12947     },
12948
12949     // private
12950     updateInfo : function(){
12951         if(this.displayEl){
12952             var count = this.ds.getCount();
12953             var msg = count == 0 ?
12954                 this.emptyMsg :
12955                 String.format(
12956                     this.displayMsg,
12957                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12958                 );
12959             this.displayEl.update(msg);
12960         }
12961     },
12962
12963     // private
12964     onLoad : function(ds, r, o){
12965        this.cursor = o.params ? o.params.start : 0;
12966        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12967
12968        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12969        this.field.dom.value = ap;
12970        this.first.setDisabled(ap == 1);
12971        this.prev.setDisabled(ap == 1);
12972        this.next.setDisabled(ap == ps);
12973        this.last.setDisabled(ap == ps);
12974        this.loading.enable();
12975        this.updateInfo();
12976     },
12977
12978     // private
12979     getPageData : function(){
12980         var total = this.ds.getTotalCount();
12981         return {
12982             total : total,
12983             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12984             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12985         };
12986     },
12987
12988     // private
12989     onLoadError : function(){
12990         this.loading.enable();
12991     },
12992
12993     // private
12994     onPagingKeydown : function(e){
12995         var k = e.getKey();
12996         var d = this.getPageData();
12997         if(k == e.RETURN){
12998             var v = this.field.dom.value, pageNum;
12999             if(!v || isNaN(pageNum = parseInt(v, 10))){
13000                 this.field.dom.value = d.activePage;
13001                 return;
13002             }
13003             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13004             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13005             e.stopEvent();
13006         }
13007         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))
13008         {
13009           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13010           this.field.dom.value = pageNum;
13011           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13012           e.stopEvent();
13013         }
13014         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13015         {
13016           var v = this.field.dom.value, pageNum; 
13017           var increment = (e.shiftKey) ? 10 : 1;
13018           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13019             increment *= -1;
13020           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13021             this.field.dom.value = d.activePage;
13022             return;
13023           }
13024           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13025           {
13026             this.field.dom.value = parseInt(v, 10) + increment;
13027             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13028             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13029           }
13030           e.stopEvent();
13031         }
13032     },
13033
13034     // private
13035     beforeLoad : function(){
13036         if(this.loading){
13037             this.loading.disable();
13038         }
13039     },
13040
13041     // private
13042     onClick : function(which){
13043         var ds = this.ds;
13044         switch(which){
13045             case "first":
13046                 ds.load({params:{start: 0, limit: this.pageSize}});
13047             break;
13048             case "prev":
13049                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13050             break;
13051             case "next":
13052                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13053             break;
13054             case "last":
13055                 var total = ds.getTotalCount();
13056                 var extra = total % this.pageSize;
13057                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13058                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13059             break;
13060             case "refresh":
13061                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13062             break;
13063         }
13064     },
13065
13066     /**
13067      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13068      * @param {Roo.data.Store} store The data store to unbind
13069      */
13070     unbind : function(ds){
13071         ds.un("beforeload", this.beforeLoad, this);
13072         ds.un("load", this.onLoad, this);
13073         ds.un("loadexception", this.onLoadError, this);
13074         ds.un("remove", this.updateInfo, this);
13075         ds.un("add", this.updateInfo, this);
13076         this.ds = undefined;
13077     },
13078
13079     /**
13080      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13081      * @param {Roo.data.Store} store The data store to bind
13082      */
13083     bind : function(ds){
13084         ds.on("beforeload", this.beforeLoad, this);
13085         ds.on("load", this.onLoad, this);
13086         ds.on("loadexception", this.onLoadError, this);
13087         ds.on("remove", this.updateInfo, this);
13088         ds.on("add", this.updateInfo, this);
13089         this.ds = ds;
13090     }
13091 });/*
13092  * Based on:
13093  * Ext JS Library 1.1.1
13094  * Copyright(c) 2006-2007, Ext JS, LLC.
13095  *
13096  * Originally Released Under LGPL - original licence link has changed is not relivant.
13097  *
13098  * Fork - LGPL
13099  * <script type="text/javascript">
13100  */
13101
13102 /**
13103  * @class Roo.Resizable
13104  * @extends Roo.util.Observable
13105  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13106  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13107  * 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
13108  * the element will be wrapped for you automatically.</p>
13109  * <p>Here is the list of valid resize handles:</p>
13110  * <pre>
13111 Value   Description
13112 ------  -------------------
13113  'n'     north
13114  's'     south
13115  'e'     east
13116  'w'     west
13117  'nw'    northwest
13118  'sw'    southwest
13119  'se'    southeast
13120  'ne'    northeast
13121  'hd'    horizontal drag
13122  'all'   all
13123 </pre>
13124  * <p>Here's an example showing the creation of a typical Resizable:</p>
13125  * <pre><code>
13126 var resizer = new Roo.Resizable("element-id", {
13127     handles: 'all',
13128     minWidth: 200,
13129     minHeight: 100,
13130     maxWidth: 500,
13131     maxHeight: 400,
13132     pinned: true
13133 });
13134 resizer.on("resize", myHandler);
13135 </code></pre>
13136  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13137  * resizer.east.setDisplayed(false);</p>
13138  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13139  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13140  * resize operation's new size (defaults to [0, 0])
13141  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13142  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13143  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13144  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13145  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13146  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13147  * @cfg {Number} width The width of the element in pixels (defaults to null)
13148  * @cfg {Number} height The height of the element in pixels (defaults to null)
13149  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13150  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13151  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13152  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13153  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13154  * in favor of the handles config option (defaults to false)
13155  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13156  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13157  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13158  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13159  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13160  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13161  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13162  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13163  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13164  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13165  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13166  * @constructor
13167  * Create a new resizable component
13168  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13169  * @param {Object} config configuration options
13170   */
13171 Roo.Resizable = function(el, config)
13172 {
13173     this.el = Roo.get(el);
13174
13175     if(config && config.wrap){
13176         config.resizeChild = this.el;
13177         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13178         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13179         this.el.setStyle("overflow", "hidden");
13180         this.el.setPositioning(config.resizeChild.getPositioning());
13181         config.resizeChild.clearPositioning();
13182         if(!config.width || !config.height){
13183             var csize = config.resizeChild.getSize();
13184             this.el.setSize(csize.width, csize.height);
13185         }
13186         if(config.pinned && !config.adjustments){
13187             config.adjustments = "auto";
13188         }
13189     }
13190
13191     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13192     this.proxy.unselectable();
13193     this.proxy.enableDisplayMode('block');
13194
13195     Roo.apply(this, config);
13196
13197     if(this.pinned){
13198         this.disableTrackOver = true;
13199         this.el.addClass("x-resizable-pinned");
13200     }
13201     // if the element isn't positioned, make it relative
13202     var position = this.el.getStyle("position");
13203     if(position != "absolute" && position != "fixed"){
13204         this.el.setStyle("position", "relative");
13205     }
13206     if(!this.handles){ // no handles passed, must be legacy style
13207         this.handles = 's,e,se';
13208         if(this.multiDirectional){
13209             this.handles += ',n,w';
13210         }
13211     }
13212     if(this.handles == "all"){
13213         this.handles = "n s e w ne nw se sw";
13214     }
13215     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13216     var ps = Roo.Resizable.positions;
13217     for(var i = 0, len = hs.length; i < len; i++){
13218         if(hs[i] && ps[hs[i]]){
13219             var pos = ps[hs[i]];
13220             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13221         }
13222     }
13223     // legacy
13224     this.corner = this.southeast;
13225     
13226     // updateBox = the box can move..
13227     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13228         this.updateBox = true;
13229     }
13230
13231     this.activeHandle = null;
13232
13233     if(this.resizeChild){
13234         if(typeof this.resizeChild == "boolean"){
13235             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13236         }else{
13237             this.resizeChild = Roo.get(this.resizeChild, true);
13238         }
13239     }
13240     
13241     if(this.adjustments == "auto"){
13242         var rc = this.resizeChild;
13243         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13244         if(rc && (hw || hn)){
13245             rc.position("relative");
13246             rc.setLeft(hw ? hw.el.getWidth() : 0);
13247             rc.setTop(hn ? hn.el.getHeight() : 0);
13248         }
13249         this.adjustments = [
13250             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13251             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13252         ];
13253     }
13254
13255     if(this.draggable){
13256         this.dd = this.dynamic ?
13257             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13258         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13259     }
13260
13261     // public events
13262     this.addEvents({
13263         /**
13264          * @event beforeresize
13265          * Fired before resize is allowed. Set enabled to false to cancel resize.
13266          * @param {Roo.Resizable} this
13267          * @param {Roo.EventObject} e The mousedown event
13268          */
13269         "beforeresize" : true,
13270         /**
13271          * @event resize
13272          * Fired after a resize.
13273          * @param {Roo.Resizable} this
13274          * @param {Number} width The new width
13275          * @param {Number} height The new height
13276          * @param {Roo.EventObject} e The mouseup event
13277          */
13278         "resize" : true
13279     });
13280
13281     if(this.width !== null && this.height !== null){
13282         this.resizeTo(this.width, this.height);
13283     }else{
13284         this.updateChildSize();
13285     }
13286     if(Roo.isIE){
13287         this.el.dom.style.zoom = 1;
13288     }
13289     Roo.Resizable.superclass.constructor.call(this);
13290 };
13291
13292 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13293         resizeChild : false,
13294         adjustments : [0, 0],
13295         minWidth : 5,
13296         minHeight : 5,
13297         maxWidth : 10000,
13298         maxHeight : 10000,
13299         enabled : true,
13300         animate : false,
13301         duration : .35,
13302         dynamic : false,
13303         handles : false,
13304         multiDirectional : false,
13305         disableTrackOver : false,
13306         easing : 'easeOutStrong',
13307         widthIncrement : 0,
13308         heightIncrement : 0,
13309         pinned : false,
13310         width : null,
13311         height : null,
13312         preserveRatio : false,
13313         transparent: false,
13314         minX: 0,
13315         minY: 0,
13316         draggable: false,
13317
13318         /**
13319          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13320          */
13321         constrainTo: undefined,
13322         /**
13323          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13324          */
13325         resizeRegion: undefined,
13326
13327
13328     /**
13329      * Perform a manual resize
13330      * @param {Number} width
13331      * @param {Number} height
13332      */
13333     resizeTo : function(width, height){
13334         this.el.setSize(width, height);
13335         this.updateChildSize();
13336         this.fireEvent("resize", this, width, height, null);
13337     },
13338
13339     // private
13340     startSizing : function(e, handle){
13341         this.fireEvent("beforeresize", this, e);
13342         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13343
13344             if(!this.overlay){
13345                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13346                 this.overlay.unselectable();
13347                 this.overlay.enableDisplayMode("block");
13348                 this.overlay.on("mousemove", this.onMouseMove, this);
13349                 this.overlay.on("mouseup", this.onMouseUp, this);
13350             }
13351             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13352
13353             this.resizing = true;
13354             this.startBox = this.el.getBox();
13355             this.startPoint = e.getXY();
13356             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13357                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13358
13359             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13360             this.overlay.show();
13361
13362             if(this.constrainTo) {
13363                 var ct = Roo.get(this.constrainTo);
13364                 this.resizeRegion = ct.getRegion().adjust(
13365                     ct.getFrameWidth('t'),
13366                     ct.getFrameWidth('l'),
13367                     -ct.getFrameWidth('b'),
13368                     -ct.getFrameWidth('r')
13369                 );
13370             }
13371
13372             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13373             this.proxy.show();
13374             this.proxy.setBox(this.startBox);
13375             if(!this.dynamic){
13376                 this.proxy.setStyle('visibility', 'visible');
13377             }
13378         }
13379     },
13380
13381     // private
13382     onMouseDown : function(handle, e){
13383         if(this.enabled){
13384             e.stopEvent();
13385             this.activeHandle = handle;
13386             this.startSizing(e, handle);
13387         }
13388     },
13389
13390     // private
13391     onMouseUp : function(e){
13392         var size = this.resizeElement();
13393         this.resizing = false;
13394         this.handleOut();
13395         this.overlay.hide();
13396         this.proxy.hide();
13397         this.fireEvent("resize", this, size.width, size.height, e);
13398     },
13399
13400     // private
13401     updateChildSize : function(){
13402         if(this.resizeChild){
13403             var el = this.el;
13404             var child = this.resizeChild;
13405             var adj = this.adjustments;
13406             if(el.dom.offsetWidth){
13407                 var b = el.getSize(true);
13408                 child.setSize(b.width+adj[0], b.height+adj[1]);
13409             }
13410             // Second call here for IE
13411             // The first call enables instant resizing and
13412             // the second call corrects scroll bars if they
13413             // exist
13414             if(Roo.isIE){
13415                 setTimeout(function(){
13416                     if(el.dom.offsetWidth){
13417                         var b = el.getSize(true);
13418                         child.setSize(b.width+adj[0], b.height+adj[1]);
13419                     }
13420                 }, 10);
13421             }
13422         }
13423     },
13424
13425     // private
13426     snap : function(value, inc, min){
13427         if(!inc || !value) return value;
13428         var newValue = value;
13429         var m = value % inc;
13430         if(m > 0){
13431             if(m > (inc/2)){
13432                 newValue = value + (inc-m);
13433             }else{
13434                 newValue = value - m;
13435             }
13436         }
13437         return Math.max(min, newValue);
13438     },
13439
13440     // private
13441     resizeElement : function(){
13442         var box = this.proxy.getBox();
13443         if(this.updateBox){
13444             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13445         }else{
13446             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13447         }
13448         this.updateChildSize();
13449         if(!this.dynamic){
13450             this.proxy.hide();
13451         }
13452         return box;
13453     },
13454
13455     // private
13456     constrain : function(v, diff, m, mx){
13457         if(v - diff < m){
13458             diff = v - m;
13459         }else if(v - diff > mx){
13460             diff = mx - v;
13461         }
13462         return diff;
13463     },
13464
13465     // private
13466     onMouseMove : function(e){
13467         if(this.enabled){
13468             try{// try catch so if something goes wrong the user doesn't get hung
13469
13470             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13471                 return;
13472             }
13473
13474             //var curXY = this.startPoint;
13475             var curSize = this.curSize || this.startBox;
13476             var x = this.startBox.x, y = this.startBox.y;
13477             var ox = x, oy = y;
13478             var w = curSize.width, h = curSize.height;
13479             var ow = w, oh = h;
13480             var mw = this.minWidth, mh = this.minHeight;
13481             var mxw = this.maxWidth, mxh = this.maxHeight;
13482             var wi = this.widthIncrement;
13483             var hi = this.heightIncrement;
13484
13485             var eventXY = e.getXY();
13486             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13487             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13488
13489             var pos = this.activeHandle.position;
13490
13491             switch(pos){
13492                 case "east":
13493                     w += diffX;
13494                     w = Math.min(Math.max(mw, w), mxw);
13495                     break;
13496              
13497                 case "south":
13498                     h += diffY;
13499                     h = Math.min(Math.max(mh, h), mxh);
13500                     break;
13501                 case "southeast":
13502                     w += diffX;
13503                     h += diffY;
13504                     w = Math.min(Math.max(mw, w), mxw);
13505                     h = Math.min(Math.max(mh, h), mxh);
13506                     break;
13507                 case "north":
13508                     diffY = this.constrain(h, diffY, mh, mxh);
13509                     y += diffY;
13510                     h -= diffY;
13511                     break;
13512                 case "hdrag":
13513                     
13514                     if (wi) {
13515                         var adiffX = Math.abs(diffX);
13516                         var sub = (adiffX % wi); // how much 
13517                         if (sub > (wi/2)) { // far enough to snap
13518                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13519                         } else {
13520                             // remove difference.. 
13521                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13522                         }
13523                     }
13524                     x += diffX;
13525                     x = Math.max(this.minX, x);
13526                     break;
13527                 case "west":
13528                     diffX = this.constrain(w, diffX, mw, mxw);
13529                     x += diffX;
13530                     w -= diffX;
13531                     break;
13532                 case "northeast":
13533                     w += diffX;
13534                     w = Math.min(Math.max(mw, w), mxw);
13535                     diffY = this.constrain(h, diffY, mh, mxh);
13536                     y += diffY;
13537                     h -= diffY;
13538                     break;
13539                 case "northwest":
13540                     diffX = this.constrain(w, diffX, mw, mxw);
13541                     diffY = this.constrain(h, diffY, mh, mxh);
13542                     y += diffY;
13543                     h -= diffY;
13544                     x += diffX;
13545                     w -= diffX;
13546                     break;
13547                case "southwest":
13548                     diffX = this.constrain(w, diffX, mw, mxw);
13549                     h += diffY;
13550                     h = Math.min(Math.max(mh, h), mxh);
13551                     x += diffX;
13552                     w -= diffX;
13553                     break;
13554             }
13555
13556             var sw = this.snap(w, wi, mw);
13557             var sh = this.snap(h, hi, mh);
13558             if(sw != w || sh != h){
13559                 switch(pos){
13560                     case "northeast":
13561                         y -= sh - h;
13562                     break;
13563                     case "north":
13564                         y -= sh - h;
13565                         break;
13566                     case "southwest":
13567                         x -= sw - w;
13568                     break;
13569                     case "west":
13570                         x -= sw - w;
13571                         break;
13572                     case "northwest":
13573                         x -= sw - w;
13574                         y -= sh - h;
13575                     break;
13576                 }
13577                 w = sw;
13578                 h = sh;
13579             }
13580
13581             if(this.preserveRatio){
13582                 switch(pos){
13583                     case "southeast":
13584                     case "east":
13585                         h = oh * (w/ow);
13586                         h = Math.min(Math.max(mh, h), mxh);
13587                         w = ow * (h/oh);
13588                        break;
13589                     case "south":
13590                         w = ow * (h/oh);
13591                         w = Math.min(Math.max(mw, w), mxw);
13592                         h = oh * (w/ow);
13593                         break;
13594                     case "northeast":
13595                         w = ow * (h/oh);
13596                         w = Math.min(Math.max(mw, w), mxw);
13597                         h = oh * (w/ow);
13598                     break;
13599                     case "north":
13600                         var tw = w;
13601                         w = ow * (h/oh);
13602                         w = Math.min(Math.max(mw, w), mxw);
13603                         h = oh * (w/ow);
13604                         x += (tw - w) / 2;
13605                         break;
13606                     case "southwest":
13607                         h = oh * (w/ow);
13608                         h = Math.min(Math.max(mh, h), mxh);
13609                         var tw = w;
13610                         w = ow * (h/oh);
13611                         x += tw - w;
13612                         break;
13613                     case "west":
13614                         var th = h;
13615                         h = oh * (w/ow);
13616                         h = Math.min(Math.max(mh, h), mxh);
13617                         y += (th - h) / 2;
13618                         var tw = w;
13619                         w = ow * (h/oh);
13620                         x += tw - w;
13621                        break;
13622                     case "northwest":
13623                         var tw = w;
13624                         var th = h;
13625                         h = oh * (w/ow);
13626                         h = Math.min(Math.max(mh, h), mxh);
13627                         w = ow * (h/oh);
13628                         y += th - h;
13629                         x += tw - w;
13630                        break;
13631
13632                 }
13633             }
13634             if (pos == 'hdrag') {
13635                 w = ow;
13636             }
13637             this.proxy.setBounds(x, y, w, h);
13638             if(this.dynamic){
13639                 this.resizeElement();
13640             }
13641             }catch(e){}
13642         }
13643     },
13644
13645     // private
13646     handleOver : function(){
13647         if(this.enabled){
13648             this.el.addClass("x-resizable-over");
13649         }
13650     },
13651
13652     // private
13653     handleOut : function(){
13654         if(!this.resizing){
13655             this.el.removeClass("x-resizable-over");
13656         }
13657     },
13658
13659     /**
13660      * Returns the element this component is bound to.
13661      * @return {Roo.Element}
13662      */
13663     getEl : function(){
13664         return this.el;
13665     },
13666
13667     /**
13668      * Returns the resizeChild element (or null).
13669      * @return {Roo.Element}
13670      */
13671     getResizeChild : function(){
13672         return this.resizeChild;
13673     },
13674
13675     /**
13676      * Destroys this resizable. If the element was wrapped and
13677      * removeEl is not true then the element remains.
13678      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13679      */
13680     destroy : function(removeEl){
13681         this.proxy.remove();
13682         if(this.overlay){
13683             this.overlay.removeAllListeners();
13684             this.overlay.remove();
13685         }
13686         var ps = Roo.Resizable.positions;
13687         for(var k in ps){
13688             if(typeof ps[k] != "function" && this[ps[k]]){
13689                 var h = this[ps[k]];
13690                 h.el.removeAllListeners();
13691                 h.el.remove();
13692             }
13693         }
13694         if(removeEl){
13695             this.el.update("");
13696             this.el.remove();
13697         }
13698     }
13699 });
13700
13701 // private
13702 // hash to map config positions to true positions
13703 Roo.Resizable.positions = {
13704     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13705     hd: "hdrag"
13706 };
13707
13708 // private
13709 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13710     if(!this.tpl){
13711         // only initialize the template if resizable is used
13712         var tpl = Roo.DomHelper.createTemplate(
13713             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13714         );
13715         tpl.compile();
13716         Roo.Resizable.Handle.prototype.tpl = tpl;
13717     }
13718     this.position = pos;
13719     this.rz = rz;
13720     // show north drag fro topdra
13721     var handlepos = pos == 'hdrag' ? 'north' : pos;
13722     
13723     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13724     if (pos == 'hdrag') {
13725         this.el.setStyle('cursor', 'pointer');
13726     }
13727     this.el.unselectable();
13728     if(transparent){
13729         this.el.setOpacity(0);
13730     }
13731     this.el.on("mousedown", this.onMouseDown, this);
13732     if(!disableTrackOver){
13733         this.el.on("mouseover", this.onMouseOver, this);
13734         this.el.on("mouseout", this.onMouseOut, this);
13735     }
13736 };
13737
13738 // private
13739 Roo.Resizable.Handle.prototype = {
13740     afterResize : function(rz){
13741         // do nothing
13742     },
13743     // private
13744     onMouseDown : function(e){
13745         this.rz.onMouseDown(this, e);
13746     },
13747     // private
13748     onMouseOver : function(e){
13749         this.rz.handleOver(this, e);
13750     },
13751     // private
13752     onMouseOut : function(e){
13753         this.rz.handleOut(this, e);
13754     }
13755 };/*
13756  * Based on:
13757  * Ext JS Library 1.1.1
13758  * Copyright(c) 2006-2007, Ext JS, LLC.
13759  *
13760  * Originally Released Under LGPL - original licence link has changed is not relivant.
13761  *
13762  * Fork - LGPL
13763  * <script type="text/javascript">
13764  */
13765
13766 /**
13767  * @class Roo.Editor
13768  * @extends Roo.Component
13769  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13770  * @constructor
13771  * Create a new Editor
13772  * @param {Roo.form.Field} field The Field object (or descendant)
13773  * @param {Object} config The config object
13774  */
13775 Roo.Editor = function(field, config){
13776     Roo.Editor.superclass.constructor.call(this, config);
13777     this.field = field;
13778     this.addEvents({
13779         /**
13780              * @event beforestartedit
13781              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13782              * false from the handler of this event.
13783              * @param {Editor} this
13784              * @param {Roo.Element} boundEl The underlying element bound to this editor
13785              * @param {Mixed} value The field value being set
13786              */
13787         "beforestartedit" : true,
13788         /**
13789              * @event startedit
13790              * Fires when this editor is displayed
13791              * @param {Roo.Element} boundEl The underlying element bound to this editor
13792              * @param {Mixed} value The starting field value
13793              */
13794         "startedit" : true,
13795         /**
13796              * @event beforecomplete
13797              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13798              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13799              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13800              * event will not fire since no edit actually occurred.
13801              * @param {Editor} this
13802              * @param {Mixed} value The current field value
13803              * @param {Mixed} startValue The original field value
13804              */
13805         "beforecomplete" : true,
13806         /**
13807              * @event complete
13808              * Fires after editing is complete and any changed value has been written to the underlying field.
13809              * @param {Editor} this
13810              * @param {Mixed} value The current field value
13811              * @param {Mixed} startValue The original field value
13812              */
13813         "complete" : true,
13814         /**
13815          * @event specialkey
13816          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13817          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13818          * @param {Roo.form.Field} this
13819          * @param {Roo.EventObject} e The event object
13820          */
13821         "specialkey" : true
13822     });
13823 };
13824
13825 Roo.extend(Roo.Editor, Roo.Component, {
13826     /**
13827      * @cfg {Boolean/String} autosize
13828      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13829      * or "height" to adopt the height only (defaults to false)
13830      */
13831     /**
13832      * @cfg {Boolean} revertInvalid
13833      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13834      * validation fails (defaults to true)
13835      */
13836     /**
13837      * @cfg {Boolean} ignoreNoChange
13838      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13839      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13840      * will never be ignored.
13841      */
13842     /**
13843      * @cfg {Boolean} hideEl
13844      * False to keep the bound element visible while the editor is displayed (defaults to true)
13845      */
13846     /**
13847      * @cfg {Mixed} value
13848      * The data value of the underlying field (defaults to "")
13849      */
13850     value : "",
13851     /**
13852      * @cfg {String} alignment
13853      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13854      */
13855     alignment: "c-c?",
13856     /**
13857      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13858      * for bottom-right shadow (defaults to "frame")
13859      */
13860     shadow : "frame",
13861     /**
13862      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13863      */
13864     constrain : false,
13865     /**
13866      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13867      */
13868     completeOnEnter : false,
13869     /**
13870      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13871      */
13872     cancelOnEsc : false,
13873     /**
13874      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13875      */
13876     updateEl : false,
13877
13878     // private
13879     onRender : function(ct, position){
13880         this.el = new Roo.Layer({
13881             shadow: this.shadow,
13882             cls: "x-editor",
13883             parentEl : ct,
13884             shim : this.shim,
13885             shadowOffset:4,
13886             id: this.id,
13887             constrain: this.constrain
13888         });
13889         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13890         if(this.field.msgTarget != 'title'){
13891             this.field.msgTarget = 'qtip';
13892         }
13893         this.field.render(this.el);
13894         if(Roo.isGecko){
13895             this.field.el.dom.setAttribute('autocomplete', 'off');
13896         }
13897         this.field.on("specialkey", this.onSpecialKey, this);
13898         if(this.swallowKeys){
13899             this.field.el.swallowEvent(['keydown','keypress']);
13900         }
13901         this.field.show();
13902         this.field.on("blur", this.onBlur, this);
13903         if(this.field.grow){
13904             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13905         }
13906     },
13907
13908     onSpecialKey : function(field, e){
13909         //Roo.log('editor onSpecialKey');
13910         if(this.completeOnEnter && e.getKey() == e.ENTER){
13911             e.stopEvent();
13912             this.completeEdit();
13913         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13914             this.cancelEdit();
13915         }else{
13916             this.fireEvent('specialkey', field, e);
13917         }
13918     },
13919
13920     /**
13921      * Starts the editing process and shows the editor.
13922      * @param {String/HTMLElement/Element} el The element to edit
13923      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13924       * to the innerHTML of el.
13925      */
13926     startEdit : function(el, value){
13927         if(this.editing){
13928             this.completeEdit();
13929         }
13930         this.boundEl = Roo.get(el);
13931         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13932         if(!this.rendered){
13933             this.render(this.parentEl || document.body);
13934         }
13935         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13936             return;
13937         }
13938         this.startValue = v;
13939         this.field.setValue(v);
13940         if(this.autoSize){
13941             var sz = this.boundEl.getSize();
13942             switch(this.autoSize){
13943                 case "width":
13944                 this.setSize(sz.width,  "");
13945                 break;
13946                 case "height":
13947                 this.setSize("",  sz.height);
13948                 break;
13949                 default:
13950                 this.setSize(sz.width,  sz.height);
13951             }
13952         }
13953         this.el.alignTo(this.boundEl, this.alignment);
13954         this.editing = true;
13955         if(Roo.QuickTips){
13956             Roo.QuickTips.disable();
13957         }
13958         this.show();
13959     },
13960
13961     /**
13962      * Sets the height and width of this editor.
13963      * @param {Number} width The new width
13964      * @param {Number} height The new height
13965      */
13966     setSize : function(w, h){
13967         this.field.setSize(w, h);
13968         if(this.el){
13969             this.el.sync();
13970         }
13971     },
13972
13973     /**
13974      * Realigns the editor to the bound field based on the current alignment config value.
13975      */
13976     realign : function(){
13977         this.el.alignTo(this.boundEl, this.alignment);
13978     },
13979
13980     /**
13981      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13982      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13983      */
13984     completeEdit : function(remainVisible){
13985         if(!this.editing){
13986             return;
13987         }
13988         var v = this.getValue();
13989         if(this.revertInvalid !== false && !this.field.isValid()){
13990             v = this.startValue;
13991             this.cancelEdit(true);
13992         }
13993         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13994             this.editing = false;
13995             this.hide();
13996             return;
13997         }
13998         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13999             this.editing = false;
14000             if(this.updateEl && this.boundEl){
14001                 this.boundEl.update(v);
14002             }
14003             if(remainVisible !== true){
14004                 this.hide();
14005             }
14006             this.fireEvent("complete", this, v, this.startValue);
14007         }
14008     },
14009
14010     // private
14011     onShow : function(){
14012         this.el.show();
14013         if(this.hideEl !== false){
14014             this.boundEl.hide();
14015         }
14016         this.field.show();
14017         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14018             this.fixIEFocus = true;
14019             this.deferredFocus.defer(50, this);
14020         }else{
14021             this.field.focus();
14022         }
14023         this.fireEvent("startedit", this.boundEl, this.startValue);
14024     },
14025
14026     deferredFocus : function(){
14027         if(this.editing){
14028             this.field.focus();
14029         }
14030     },
14031
14032     /**
14033      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14034      * reverted to the original starting value.
14035      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14036      * cancel (defaults to false)
14037      */
14038     cancelEdit : function(remainVisible){
14039         if(this.editing){
14040             this.setValue(this.startValue);
14041             if(remainVisible !== true){
14042                 this.hide();
14043             }
14044         }
14045     },
14046
14047     // private
14048     onBlur : function(){
14049         if(this.allowBlur !== true && this.editing){
14050             this.completeEdit();
14051         }
14052     },
14053
14054     // private
14055     onHide : function(){
14056         if(this.editing){
14057             this.completeEdit();
14058             return;
14059         }
14060         this.field.blur();
14061         if(this.field.collapse){
14062             this.field.collapse();
14063         }
14064         this.el.hide();
14065         if(this.hideEl !== false){
14066             this.boundEl.show();
14067         }
14068         if(Roo.QuickTips){
14069             Roo.QuickTips.enable();
14070         }
14071     },
14072
14073     /**
14074      * Sets the data value of the editor
14075      * @param {Mixed} value Any valid value supported by the underlying field
14076      */
14077     setValue : function(v){
14078         this.field.setValue(v);
14079     },
14080
14081     /**
14082      * Gets the data value of the editor
14083      * @return {Mixed} The data value
14084      */
14085     getValue : function(){
14086         return this.field.getValue();
14087     }
14088 });/*
14089  * Based on:
14090  * Ext JS Library 1.1.1
14091  * Copyright(c) 2006-2007, Ext JS, LLC.
14092  *
14093  * Originally Released Under LGPL - original licence link has changed is not relivant.
14094  *
14095  * Fork - LGPL
14096  * <script type="text/javascript">
14097  */
14098  
14099 /**
14100  * @class Roo.BasicDialog
14101  * @extends Roo.util.Observable
14102  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14103  * <pre><code>
14104 var dlg = new Roo.BasicDialog("my-dlg", {
14105     height: 200,
14106     width: 300,
14107     minHeight: 100,
14108     minWidth: 150,
14109     modal: true,
14110     proxyDrag: true,
14111     shadow: true
14112 });
14113 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14114 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14115 dlg.addButton('Cancel', dlg.hide, dlg);
14116 dlg.show();
14117 </code></pre>
14118   <b>A Dialog should always be a direct child of the body element.</b>
14119  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14120  * @cfg {String} title Default text to display in the title bar (defaults to null)
14121  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14122  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14123  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14124  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14125  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14126  * (defaults to null with no animation)
14127  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14128  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14129  * property for valid values (defaults to 'all')
14130  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14131  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14132  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14133  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14134  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14135  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14136  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14137  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14138  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14139  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14140  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14141  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14142  * draggable = true (defaults to false)
14143  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14144  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14145  * shadow (defaults to false)
14146  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14147  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14148  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14149  * @cfg {Array} buttons Array of buttons
14150  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14151  * @constructor
14152  * Create a new BasicDialog.
14153  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14154  * @param {Object} config Configuration options
14155  */
14156 Roo.BasicDialog = function(el, config){
14157     this.el = Roo.get(el);
14158     var dh = Roo.DomHelper;
14159     if(!this.el && config && config.autoCreate){
14160         if(typeof config.autoCreate == "object"){
14161             if(!config.autoCreate.id){
14162                 config.autoCreate.id = el;
14163             }
14164             this.el = dh.append(document.body,
14165                         config.autoCreate, true);
14166         }else{
14167             this.el = dh.append(document.body,
14168                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14169         }
14170     }
14171     el = this.el;
14172     el.setDisplayed(true);
14173     el.hide = this.hideAction;
14174     this.id = el.id;
14175     el.addClass("x-dlg");
14176
14177     Roo.apply(this, config);
14178
14179     this.proxy = el.createProxy("x-dlg-proxy");
14180     this.proxy.hide = this.hideAction;
14181     this.proxy.setOpacity(.5);
14182     this.proxy.hide();
14183
14184     if(config.width){
14185         el.setWidth(config.width);
14186     }
14187     if(config.height){
14188         el.setHeight(config.height);
14189     }
14190     this.size = el.getSize();
14191     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14192         this.xy = [config.x,config.y];
14193     }else{
14194         this.xy = el.getCenterXY(true);
14195     }
14196     /** The header element @type Roo.Element */
14197     this.header = el.child("> .x-dlg-hd");
14198     /** The body element @type Roo.Element */
14199     this.body = el.child("> .x-dlg-bd");
14200     /** The footer element @type Roo.Element */
14201     this.footer = el.child("> .x-dlg-ft");
14202
14203     if(!this.header){
14204         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14205     }
14206     if(!this.body){
14207         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14208     }
14209
14210     this.header.unselectable();
14211     if(this.title){
14212         this.header.update(this.title);
14213     }
14214     // this element allows the dialog to be focused for keyboard event
14215     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14216     this.focusEl.swallowEvent("click", true);
14217
14218     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14219
14220     // wrap the body and footer for special rendering
14221     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14222     if(this.footer){
14223         this.bwrap.dom.appendChild(this.footer.dom);
14224     }
14225
14226     this.bg = this.el.createChild({
14227         tag: "div", cls:"x-dlg-bg",
14228         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14229     });
14230     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14231
14232
14233     if(this.autoScroll !== false && !this.autoTabs){
14234         this.body.setStyle("overflow", "auto");
14235     }
14236
14237     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14238
14239     if(this.closable !== false){
14240         this.el.addClass("x-dlg-closable");
14241         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14242         this.close.on("click", this.closeClick, this);
14243         this.close.addClassOnOver("x-dlg-close-over");
14244     }
14245     if(this.collapsible !== false){
14246         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14247         this.collapseBtn.on("click", this.collapseClick, this);
14248         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14249         this.header.on("dblclick", this.collapseClick, this);
14250     }
14251     if(this.resizable !== false){
14252         this.el.addClass("x-dlg-resizable");
14253         this.resizer = new Roo.Resizable(el, {
14254             minWidth: this.minWidth || 80,
14255             minHeight:this.minHeight || 80,
14256             handles: this.resizeHandles || "all",
14257             pinned: true
14258         });
14259         this.resizer.on("beforeresize", this.beforeResize, this);
14260         this.resizer.on("resize", this.onResize, this);
14261     }
14262     if(this.draggable !== false){
14263         el.addClass("x-dlg-draggable");
14264         if (!this.proxyDrag) {
14265             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14266         }
14267         else {
14268             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14269         }
14270         dd.setHandleElId(this.header.id);
14271         dd.endDrag = this.endMove.createDelegate(this);
14272         dd.startDrag = this.startMove.createDelegate(this);
14273         dd.onDrag = this.onDrag.createDelegate(this);
14274         dd.scroll = false;
14275         this.dd = dd;
14276     }
14277     if(this.modal){
14278         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14279         this.mask.enableDisplayMode("block");
14280         this.mask.hide();
14281         this.el.addClass("x-dlg-modal");
14282     }
14283     if(this.shadow){
14284         this.shadow = new Roo.Shadow({
14285             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14286             offset : this.shadowOffset
14287         });
14288     }else{
14289         this.shadowOffset = 0;
14290     }
14291     if(Roo.useShims && this.shim !== false){
14292         this.shim = this.el.createShim();
14293         this.shim.hide = this.hideAction;
14294         this.shim.hide();
14295     }else{
14296         this.shim = false;
14297     }
14298     if(this.autoTabs){
14299         this.initTabs();
14300     }
14301     if (this.buttons) { 
14302         var bts= this.buttons;
14303         this.buttons = [];
14304         Roo.each(bts, function(b) {
14305             this.addButton(b);
14306         }, this);
14307     }
14308     
14309     
14310     this.addEvents({
14311         /**
14312          * @event keydown
14313          * Fires when a key is pressed
14314          * @param {Roo.BasicDialog} this
14315          * @param {Roo.EventObject} e
14316          */
14317         "keydown" : true,
14318         /**
14319          * @event move
14320          * Fires when this dialog is moved by the user.
14321          * @param {Roo.BasicDialog} this
14322          * @param {Number} x The new page X
14323          * @param {Number} y The new page Y
14324          */
14325         "move" : true,
14326         /**
14327          * @event resize
14328          * Fires when this dialog is resized by the user.
14329          * @param {Roo.BasicDialog} this
14330          * @param {Number} width The new width
14331          * @param {Number} height The new height
14332          */
14333         "resize" : true,
14334         /**
14335          * @event beforehide
14336          * Fires before this dialog is hidden.
14337          * @param {Roo.BasicDialog} this
14338          */
14339         "beforehide" : true,
14340         /**
14341          * @event hide
14342          * Fires when this dialog is hidden.
14343          * @param {Roo.BasicDialog} this
14344          */
14345         "hide" : true,
14346         /**
14347          * @event beforeshow
14348          * Fires before this dialog is shown.
14349          * @param {Roo.BasicDialog} this
14350          */
14351         "beforeshow" : true,
14352         /**
14353          * @event show
14354          * Fires when this dialog is shown.
14355          * @param {Roo.BasicDialog} this
14356          */
14357         "show" : true
14358     });
14359     el.on("keydown", this.onKeyDown, this);
14360     el.on("mousedown", this.toFront, this);
14361     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14362     this.el.hide();
14363     Roo.DialogManager.register(this);
14364     Roo.BasicDialog.superclass.constructor.call(this);
14365 };
14366
14367 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14368     shadowOffset: Roo.isIE ? 6 : 5,
14369     minHeight: 80,
14370     minWidth: 200,
14371     minButtonWidth: 75,
14372     defaultButton: null,
14373     buttonAlign: "right",
14374     tabTag: 'div',
14375     firstShow: true,
14376
14377     /**
14378      * Sets the dialog title text
14379      * @param {String} text The title text to display
14380      * @return {Roo.BasicDialog} this
14381      */
14382     setTitle : function(text){
14383         this.header.update(text);
14384         return this;
14385     },
14386
14387     // private
14388     closeClick : function(){
14389         this.hide();
14390     },
14391
14392     // private
14393     collapseClick : function(){
14394         this[this.collapsed ? "expand" : "collapse"]();
14395     },
14396
14397     /**
14398      * Collapses the dialog to its minimized state (only the title bar is visible).
14399      * Equivalent to the user clicking the collapse dialog button.
14400      */
14401     collapse : function(){
14402         if(!this.collapsed){
14403             this.collapsed = true;
14404             this.el.addClass("x-dlg-collapsed");
14405             this.restoreHeight = this.el.getHeight();
14406             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14407         }
14408     },
14409
14410     /**
14411      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14412      * clicking the expand dialog button.
14413      */
14414     expand : function(){
14415         if(this.collapsed){
14416             this.collapsed = false;
14417             this.el.removeClass("x-dlg-collapsed");
14418             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14419         }
14420     },
14421
14422     /**
14423      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14424      * @return {Roo.TabPanel} The tabs component
14425      */
14426     initTabs : function(){
14427         var tabs = this.getTabs();
14428         while(tabs.getTab(0)){
14429             tabs.removeTab(0);
14430         }
14431         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14432             var dom = el.dom;
14433             tabs.addTab(Roo.id(dom), dom.title);
14434             dom.title = "";
14435         });
14436         tabs.activate(0);
14437         return tabs;
14438     },
14439
14440     // private
14441     beforeResize : function(){
14442         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14443     },
14444
14445     // private
14446     onResize : function(){
14447         this.refreshSize();
14448         this.syncBodyHeight();
14449         this.adjustAssets();
14450         this.focus();
14451         this.fireEvent("resize", this, this.size.width, this.size.height);
14452     },
14453
14454     // private
14455     onKeyDown : function(e){
14456         if(this.isVisible()){
14457             this.fireEvent("keydown", this, e);
14458         }
14459     },
14460
14461     /**
14462      * Resizes the dialog.
14463      * @param {Number} width
14464      * @param {Number} height
14465      * @return {Roo.BasicDialog} this
14466      */
14467     resizeTo : function(width, height){
14468         this.el.setSize(width, height);
14469         this.size = {width: width, height: height};
14470         this.syncBodyHeight();
14471         if(this.fixedcenter){
14472             this.center();
14473         }
14474         if(this.isVisible()){
14475             this.constrainXY();
14476             this.adjustAssets();
14477         }
14478         this.fireEvent("resize", this, width, height);
14479         return this;
14480     },
14481
14482
14483     /**
14484      * Resizes the dialog to fit the specified content size.
14485      * @param {Number} width
14486      * @param {Number} height
14487      * @return {Roo.BasicDialog} this
14488      */
14489     setContentSize : function(w, h){
14490         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14491         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14492         //if(!this.el.isBorderBox()){
14493             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14494             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14495         //}
14496         if(this.tabs){
14497             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14498             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14499         }
14500         this.resizeTo(w, h);
14501         return this;
14502     },
14503
14504     /**
14505      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14506      * executed in response to a particular key being pressed while the dialog is active.
14507      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14508      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14509      * @param {Function} fn The function to call
14510      * @param {Object} scope (optional) The scope of the function
14511      * @return {Roo.BasicDialog} this
14512      */
14513     addKeyListener : function(key, fn, scope){
14514         var keyCode, shift, ctrl, alt;
14515         if(typeof key == "object" && !(key instanceof Array)){
14516             keyCode = key["key"];
14517             shift = key["shift"];
14518             ctrl = key["ctrl"];
14519             alt = key["alt"];
14520         }else{
14521             keyCode = key;
14522         }
14523         var handler = function(dlg, e){
14524             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14525                 var k = e.getKey();
14526                 if(keyCode instanceof Array){
14527                     for(var i = 0, len = keyCode.length; i < len; i++){
14528                         if(keyCode[i] == k){
14529                           fn.call(scope || window, dlg, k, e);
14530                           return;
14531                         }
14532                     }
14533                 }else{
14534                     if(k == keyCode){
14535                         fn.call(scope || window, dlg, k, e);
14536                     }
14537                 }
14538             }
14539         };
14540         this.on("keydown", handler);
14541         return this;
14542     },
14543
14544     /**
14545      * Returns the TabPanel component (creates it if it doesn't exist).
14546      * Note: If you wish to simply check for the existence of tabs without creating them,
14547      * check for a null 'tabs' property.
14548      * @return {Roo.TabPanel} The tabs component
14549      */
14550     getTabs : function(){
14551         if(!this.tabs){
14552             this.el.addClass("x-dlg-auto-tabs");
14553             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14554             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14555         }
14556         return this.tabs;
14557     },
14558
14559     /**
14560      * Adds a button to the footer section of the dialog.
14561      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14562      * object or a valid Roo.DomHelper element config
14563      * @param {Function} handler The function called when the button is clicked
14564      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14565      * @return {Roo.Button} The new button
14566      */
14567     addButton : function(config, handler, scope){
14568         var dh = Roo.DomHelper;
14569         if(!this.footer){
14570             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14571         }
14572         if(!this.btnContainer){
14573             var tb = this.footer.createChild({
14574
14575                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14576                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14577             }, null, true);
14578             this.btnContainer = tb.firstChild.firstChild.firstChild;
14579         }
14580         var bconfig = {
14581             handler: handler,
14582             scope: scope,
14583             minWidth: this.minButtonWidth,
14584             hideParent:true
14585         };
14586         if(typeof config == "string"){
14587             bconfig.text = config;
14588         }else{
14589             if(config.tag){
14590                 bconfig.dhconfig = config;
14591             }else{
14592                 Roo.apply(bconfig, config);
14593             }
14594         }
14595         var fc = false;
14596         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14597             bconfig.position = Math.max(0, bconfig.position);
14598             fc = this.btnContainer.childNodes[bconfig.position];
14599         }
14600          
14601         var btn = new Roo.Button(
14602             fc ? 
14603                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14604                 : this.btnContainer.appendChild(document.createElement("td")),
14605             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14606             bconfig
14607         );
14608         this.syncBodyHeight();
14609         if(!this.buttons){
14610             /**
14611              * Array of all the buttons that have been added to this dialog via addButton
14612              * @type Array
14613              */
14614             this.buttons = [];
14615         }
14616         this.buttons.push(btn);
14617         return btn;
14618     },
14619
14620     /**
14621      * Sets the default button to be focused when the dialog is displayed.
14622      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14623      * @return {Roo.BasicDialog} this
14624      */
14625     setDefaultButton : function(btn){
14626         this.defaultButton = btn;
14627         return this;
14628     },
14629
14630     // private
14631     getHeaderFooterHeight : function(safe){
14632         var height = 0;
14633         if(this.header){
14634            height += this.header.getHeight();
14635         }
14636         if(this.footer){
14637            var fm = this.footer.getMargins();
14638             height += (this.footer.getHeight()+fm.top+fm.bottom);
14639         }
14640         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14641         height += this.centerBg.getPadding("tb");
14642         return height;
14643     },
14644
14645     // private
14646     syncBodyHeight : function(){
14647         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14648         var height = this.size.height - this.getHeaderFooterHeight(false);
14649         bd.setHeight(height-bd.getMargins("tb"));
14650         var hh = this.header.getHeight();
14651         var h = this.size.height-hh;
14652         cb.setHeight(h);
14653         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14654         bw.setHeight(h-cb.getPadding("tb"));
14655         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14656         bd.setWidth(bw.getWidth(true));
14657         if(this.tabs){
14658             this.tabs.syncHeight();
14659             if(Roo.isIE){
14660                 this.tabs.el.repaint();
14661             }
14662         }
14663     },
14664
14665     /**
14666      * Restores the previous state of the dialog if Roo.state is configured.
14667      * @return {Roo.BasicDialog} this
14668      */
14669     restoreState : function(){
14670         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14671         if(box && box.width){
14672             this.xy = [box.x, box.y];
14673             this.resizeTo(box.width, box.height);
14674         }
14675         return this;
14676     },
14677
14678     // private
14679     beforeShow : function(){
14680         this.expand();
14681         if(this.fixedcenter){
14682             this.xy = this.el.getCenterXY(true);
14683         }
14684         if(this.modal){
14685             Roo.get(document.body).addClass("x-body-masked");
14686             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14687             this.mask.show();
14688         }
14689         this.constrainXY();
14690     },
14691
14692     // private
14693     animShow : function(){
14694         var b = Roo.get(this.animateTarget).getBox();
14695         this.proxy.setSize(b.width, b.height);
14696         this.proxy.setLocation(b.x, b.y);
14697         this.proxy.show();
14698         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14699                     true, .35, this.showEl.createDelegate(this));
14700     },
14701
14702     /**
14703      * Shows the dialog.
14704      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14705      * @return {Roo.BasicDialog} this
14706      */
14707     show : function(animateTarget){
14708         if (this.fireEvent("beforeshow", this) === false){
14709             return;
14710         }
14711         if(this.syncHeightBeforeShow){
14712             this.syncBodyHeight();
14713         }else if(this.firstShow){
14714             this.firstShow = false;
14715             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14716         }
14717         this.animateTarget = animateTarget || this.animateTarget;
14718         if(!this.el.isVisible()){
14719             this.beforeShow();
14720             if(this.animateTarget && Roo.get(this.animateTarget)){
14721                 this.animShow();
14722             }else{
14723                 this.showEl();
14724             }
14725         }
14726         return this;
14727     },
14728
14729     // private
14730     showEl : function(){
14731         this.proxy.hide();
14732         this.el.setXY(this.xy);
14733         this.el.show();
14734         this.adjustAssets(true);
14735         this.toFront();
14736         this.focus();
14737         // IE peekaboo bug - fix found by Dave Fenwick
14738         if(Roo.isIE){
14739             this.el.repaint();
14740         }
14741         this.fireEvent("show", this);
14742     },
14743
14744     /**
14745      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14746      * dialog itself will receive focus.
14747      */
14748     focus : function(){
14749         if(this.defaultButton){
14750             this.defaultButton.focus();
14751         }else{
14752             this.focusEl.focus();
14753         }
14754     },
14755
14756     // private
14757     constrainXY : function(){
14758         if(this.constraintoviewport !== false){
14759             if(!this.viewSize){
14760                 if(this.container){
14761                     var s = this.container.getSize();
14762                     this.viewSize = [s.width, s.height];
14763                 }else{
14764                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14765                 }
14766             }
14767             var s = Roo.get(this.container||document).getScroll();
14768
14769             var x = this.xy[0], y = this.xy[1];
14770             var w = this.size.width, h = this.size.height;
14771             var vw = this.viewSize[0], vh = this.viewSize[1];
14772             // only move it if it needs it
14773             var moved = false;
14774             // first validate right/bottom
14775             if(x + w > vw+s.left){
14776                 x = vw - w;
14777                 moved = true;
14778             }
14779             if(y + h > vh+s.top){
14780                 y = vh - h;
14781                 moved = true;
14782             }
14783             // then make sure top/left isn't negative
14784             if(x < s.left){
14785                 x = s.left;
14786                 moved = true;
14787             }
14788             if(y < s.top){
14789                 y = s.top;
14790                 moved = true;
14791             }
14792             if(moved){
14793                 // cache xy
14794                 this.xy = [x, y];
14795                 if(this.isVisible()){
14796                     this.el.setLocation(x, y);
14797                     this.adjustAssets();
14798                 }
14799             }
14800         }
14801     },
14802
14803     // private
14804     onDrag : function(){
14805         if(!this.proxyDrag){
14806             this.xy = this.el.getXY();
14807             this.adjustAssets();
14808         }
14809     },
14810
14811     // private
14812     adjustAssets : function(doShow){
14813         var x = this.xy[0], y = this.xy[1];
14814         var w = this.size.width, h = this.size.height;
14815         if(doShow === true){
14816             if(this.shadow){
14817                 this.shadow.show(this.el);
14818             }
14819             if(this.shim){
14820                 this.shim.show();
14821             }
14822         }
14823         if(this.shadow && this.shadow.isVisible()){
14824             this.shadow.show(this.el);
14825         }
14826         if(this.shim && this.shim.isVisible()){
14827             this.shim.setBounds(x, y, w, h);
14828         }
14829     },
14830
14831     // private
14832     adjustViewport : function(w, h){
14833         if(!w || !h){
14834             w = Roo.lib.Dom.getViewWidth();
14835             h = Roo.lib.Dom.getViewHeight();
14836         }
14837         // cache the size
14838         this.viewSize = [w, h];
14839         if(this.modal && this.mask.isVisible()){
14840             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14841             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14842         }
14843         if(this.isVisible()){
14844             this.constrainXY();
14845         }
14846     },
14847
14848     /**
14849      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14850      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14851      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14852      */
14853     destroy : function(removeEl){
14854         if(this.isVisible()){
14855             this.animateTarget = null;
14856             this.hide();
14857         }
14858         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14859         if(this.tabs){
14860             this.tabs.destroy(removeEl);
14861         }
14862         Roo.destroy(
14863              this.shim,
14864              this.proxy,
14865              this.resizer,
14866              this.close,
14867              this.mask
14868         );
14869         if(this.dd){
14870             this.dd.unreg();
14871         }
14872         if(this.buttons){
14873            for(var i = 0, len = this.buttons.length; i < len; i++){
14874                this.buttons[i].destroy();
14875            }
14876         }
14877         this.el.removeAllListeners();
14878         if(removeEl === true){
14879             this.el.update("");
14880             this.el.remove();
14881         }
14882         Roo.DialogManager.unregister(this);
14883     },
14884
14885     // private
14886     startMove : function(){
14887         if(this.proxyDrag){
14888             this.proxy.show();
14889         }
14890         if(this.constraintoviewport !== false){
14891             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14892         }
14893     },
14894
14895     // private
14896     endMove : function(){
14897         if(!this.proxyDrag){
14898             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14899         }else{
14900             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14901             this.proxy.hide();
14902         }
14903         this.refreshSize();
14904         this.adjustAssets();
14905         this.focus();
14906         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14907     },
14908
14909     /**
14910      * Brings this dialog to the front of any other visible dialogs
14911      * @return {Roo.BasicDialog} this
14912      */
14913     toFront : function(){
14914         Roo.DialogManager.bringToFront(this);
14915         return this;
14916     },
14917
14918     /**
14919      * Sends this dialog to the back (under) of any other visible dialogs
14920      * @return {Roo.BasicDialog} this
14921      */
14922     toBack : function(){
14923         Roo.DialogManager.sendToBack(this);
14924         return this;
14925     },
14926
14927     /**
14928      * Centers this dialog in the viewport
14929      * @return {Roo.BasicDialog} this
14930      */
14931     center : function(){
14932         var xy = this.el.getCenterXY(true);
14933         this.moveTo(xy[0], xy[1]);
14934         return this;
14935     },
14936
14937     /**
14938      * Moves the dialog's top-left corner to the specified point
14939      * @param {Number} x
14940      * @param {Number} y
14941      * @return {Roo.BasicDialog} this
14942      */
14943     moveTo : function(x, y){
14944         this.xy = [x,y];
14945         if(this.isVisible()){
14946             this.el.setXY(this.xy);
14947             this.adjustAssets();
14948         }
14949         return this;
14950     },
14951
14952     /**
14953      * Aligns the dialog to the specified element
14954      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14955      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14956      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14957      * @return {Roo.BasicDialog} this
14958      */
14959     alignTo : function(element, position, offsets){
14960         this.xy = this.el.getAlignToXY(element, position, offsets);
14961         if(this.isVisible()){
14962             this.el.setXY(this.xy);
14963             this.adjustAssets();
14964         }
14965         return this;
14966     },
14967
14968     /**
14969      * Anchors an element to another element and realigns it when the window is resized.
14970      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14971      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14972      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14973      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14974      * is a number, it is used as the buffer delay (defaults to 50ms).
14975      * @return {Roo.BasicDialog} this
14976      */
14977     anchorTo : function(el, alignment, offsets, monitorScroll){
14978         var action = function(){
14979             this.alignTo(el, alignment, offsets);
14980         };
14981         Roo.EventManager.onWindowResize(action, this);
14982         var tm = typeof monitorScroll;
14983         if(tm != 'undefined'){
14984             Roo.EventManager.on(window, 'scroll', action, this,
14985                 {buffer: tm == 'number' ? monitorScroll : 50});
14986         }
14987         action.call(this);
14988         return this;
14989     },
14990
14991     /**
14992      * Returns true if the dialog is visible
14993      * @return {Boolean}
14994      */
14995     isVisible : function(){
14996         return this.el.isVisible();
14997     },
14998
14999     // private
15000     animHide : function(callback){
15001         var b = Roo.get(this.animateTarget).getBox();
15002         this.proxy.show();
15003         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15004         this.el.hide();
15005         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15006                     this.hideEl.createDelegate(this, [callback]));
15007     },
15008
15009     /**
15010      * Hides the dialog.
15011      * @param {Function} callback (optional) Function to call when the dialog is hidden
15012      * @return {Roo.BasicDialog} this
15013      */
15014     hide : function(callback){
15015         if (this.fireEvent("beforehide", this) === false){
15016             return;
15017         }
15018         if(this.shadow){
15019             this.shadow.hide();
15020         }
15021         if(this.shim) {
15022           this.shim.hide();
15023         }
15024         // sometimes animateTarget seems to get set.. causing problems...
15025         // this just double checks..
15026         if(this.animateTarget && Roo.get(this.animateTarget)) {
15027            this.animHide(callback);
15028         }else{
15029             this.el.hide();
15030             this.hideEl(callback);
15031         }
15032         return this;
15033     },
15034
15035     // private
15036     hideEl : function(callback){
15037         this.proxy.hide();
15038         if(this.modal){
15039             this.mask.hide();
15040             Roo.get(document.body).removeClass("x-body-masked");
15041         }
15042         this.fireEvent("hide", this);
15043         if(typeof callback == "function"){
15044             callback();
15045         }
15046     },
15047
15048     // private
15049     hideAction : function(){
15050         this.setLeft("-10000px");
15051         this.setTop("-10000px");
15052         this.setStyle("visibility", "hidden");
15053     },
15054
15055     // private
15056     refreshSize : function(){
15057         this.size = this.el.getSize();
15058         this.xy = this.el.getXY();
15059         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15060     },
15061
15062     // private
15063     // z-index is managed by the DialogManager and may be overwritten at any time
15064     setZIndex : function(index){
15065         if(this.modal){
15066             this.mask.setStyle("z-index", index);
15067         }
15068         if(this.shim){
15069             this.shim.setStyle("z-index", ++index);
15070         }
15071         if(this.shadow){
15072             this.shadow.setZIndex(++index);
15073         }
15074         this.el.setStyle("z-index", ++index);
15075         if(this.proxy){
15076             this.proxy.setStyle("z-index", ++index);
15077         }
15078         if(this.resizer){
15079             this.resizer.proxy.setStyle("z-index", ++index);
15080         }
15081
15082         this.lastZIndex = index;
15083     },
15084
15085     /**
15086      * Returns the element for this dialog
15087      * @return {Roo.Element} The underlying dialog Element
15088      */
15089     getEl : function(){
15090         return this.el;
15091     }
15092 });
15093
15094 /**
15095  * @class Roo.DialogManager
15096  * Provides global access to BasicDialogs that have been created and
15097  * support for z-indexing (layering) multiple open dialogs.
15098  */
15099 Roo.DialogManager = function(){
15100     var list = {};
15101     var accessList = [];
15102     var front = null;
15103
15104     // private
15105     var sortDialogs = function(d1, d2){
15106         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15107     };
15108
15109     // private
15110     var orderDialogs = function(){
15111         accessList.sort(sortDialogs);
15112         var seed = Roo.DialogManager.zseed;
15113         for(var i = 0, len = accessList.length; i < len; i++){
15114             var dlg = accessList[i];
15115             if(dlg){
15116                 dlg.setZIndex(seed + (i*10));
15117             }
15118         }
15119     };
15120
15121     return {
15122         /**
15123          * The starting z-index for BasicDialogs (defaults to 9000)
15124          * @type Number The z-index value
15125          */
15126         zseed : 9000,
15127
15128         // private
15129         register : function(dlg){
15130             list[dlg.id] = dlg;
15131             accessList.push(dlg);
15132         },
15133
15134         // private
15135         unregister : function(dlg){
15136             delete list[dlg.id];
15137             var i=0;
15138             var len=0;
15139             if(!accessList.indexOf){
15140                 for(  i = 0, len = accessList.length; i < len; i++){
15141                     if(accessList[i] == dlg){
15142                         accessList.splice(i, 1);
15143                         return;
15144                     }
15145                 }
15146             }else{
15147                  i = accessList.indexOf(dlg);
15148                 if(i != -1){
15149                     accessList.splice(i, 1);
15150                 }
15151             }
15152         },
15153
15154         /**
15155          * Gets a registered dialog by id
15156          * @param {String/Object} id The id of the dialog or a dialog
15157          * @return {Roo.BasicDialog} this
15158          */
15159         get : function(id){
15160             return typeof id == "object" ? id : list[id];
15161         },
15162
15163         /**
15164          * Brings the specified dialog to the front
15165          * @param {String/Object} dlg The id of the dialog or a dialog
15166          * @return {Roo.BasicDialog} this
15167          */
15168         bringToFront : function(dlg){
15169             dlg = this.get(dlg);
15170             if(dlg != front){
15171                 front = dlg;
15172                 dlg._lastAccess = new Date().getTime();
15173                 orderDialogs();
15174             }
15175             return dlg;
15176         },
15177
15178         /**
15179          * Sends the specified dialog to the back
15180          * @param {String/Object} dlg The id of the dialog or a dialog
15181          * @return {Roo.BasicDialog} this
15182          */
15183         sendToBack : function(dlg){
15184             dlg = this.get(dlg);
15185             dlg._lastAccess = -(new Date().getTime());
15186             orderDialogs();
15187             return dlg;
15188         },
15189
15190         /**
15191          * Hides all dialogs
15192          */
15193         hideAll : function(){
15194             for(var id in list){
15195                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15196                     list[id].hide();
15197                 }
15198             }
15199         }
15200     };
15201 }();
15202
15203 /**
15204  * @class Roo.LayoutDialog
15205  * @extends Roo.BasicDialog
15206  * Dialog which provides adjustments for working with a layout in a Dialog.
15207  * Add your necessary layout config options to the dialog's config.<br>
15208  * Example usage (including a nested layout):
15209  * <pre><code>
15210 if(!dialog){
15211     dialog = new Roo.LayoutDialog("download-dlg", {
15212         modal: true,
15213         width:600,
15214         height:450,
15215         shadow:true,
15216         minWidth:500,
15217         minHeight:350,
15218         autoTabs:true,
15219         proxyDrag:true,
15220         // layout config merges with the dialog config
15221         center:{
15222             tabPosition: "top",
15223             alwaysShowTabs: true
15224         }
15225     });
15226     dialog.addKeyListener(27, dialog.hide, dialog);
15227     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15228     dialog.addButton("Build It!", this.getDownload, this);
15229
15230     // we can even add nested layouts
15231     var innerLayout = new Roo.BorderLayout("dl-inner", {
15232         east: {
15233             initialSize: 200,
15234             autoScroll:true,
15235             split:true
15236         },
15237         center: {
15238             autoScroll:true
15239         }
15240     });
15241     innerLayout.beginUpdate();
15242     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15243     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15244     innerLayout.endUpdate(true);
15245
15246     var layout = dialog.getLayout();
15247     layout.beginUpdate();
15248     layout.add("center", new Roo.ContentPanel("standard-panel",
15249                         {title: "Download the Source", fitToFrame:true}));
15250     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15251                {title: "Build your own roo.js"}));
15252     layout.getRegion("center").showPanel(sp);
15253     layout.endUpdate();
15254 }
15255 </code></pre>
15256     * @constructor
15257     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15258     * @param {Object} config configuration options
15259   */
15260 Roo.LayoutDialog = function(el, cfg){
15261     
15262     var config=  cfg;
15263     if (typeof(cfg) == 'undefined') {
15264         config = Roo.apply({}, el);
15265         // not sure why we use documentElement here.. - it should always be body.
15266         // IE7 borks horribly if we use documentElement.
15267         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15268         //config.autoCreate = true;
15269     }
15270     
15271     
15272     config.autoTabs = false;
15273     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15274     this.body.setStyle({overflow:"hidden", position:"relative"});
15275     this.layout = new Roo.BorderLayout(this.body.dom, config);
15276     this.layout.monitorWindowResize = false;
15277     this.el.addClass("x-dlg-auto-layout");
15278     // fix case when center region overwrites center function
15279     this.center = Roo.BasicDialog.prototype.center;
15280     this.on("show", this.layout.layout, this.layout, true);
15281     if (config.items) {
15282         var xitems = config.items;
15283         delete config.items;
15284         Roo.each(xitems, this.addxtype, this);
15285     }
15286     
15287     
15288 };
15289 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15290     /**
15291      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15292      * @deprecated
15293      */
15294     endUpdate : function(){
15295         this.layout.endUpdate();
15296     },
15297
15298     /**
15299      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15300      *  @deprecated
15301      */
15302     beginUpdate : function(){
15303         this.layout.beginUpdate();
15304     },
15305
15306     /**
15307      * Get the BorderLayout for this dialog
15308      * @return {Roo.BorderLayout}
15309      */
15310     getLayout : function(){
15311         return this.layout;
15312     },
15313
15314     showEl : function(){
15315         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15316         if(Roo.isIE7){
15317             this.layout.layout();
15318         }
15319     },
15320
15321     // private
15322     // Use the syncHeightBeforeShow config option to control this automatically
15323     syncBodyHeight : function(){
15324         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15325         if(this.layout){this.layout.layout();}
15326     },
15327     
15328       /**
15329      * Add an xtype element (actually adds to the layout.)
15330      * @return {Object} xdata xtype object data.
15331      */
15332     
15333     addxtype : function(c) {
15334         return this.layout.addxtype(c);
15335     }
15336 });/*
15337  * Based on:
15338  * Ext JS Library 1.1.1
15339  * Copyright(c) 2006-2007, Ext JS, LLC.
15340  *
15341  * Originally Released Under LGPL - original licence link has changed is not relivant.
15342  *
15343  * Fork - LGPL
15344  * <script type="text/javascript">
15345  */
15346  
15347 /**
15348  * @class Roo.MessageBox
15349  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15350  * Example usage:
15351  *<pre><code>
15352 // Basic alert:
15353 Roo.Msg.alert('Status', 'Changes saved successfully.');
15354
15355 // Prompt for user data:
15356 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15357     if (btn == 'ok'){
15358         // process text value...
15359     }
15360 });
15361
15362 // Show a dialog using config options:
15363 Roo.Msg.show({
15364    title:'Save Changes?',
15365    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15366    buttons: Roo.Msg.YESNOCANCEL,
15367    fn: processResult,
15368    animEl: 'elId'
15369 });
15370 </code></pre>
15371  * @singleton
15372  */
15373 Roo.MessageBox = function(){
15374     var dlg, opt, mask, waitTimer;
15375     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15376     var buttons, activeTextEl, bwidth;
15377
15378     // private
15379     var handleButton = function(button){
15380         dlg.hide();
15381         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15382     };
15383
15384     // private
15385     var handleHide = function(){
15386         if(opt && opt.cls){
15387             dlg.el.removeClass(opt.cls);
15388         }
15389         if(waitTimer){
15390             Roo.TaskMgr.stop(waitTimer);
15391             waitTimer = null;
15392         }
15393     };
15394
15395     // private
15396     var updateButtons = function(b){
15397         var width = 0;
15398         if(!b){
15399             buttons["ok"].hide();
15400             buttons["cancel"].hide();
15401             buttons["yes"].hide();
15402             buttons["no"].hide();
15403             dlg.footer.dom.style.display = 'none';
15404             return width;
15405         }
15406         dlg.footer.dom.style.display = '';
15407         for(var k in buttons){
15408             if(typeof buttons[k] != "function"){
15409                 if(b[k]){
15410                     buttons[k].show();
15411                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15412                     width += buttons[k].el.getWidth()+15;
15413                 }else{
15414                     buttons[k].hide();
15415                 }
15416             }
15417         }
15418         return width;
15419     };
15420
15421     // private
15422     var handleEsc = function(d, k, e){
15423         if(opt && opt.closable !== false){
15424             dlg.hide();
15425         }
15426         if(e){
15427             e.stopEvent();
15428         }
15429     };
15430
15431     return {
15432         /**
15433          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15434          * @return {Roo.BasicDialog} The BasicDialog element
15435          */
15436         getDialog : function(){
15437            if(!dlg){
15438                 dlg = new Roo.BasicDialog("x-msg-box", {
15439                     autoCreate : true,
15440                     shadow: true,
15441                     draggable: true,
15442                     resizable:false,
15443                     constraintoviewport:false,
15444                     fixedcenter:true,
15445                     collapsible : false,
15446                     shim:true,
15447                     modal: true,
15448                     width:400, height:100,
15449                     buttonAlign:"center",
15450                     closeClick : function(){
15451                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15452                             handleButton("no");
15453                         }else{
15454                             handleButton("cancel");
15455                         }
15456                     }
15457                 });
15458                 dlg.on("hide", handleHide);
15459                 mask = dlg.mask;
15460                 dlg.addKeyListener(27, handleEsc);
15461                 buttons = {};
15462                 var bt = this.buttonText;
15463                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15464                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15465                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15466                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15467                 bodyEl = dlg.body.createChild({
15468
15469                     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>'
15470                 });
15471                 msgEl = bodyEl.dom.firstChild;
15472                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15473                 textboxEl.enableDisplayMode();
15474                 textboxEl.addKeyListener([10,13], function(){
15475                     if(dlg.isVisible() && opt && opt.buttons){
15476                         if(opt.buttons.ok){
15477                             handleButton("ok");
15478                         }else if(opt.buttons.yes){
15479                             handleButton("yes");
15480                         }
15481                     }
15482                 });
15483                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15484                 textareaEl.enableDisplayMode();
15485                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15486                 progressEl.enableDisplayMode();
15487                 var pf = progressEl.dom.firstChild;
15488                 if (pf) {
15489                     pp = Roo.get(pf.firstChild);
15490                     pp.setHeight(pf.offsetHeight);
15491                 }
15492                 
15493             }
15494             return dlg;
15495         },
15496
15497         /**
15498          * Updates the message box body text
15499          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15500          * the XHTML-compliant non-breaking space character '&amp;#160;')
15501          * @return {Roo.MessageBox} This message box
15502          */
15503         updateText : function(text){
15504             if(!dlg.isVisible() && !opt.width){
15505                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15506             }
15507             msgEl.innerHTML = text || '&#160;';
15508             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15509                         Math.max(opt.minWidth || this.minWidth, bwidth));
15510             if(opt.prompt){
15511                 activeTextEl.setWidth(w);
15512             }
15513             if(dlg.isVisible()){
15514                 dlg.fixedcenter = false;
15515             }
15516             dlg.setContentSize(w, bodyEl.getHeight());
15517             if(dlg.isVisible()){
15518                 dlg.fixedcenter = true;
15519             }
15520             return this;
15521         },
15522
15523         /**
15524          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15525          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15526          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15527          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15528          * @return {Roo.MessageBox} This message box
15529          */
15530         updateProgress : function(value, text){
15531             if(text){
15532                 this.updateText(text);
15533             }
15534             if (pp) { // weird bug on my firefox - for some reason this is not defined
15535                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15536             }
15537             return this;
15538         },        
15539
15540         /**
15541          * Returns true if the message box is currently displayed
15542          * @return {Boolean} True if the message box is visible, else false
15543          */
15544         isVisible : function(){
15545             return dlg && dlg.isVisible();  
15546         },
15547
15548         /**
15549          * Hides the message box if it is displayed
15550          */
15551         hide : function(){
15552             if(this.isVisible()){
15553                 dlg.hide();
15554             }  
15555         },
15556
15557         /**
15558          * Displays a new message box, or reinitializes an existing message box, based on the config options
15559          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15560          * The following config object properties are supported:
15561          * <pre>
15562 Property    Type             Description
15563 ----------  ---------------  ------------------------------------------------------------------------------------
15564 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15565                                    closes (defaults to undefined)
15566 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15567                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15568 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15569                                    progress and wait dialogs will ignore this property and always hide the
15570                                    close button as they can only be closed programmatically.
15571 cls               String           A custom CSS class to apply to the message box element
15572 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15573                                    displayed (defaults to 75)
15574 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15575                                    function will be btn (the name of the button that was clicked, if applicable,
15576                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15577                                    Progress and wait dialogs will ignore this option since they do not respond to
15578                                    user actions and can only be closed programmatically, so any required function
15579                                    should be called by the same code after it closes the dialog.
15580 icon              String           A CSS class that provides a background image to be used as an icon for
15581                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15582 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15583 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15584 modal             Boolean          False to allow user interaction with the page while the message box is
15585                                    displayed (defaults to true)
15586 msg               String           A string that will replace the existing message box body text (defaults
15587                                    to the XHTML-compliant non-breaking space character '&#160;')
15588 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15589 progress          Boolean          True to display a progress bar (defaults to false)
15590 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15591 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15592 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15593 title             String           The title text
15594 value             String           The string value to set into the active textbox element if displayed
15595 wait              Boolean          True to display a progress bar (defaults to false)
15596 width             Number           The width of the dialog in pixels
15597 </pre>
15598          *
15599          * Example usage:
15600          * <pre><code>
15601 Roo.Msg.show({
15602    title: 'Address',
15603    msg: 'Please enter your address:',
15604    width: 300,
15605    buttons: Roo.MessageBox.OKCANCEL,
15606    multiline: true,
15607    fn: saveAddress,
15608    animEl: 'addAddressBtn'
15609 });
15610 </code></pre>
15611          * @param {Object} config Configuration options
15612          * @return {Roo.MessageBox} This message box
15613          */
15614         show : function(options){
15615             if(this.isVisible()){
15616                 this.hide();
15617             }
15618             var d = this.getDialog();
15619             opt = options;
15620             d.setTitle(opt.title || "&#160;");
15621             d.close.setDisplayed(opt.closable !== false);
15622             activeTextEl = textboxEl;
15623             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15624             if(opt.prompt){
15625                 if(opt.multiline){
15626                     textboxEl.hide();
15627                     textareaEl.show();
15628                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15629                         opt.multiline : this.defaultTextHeight);
15630                     activeTextEl = textareaEl;
15631                 }else{
15632                     textboxEl.show();
15633                     textareaEl.hide();
15634                 }
15635             }else{
15636                 textboxEl.hide();
15637                 textareaEl.hide();
15638             }
15639             progressEl.setDisplayed(opt.progress === true);
15640             this.updateProgress(0);
15641             activeTextEl.dom.value = opt.value || "";
15642             if(opt.prompt){
15643                 dlg.setDefaultButton(activeTextEl);
15644             }else{
15645                 var bs = opt.buttons;
15646                 var db = null;
15647                 if(bs && bs.ok){
15648                     db = buttons["ok"];
15649                 }else if(bs && bs.yes){
15650                     db = buttons["yes"];
15651                 }
15652                 dlg.setDefaultButton(db);
15653             }
15654             bwidth = updateButtons(opt.buttons);
15655             this.updateText(opt.msg);
15656             if(opt.cls){
15657                 d.el.addClass(opt.cls);
15658             }
15659             d.proxyDrag = opt.proxyDrag === true;
15660             d.modal = opt.modal !== false;
15661             d.mask = opt.modal !== false ? mask : false;
15662             if(!d.isVisible()){
15663                 // force it to the end of the z-index stack so it gets a cursor in FF
15664                 document.body.appendChild(dlg.el.dom);
15665                 d.animateTarget = null;
15666                 d.show(options.animEl);
15667             }
15668             return this;
15669         },
15670
15671         /**
15672          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15673          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15674          * and closing the message box when the process is complete.
15675          * @param {String} title The title bar text
15676          * @param {String} msg The message box body text
15677          * @return {Roo.MessageBox} This message box
15678          */
15679         progress : function(title, msg){
15680             this.show({
15681                 title : title,
15682                 msg : msg,
15683                 buttons: false,
15684                 progress:true,
15685                 closable:false,
15686                 minWidth: this.minProgressWidth,
15687                 modal : true
15688             });
15689             return this;
15690         },
15691
15692         /**
15693          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15694          * If a callback function is passed it will be called after the user clicks the button, and the
15695          * id of the button that was clicked will be passed as the only parameter to the callback
15696          * (could also be the top-right close button).
15697          * @param {String} title The title bar text
15698          * @param {String} msg The message box body text
15699          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15700          * @param {Object} scope (optional) The scope of the callback function
15701          * @return {Roo.MessageBox} This message box
15702          */
15703         alert : function(title, msg, fn, scope){
15704             this.show({
15705                 title : title,
15706                 msg : msg,
15707                 buttons: this.OK,
15708                 fn: fn,
15709                 scope : scope,
15710                 modal : true
15711             });
15712             return this;
15713         },
15714
15715         /**
15716          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15717          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15718          * You are responsible for closing the message box when the process is complete.
15719          * @param {String} msg The message box body text
15720          * @param {String} title (optional) The title bar text
15721          * @return {Roo.MessageBox} This message box
15722          */
15723         wait : function(msg, title){
15724             this.show({
15725                 title : title,
15726                 msg : msg,
15727                 buttons: false,
15728                 closable:false,
15729                 progress:true,
15730                 modal:true,
15731                 width:300,
15732                 wait:true
15733             });
15734             waitTimer = Roo.TaskMgr.start({
15735                 run: function(i){
15736                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15737                 },
15738                 interval: 1000
15739             });
15740             return this;
15741         },
15742
15743         /**
15744          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15745          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15746          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15747          * @param {String} title The title bar text
15748          * @param {String} msg The message box body text
15749          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15750          * @param {Object} scope (optional) The scope of the callback function
15751          * @return {Roo.MessageBox} This message box
15752          */
15753         confirm : function(title, msg, fn, scope){
15754             this.show({
15755                 title : title,
15756                 msg : msg,
15757                 buttons: this.YESNO,
15758                 fn: fn,
15759                 scope : scope,
15760                 modal : true
15761             });
15762             return this;
15763         },
15764
15765         /**
15766          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15767          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15768          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15769          * (could also be the top-right close button) and the text that was entered will be passed as the two
15770          * parameters to the callback.
15771          * @param {String} title The title bar text
15772          * @param {String} msg The message box body text
15773          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15774          * @param {Object} scope (optional) The scope of the callback function
15775          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15776          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15777          * @return {Roo.MessageBox} This message box
15778          */
15779         prompt : function(title, msg, fn, scope, multiline){
15780             this.show({
15781                 title : title,
15782                 msg : msg,
15783                 buttons: this.OKCANCEL,
15784                 fn: fn,
15785                 minWidth:250,
15786                 scope : scope,
15787                 prompt:true,
15788                 multiline: multiline,
15789                 modal : true
15790             });
15791             return this;
15792         },
15793
15794         /**
15795          * Button config that displays a single OK button
15796          * @type Object
15797          */
15798         OK : {ok:true},
15799         /**
15800          * Button config that displays Yes and No buttons
15801          * @type Object
15802          */
15803         YESNO : {yes:true, no:true},
15804         /**
15805          * Button config that displays OK and Cancel buttons
15806          * @type Object
15807          */
15808         OKCANCEL : {ok:true, cancel:true},
15809         /**
15810          * Button config that displays Yes, No and Cancel buttons
15811          * @type Object
15812          */
15813         YESNOCANCEL : {yes:true, no:true, cancel:true},
15814
15815         /**
15816          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15817          * @type Number
15818          */
15819         defaultTextHeight : 75,
15820         /**
15821          * The maximum width in pixels of the message box (defaults to 600)
15822          * @type Number
15823          */
15824         maxWidth : 600,
15825         /**
15826          * The minimum width in pixels of the message box (defaults to 100)
15827          * @type Number
15828          */
15829         minWidth : 100,
15830         /**
15831          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15832          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15833          * @type Number
15834          */
15835         minProgressWidth : 250,
15836         /**
15837          * An object containing the default button text strings that can be overriden for localized language support.
15838          * Supported properties are: ok, cancel, yes and no.
15839          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15840          * @type Object
15841          */
15842         buttonText : {
15843             ok : "OK",
15844             cancel : "Cancel",
15845             yes : "Yes",
15846             no : "No"
15847         }
15848     };
15849 }();
15850
15851 /**
15852  * Shorthand for {@link Roo.MessageBox}
15853  */
15854 Roo.Msg = Roo.MessageBox;/*
15855  * Based on:
15856  * Ext JS Library 1.1.1
15857  * Copyright(c) 2006-2007, Ext JS, LLC.
15858  *
15859  * Originally Released Under LGPL - original licence link has changed is not relivant.
15860  *
15861  * Fork - LGPL
15862  * <script type="text/javascript">
15863  */
15864 /**
15865  * @class Roo.QuickTips
15866  * Provides attractive and customizable tooltips for any element.
15867  * @singleton
15868  */
15869 Roo.QuickTips = function(){
15870     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15871     var ce, bd, xy, dd;
15872     var visible = false, disabled = true, inited = false;
15873     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15874     
15875     var onOver = function(e){
15876         if(disabled){
15877             return;
15878         }
15879         var t = e.getTarget();
15880         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15881             return;
15882         }
15883         if(ce && t == ce.el){
15884             clearTimeout(hideProc);
15885             return;
15886         }
15887         if(t && tagEls[t.id]){
15888             tagEls[t.id].el = t;
15889             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15890             return;
15891         }
15892         var ttp, et = Roo.fly(t);
15893         var ns = cfg.namespace;
15894         if(tm.interceptTitles && t.title){
15895             ttp = t.title;
15896             t.qtip = ttp;
15897             t.removeAttribute("title");
15898             e.preventDefault();
15899         }else{
15900             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15901         }
15902         if(ttp){
15903             showProc = show.defer(tm.showDelay, tm, [{
15904                 el: t, 
15905                 text: ttp, 
15906                 width: et.getAttributeNS(ns, cfg.width),
15907                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15908                 title: et.getAttributeNS(ns, cfg.title),
15909                     cls: et.getAttributeNS(ns, cfg.cls)
15910             }]);
15911         }
15912     };
15913     
15914     var onOut = function(e){
15915         clearTimeout(showProc);
15916         var t = e.getTarget();
15917         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15918             hideProc = setTimeout(hide, tm.hideDelay);
15919         }
15920     };
15921     
15922     var onMove = function(e){
15923         if(disabled){
15924             return;
15925         }
15926         xy = e.getXY();
15927         xy[1] += 18;
15928         if(tm.trackMouse && ce){
15929             el.setXY(xy);
15930         }
15931     };
15932     
15933     var onDown = function(e){
15934         clearTimeout(showProc);
15935         clearTimeout(hideProc);
15936         if(!e.within(el)){
15937             if(tm.hideOnClick){
15938                 hide();
15939                 tm.disable();
15940                 tm.enable.defer(100, tm);
15941             }
15942         }
15943     };
15944     
15945     var getPad = function(){
15946         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15947     };
15948
15949     var show = function(o){
15950         if(disabled){
15951             return;
15952         }
15953         clearTimeout(dismissProc);
15954         ce = o;
15955         if(removeCls){ // in case manually hidden
15956             el.removeClass(removeCls);
15957             removeCls = null;
15958         }
15959         if(ce.cls){
15960             el.addClass(ce.cls);
15961             removeCls = ce.cls;
15962         }
15963         if(ce.title){
15964             tipTitle.update(ce.title);
15965             tipTitle.show();
15966         }else{
15967             tipTitle.update('');
15968             tipTitle.hide();
15969         }
15970         el.dom.style.width  = tm.maxWidth+'px';
15971         //tipBody.dom.style.width = '';
15972         tipBodyText.update(o.text);
15973         var p = getPad(), w = ce.width;
15974         if(!w){
15975             var td = tipBodyText.dom;
15976             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15977             if(aw > tm.maxWidth){
15978                 w = tm.maxWidth;
15979             }else if(aw < tm.minWidth){
15980                 w = tm.minWidth;
15981             }else{
15982                 w = aw;
15983             }
15984         }
15985         //tipBody.setWidth(w);
15986         el.setWidth(parseInt(w, 10) + p);
15987         if(ce.autoHide === false){
15988             close.setDisplayed(true);
15989             if(dd){
15990                 dd.unlock();
15991             }
15992         }else{
15993             close.setDisplayed(false);
15994             if(dd){
15995                 dd.lock();
15996             }
15997         }
15998         if(xy){
15999             el.avoidY = xy[1]-18;
16000             el.setXY(xy);
16001         }
16002         if(tm.animate){
16003             el.setOpacity(.1);
16004             el.setStyle("visibility", "visible");
16005             el.fadeIn({callback: afterShow});
16006         }else{
16007             afterShow();
16008         }
16009     };
16010     
16011     var afterShow = function(){
16012         if(ce){
16013             el.show();
16014             esc.enable();
16015             if(tm.autoDismiss && ce.autoHide !== false){
16016                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16017             }
16018         }
16019     };
16020     
16021     var hide = function(noanim){
16022         clearTimeout(dismissProc);
16023         clearTimeout(hideProc);
16024         ce = null;
16025         if(el.isVisible()){
16026             esc.disable();
16027             if(noanim !== true && tm.animate){
16028                 el.fadeOut({callback: afterHide});
16029             }else{
16030                 afterHide();
16031             } 
16032         }
16033     };
16034     
16035     var afterHide = function(){
16036         el.hide();
16037         if(removeCls){
16038             el.removeClass(removeCls);
16039             removeCls = null;
16040         }
16041     };
16042     
16043     return {
16044         /**
16045         * @cfg {Number} minWidth
16046         * The minimum width of the quick tip (defaults to 40)
16047         */
16048        minWidth : 40,
16049         /**
16050         * @cfg {Number} maxWidth
16051         * The maximum width of the quick tip (defaults to 300)
16052         */
16053        maxWidth : 300,
16054         /**
16055         * @cfg {Boolean} interceptTitles
16056         * True to automatically use the element's DOM title value if available (defaults to false)
16057         */
16058        interceptTitles : false,
16059         /**
16060         * @cfg {Boolean} trackMouse
16061         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16062         */
16063        trackMouse : false,
16064         /**
16065         * @cfg {Boolean} hideOnClick
16066         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16067         */
16068        hideOnClick : true,
16069         /**
16070         * @cfg {Number} showDelay
16071         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16072         */
16073        showDelay : 500,
16074         /**
16075         * @cfg {Number} hideDelay
16076         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16077         */
16078        hideDelay : 200,
16079         /**
16080         * @cfg {Boolean} autoHide
16081         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16082         * Used in conjunction with hideDelay.
16083         */
16084        autoHide : true,
16085         /**
16086         * @cfg {Boolean}
16087         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16088         * (defaults to true).  Used in conjunction with autoDismissDelay.
16089         */
16090        autoDismiss : true,
16091         /**
16092         * @cfg {Number}
16093         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16094         */
16095        autoDismissDelay : 5000,
16096        /**
16097         * @cfg {Boolean} animate
16098         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16099         */
16100        animate : false,
16101
16102        /**
16103         * @cfg {String} title
16104         * Title text to display (defaults to '').  This can be any valid HTML markup.
16105         */
16106         title: '',
16107        /**
16108         * @cfg {String} text
16109         * Body text to display (defaults to '').  This can be any valid HTML markup.
16110         */
16111         text : '',
16112        /**
16113         * @cfg {String} cls
16114         * A CSS class to apply to the base quick tip element (defaults to '').
16115         */
16116         cls : '',
16117        /**
16118         * @cfg {Number} width
16119         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16120         * minWidth or maxWidth.
16121         */
16122         width : null,
16123
16124     /**
16125      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16126      * or display QuickTips in a page.
16127      */
16128        init : function(){
16129           tm = Roo.QuickTips;
16130           cfg = tm.tagConfig;
16131           if(!inited){
16132               if(!Roo.isReady){ // allow calling of init() before onReady
16133                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16134                   return;
16135               }
16136               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16137               el.fxDefaults = {stopFx: true};
16138               // maximum custom styling
16139               //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>');
16140               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>');              
16141               tipTitle = el.child('h3');
16142               tipTitle.enableDisplayMode("block");
16143               tipBody = el.child('div.x-tip-bd');
16144               tipBodyText = el.child('div.x-tip-bd-inner');
16145               //bdLeft = el.child('div.x-tip-bd-left');
16146               //bdRight = el.child('div.x-tip-bd-right');
16147               close = el.child('div.x-tip-close');
16148               close.enableDisplayMode("block");
16149               close.on("click", hide);
16150               var d = Roo.get(document);
16151               d.on("mousedown", onDown);
16152               d.on("mouseover", onOver);
16153               d.on("mouseout", onOut);
16154               d.on("mousemove", onMove);
16155               esc = d.addKeyListener(27, hide);
16156               esc.disable();
16157               if(Roo.dd.DD){
16158                   dd = el.initDD("default", null, {
16159                       onDrag : function(){
16160                           el.sync();  
16161                       }
16162                   });
16163                   dd.setHandleElId(tipTitle.id);
16164                   dd.lock();
16165               }
16166               inited = true;
16167           }
16168           this.enable(); 
16169        },
16170
16171     /**
16172      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16173      * are supported:
16174      * <pre>
16175 Property    Type                   Description
16176 ----------  ---------------------  ------------------------------------------------------------------------
16177 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16178      * </ul>
16179      * @param {Object} config The config object
16180      */
16181        register : function(config){
16182            var cs = config instanceof Array ? config : arguments;
16183            for(var i = 0, len = cs.length; i < len; i++) {
16184                var c = cs[i];
16185                var target = c.target;
16186                if(target){
16187                    if(target instanceof Array){
16188                        for(var j = 0, jlen = target.length; j < jlen; j++){
16189                            tagEls[target[j]] = c;
16190                        }
16191                    }else{
16192                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16193                    }
16194                }
16195            }
16196        },
16197
16198     /**
16199      * Removes this quick tip from its element and destroys it.
16200      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16201      */
16202        unregister : function(el){
16203            delete tagEls[Roo.id(el)];
16204        },
16205
16206     /**
16207      * Enable this quick tip.
16208      */
16209        enable : function(){
16210            if(inited && disabled){
16211                locks.pop();
16212                if(locks.length < 1){
16213                    disabled = false;
16214                }
16215            }
16216        },
16217
16218     /**
16219      * Disable this quick tip.
16220      */
16221        disable : function(){
16222           disabled = true;
16223           clearTimeout(showProc);
16224           clearTimeout(hideProc);
16225           clearTimeout(dismissProc);
16226           if(ce){
16227               hide(true);
16228           }
16229           locks.push(1);
16230        },
16231
16232     /**
16233      * Returns true if the quick tip is enabled, else false.
16234      */
16235        isEnabled : function(){
16236             return !disabled;
16237        },
16238
16239         // private
16240        tagConfig : {
16241            namespace : "ext",
16242            attribute : "qtip",
16243            width : "width",
16244            target : "target",
16245            title : "qtitle",
16246            hide : "hide",
16247            cls : "qclass"
16248        }
16249    };
16250 }();
16251
16252 // backwards compat
16253 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16254  * Based on:
16255  * Ext JS Library 1.1.1
16256  * Copyright(c) 2006-2007, Ext JS, LLC.
16257  *
16258  * Originally Released Under LGPL - original licence link has changed is not relivant.
16259  *
16260  * Fork - LGPL
16261  * <script type="text/javascript">
16262  */
16263  
16264
16265 /**
16266  * @class Roo.tree.TreePanel
16267  * @extends Roo.data.Tree
16268
16269  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16270  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16271  * @cfg {Boolean} enableDD true to enable drag and drop
16272  * @cfg {Boolean} enableDrag true to enable just drag
16273  * @cfg {Boolean} enableDrop true to enable just drop
16274  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16275  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16276  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16277  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16278  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16279  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16280  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16281  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16282  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16283  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16284  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16285  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16286  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16287  * @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>
16288  * @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>
16289  * 
16290  * @constructor
16291  * @param {String/HTMLElement/Element} el The container element
16292  * @param {Object} config
16293  */
16294 Roo.tree.TreePanel = function(el, config){
16295     var root = false;
16296     var loader = false;
16297     if (config.root) {
16298         root = config.root;
16299         delete config.root;
16300     }
16301     if (config.loader) {
16302         loader = config.loader;
16303         delete config.loader;
16304     }
16305     
16306     Roo.apply(this, config);
16307     Roo.tree.TreePanel.superclass.constructor.call(this);
16308     this.el = Roo.get(el);
16309     this.el.addClass('x-tree');
16310     //console.log(root);
16311     if (root) {
16312         this.setRootNode( Roo.factory(root, Roo.tree));
16313     }
16314     if (loader) {
16315         this.loader = Roo.factory(loader, Roo.tree);
16316     }
16317    /**
16318     * Read-only. The id of the container element becomes this TreePanel's id.
16319     */
16320    this.id = this.el.id;
16321    this.addEvents({
16322         /**
16323         * @event beforeload
16324         * Fires before a node is loaded, return false to cancel
16325         * @param {Node} node The node being loaded
16326         */
16327         "beforeload" : true,
16328         /**
16329         * @event load
16330         * Fires when a node is loaded
16331         * @param {Node} node The node that was loaded
16332         */
16333         "load" : true,
16334         /**
16335         * @event textchange
16336         * Fires when the text for a node is changed
16337         * @param {Node} node The node
16338         * @param {String} text The new text
16339         * @param {String} oldText The old text
16340         */
16341         "textchange" : true,
16342         /**
16343         * @event beforeexpand
16344         * Fires before a node is expanded, return false to cancel.
16345         * @param {Node} node The node
16346         * @param {Boolean} deep
16347         * @param {Boolean} anim
16348         */
16349         "beforeexpand" : true,
16350         /**
16351         * @event beforecollapse
16352         * Fires before a node is collapsed, return false to cancel.
16353         * @param {Node} node The node
16354         * @param {Boolean} deep
16355         * @param {Boolean} anim
16356         */
16357         "beforecollapse" : true,
16358         /**
16359         * @event expand
16360         * Fires when a node is expanded
16361         * @param {Node} node The node
16362         */
16363         "expand" : true,
16364         /**
16365         * @event disabledchange
16366         * Fires when the disabled status of a node changes
16367         * @param {Node} node The node
16368         * @param {Boolean} disabled
16369         */
16370         "disabledchange" : true,
16371         /**
16372         * @event collapse
16373         * Fires when a node is collapsed
16374         * @param {Node} node The node
16375         */
16376         "collapse" : true,
16377         /**
16378         * @event beforeclick
16379         * Fires before click processing on a node. Return false to cancel the default action.
16380         * @param {Node} node The node
16381         * @param {Roo.EventObject} e The event object
16382         */
16383         "beforeclick":true,
16384         /**
16385         * @event checkchange
16386         * Fires when a node with a checkbox's checked property changes
16387         * @param {Node} this This node
16388         * @param {Boolean} checked
16389         */
16390         "checkchange":true,
16391         /**
16392         * @event click
16393         * Fires when a node is clicked
16394         * @param {Node} node The node
16395         * @param {Roo.EventObject} e The event object
16396         */
16397         "click":true,
16398         /**
16399         * @event dblclick
16400         * Fires when a node is double clicked
16401         * @param {Node} node The node
16402         * @param {Roo.EventObject} e The event object
16403         */
16404         "dblclick":true,
16405         /**
16406         * @event contextmenu
16407         * Fires when a node is right clicked
16408         * @param {Node} node The node
16409         * @param {Roo.EventObject} e The event object
16410         */
16411         "contextmenu":true,
16412         /**
16413         * @event beforechildrenrendered
16414         * Fires right before the child nodes for a node are rendered
16415         * @param {Node} node The node
16416         */
16417         "beforechildrenrendered":true,
16418        /**
16419              * @event startdrag
16420              * Fires when a node starts being dragged
16421              * @param {Roo.tree.TreePanel} this
16422              * @param {Roo.tree.TreeNode} node
16423              * @param {event} e The raw browser event
16424              */ 
16425             "startdrag" : true,
16426             /**
16427              * @event enddrag
16428              * Fires when a drag operation is complete
16429              * @param {Roo.tree.TreePanel} this
16430              * @param {Roo.tree.TreeNode} node
16431              * @param {event} e The raw browser event
16432              */
16433             "enddrag" : true,
16434             /**
16435              * @event dragdrop
16436              * Fires when a dragged node is dropped on a valid DD target
16437              * @param {Roo.tree.TreePanel} this
16438              * @param {Roo.tree.TreeNode} node
16439              * @param {DD} dd The dd it was dropped on
16440              * @param {event} e The raw browser event
16441              */
16442             "dragdrop" : true,
16443             /**
16444              * @event beforenodedrop
16445              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16446              * passed to handlers has the following properties:<br />
16447              * <ul style="padding:5px;padding-left:16px;">
16448              * <li>tree - The TreePanel</li>
16449              * <li>target - The node being targeted for the drop</li>
16450              * <li>data - The drag data from the drag source</li>
16451              * <li>point - The point of the drop - append, above or below</li>
16452              * <li>source - The drag source</li>
16453              * <li>rawEvent - Raw mouse event</li>
16454              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16455              * to be inserted by setting them on this object.</li>
16456              * <li>cancel - Set this to true to cancel the drop.</li>
16457              * </ul>
16458              * @param {Object} dropEvent
16459              */
16460             "beforenodedrop" : true,
16461             /**
16462              * @event nodedrop
16463              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16464              * passed to handlers has the following properties:<br />
16465              * <ul style="padding:5px;padding-left:16px;">
16466              * <li>tree - The TreePanel</li>
16467              * <li>target - The node being targeted for the drop</li>
16468              * <li>data - The drag data from the drag source</li>
16469              * <li>point - The point of the drop - append, above or below</li>
16470              * <li>source - The drag source</li>
16471              * <li>rawEvent - Raw mouse event</li>
16472              * <li>dropNode - Dropped node(s).</li>
16473              * </ul>
16474              * @param {Object} dropEvent
16475              */
16476             "nodedrop" : true,
16477              /**
16478              * @event nodedragover
16479              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16480              * passed to handlers has the following properties:<br />
16481              * <ul style="padding:5px;padding-left:16px;">
16482              * <li>tree - The TreePanel</li>
16483              * <li>target - The node being targeted for the drop</li>
16484              * <li>data - The drag data from the drag source</li>
16485              * <li>point - The point of the drop - append, above or below</li>
16486              * <li>source - The drag source</li>
16487              * <li>rawEvent - Raw mouse event</li>
16488              * <li>dropNode - Drop node(s) provided by the source.</li>
16489              * <li>cancel - Set this to true to signal drop not allowed.</li>
16490              * </ul>
16491              * @param {Object} dragOverEvent
16492              */
16493             "nodedragover" : true
16494         
16495    });
16496    if(this.singleExpand){
16497        this.on("beforeexpand", this.restrictExpand, this);
16498    }
16499 };
16500 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16501     rootVisible : true,
16502     animate: Roo.enableFx,
16503     lines : true,
16504     enableDD : false,
16505     hlDrop : Roo.enableFx,
16506   
16507     renderer: false,
16508     
16509     rendererTip: false,
16510     // private
16511     restrictExpand : function(node){
16512         var p = node.parentNode;
16513         if(p){
16514             if(p.expandedChild && p.expandedChild.parentNode == p){
16515                 p.expandedChild.collapse();
16516             }
16517             p.expandedChild = node;
16518         }
16519     },
16520
16521     // private override
16522     setRootNode : function(node){
16523         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16524         if(!this.rootVisible){
16525             node.ui = new Roo.tree.RootTreeNodeUI(node);
16526         }
16527         return node;
16528     },
16529
16530     /**
16531      * Returns the container element for this TreePanel
16532      */
16533     getEl : function(){
16534         return this.el;
16535     },
16536
16537     /**
16538      * Returns the default TreeLoader for this TreePanel
16539      */
16540     getLoader : function(){
16541         return this.loader;
16542     },
16543
16544     /**
16545      * Expand all nodes
16546      */
16547     expandAll : function(){
16548         this.root.expand(true);
16549     },
16550
16551     /**
16552      * Collapse all nodes
16553      */
16554     collapseAll : function(){
16555         this.root.collapse(true);
16556     },
16557
16558     /**
16559      * Returns the selection model used by this TreePanel
16560      */
16561     getSelectionModel : function(){
16562         if(!this.selModel){
16563             this.selModel = new Roo.tree.DefaultSelectionModel();
16564         }
16565         return this.selModel;
16566     },
16567
16568     /**
16569      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16570      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16571      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16572      * @return {Array}
16573      */
16574     getChecked : function(a, startNode){
16575         startNode = startNode || this.root;
16576         var r = [];
16577         var f = function(){
16578             if(this.attributes.checked){
16579                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16580             }
16581         }
16582         startNode.cascade(f);
16583         return r;
16584     },
16585
16586     /**
16587      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16588      * @param {String} path
16589      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16590      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16591      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16592      */
16593     expandPath : function(path, attr, callback){
16594         attr = attr || "id";
16595         var keys = path.split(this.pathSeparator);
16596         var curNode = this.root;
16597         if(curNode.attributes[attr] != keys[1]){ // invalid root
16598             if(callback){
16599                 callback(false, null);
16600             }
16601             return;
16602         }
16603         var index = 1;
16604         var f = function(){
16605             if(++index == keys.length){
16606                 if(callback){
16607                     callback(true, curNode);
16608                 }
16609                 return;
16610             }
16611             var c = curNode.findChild(attr, keys[index]);
16612             if(!c){
16613                 if(callback){
16614                     callback(false, curNode);
16615                 }
16616                 return;
16617             }
16618             curNode = c;
16619             c.expand(false, false, f);
16620         };
16621         curNode.expand(false, false, f);
16622     },
16623
16624     /**
16625      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16626      * @param {String} path
16627      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16628      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16629      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16630      */
16631     selectPath : function(path, attr, callback){
16632         attr = attr || "id";
16633         var keys = path.split(this.pathSeparator);
16634         var v = keys.pop();
16635         if(keys.length > 0){
16636             var f = function(success, node){
16637                 if(success && node){
16638                     var n = node.findChild(attr, v);
16639                     if(n){
16640                         n.select();
16641                         if(callback){
16642                             callback(true, n);
16643                         }
16644                     }else if(callback){
16645                         callback(false, n);
16646                     }
16647                 }else{
16648                     if(callback){
16649                         callback(false, n);
16650                     }
16651                 }
16652             };
16653             this.expandPath(keys.join(this.pathSeparator), attr, f);
16654         }else{
16655             this.root.select();
16656             if(callback){
16657                 callback(true, this.root);
16658             }
16659         }
16660     },
16661
16662     getTreeEl : function(){
16663         return this.el;
16664     },
16665
16666     /**
16667      * Trigger rendering of this TreePanel
16668      */
16669     render : function(){
16670         if (this.innerCt) {
16671             return this; // stop it rendering more than once!!
16672         }
16673         
16674         this.innerCt = this.el.createChild({tag:"ul",
16675                cls:"x-tree-root-ct " +
16676                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16677
16678         if(this.containerScroll){
16679             Roo.dd.ScrollManager.register(this.el);
16680         }
16681         if((this.enableDD || this.enableDrop) && !this.dropZone){
16682            /**
16683             * The dropZone used by this tree if drop is enabled
16684             * @type Roo.tree.TreeDropZone
16685             */
16686              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16687                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16688            });
16689         }
16690         if((this.enableDD || this.enableDrag) && !this.dragZone){
16691            /**
16692             * The dragZone used by this tree if drag is enabled
16693             * @type Roo.tree.TreeDragZone
16694             */
16695             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16696                ddGroup: this.ddGroup || "TreeDD",
16697                scroll: this.ddScroll
16698            });
16699         }
16700         this.getSelectionModel().init(this);
16701         if (!this.root) {
16702             console.log("ROOT not set in tree");
16703             return;
16704         }
16705         this.root.render();
16706         if(!this.rootVisible){
16707             this.root.renderChildren();
16708         }
16709         return this;
16710     }
16711 });/*
16712  * Based on:
16713  * Ext JS Library 1.1.1
16714  * Copyright(c) 2006-2007, Ext JS, LLC.
16715  *
16716  * Originally Released Under LGPL - original licence link has changed is not relivant.
16717  *
16718  * Fork - LGPL
16719  * <script type="text/javascript">
16720  */
16721  
16722
16723 /**
16724  * @class Roo.tree.DefaultSelectionModel
16725  * @extends Roo.util.Observable
16726  * The default single selection for a TreePanel.
16727  */
16728 Roo.tree.DefaultSelectionModel = function(){
16729    this.selNode = null;
16730    
16731    this.addEvents({
16732        /**
16733         * @event selectionchange
16734         * Fires when the selected node changes
16735         * @param {DefaultSelectionModel} this
16736         * @param {TreeNode} node the new selection
16737         */
16738        "selectionchange" : true,
16739
16740        /**
16741         * @event beforeselect
16742         * Fires before the selected node changes, return false to cancel the change
16743         * @param {DefaultSelectionModel} this
16744         * @param {TreeNode} node the new selection
16745         * @param {TreeNode} node the old selection
16746         */
16747        "beforeselect" : true
16748    });
16749 };
16750
16751 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16752     init : function(tree){
16753         this.tree = tree;
16754         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16755         tree.on("click", this.onNodeClick, this);
16756     },
16757     
16758     onNodeClick : function(node, e){
16759         if (e.ctrlKey && this.selNode == node)  {
16760             this.unselect(node);
16761             return;
16762         }
16763         this.select(node);
16764     },
16765     
16766     /**
16767      * Select a node.
16768      * @param {TreeNode} node The node to select
16769      * @return {TreeNode} The selected node
16770      */
16771     select : function(node){
16772         var last = this.selNode;
16773         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16774             if(last){
16775                 last.ui.onSelectedChange(false);
16776             }
16777             this.selNode = node;
16778             node.ui.onSelectedChange(true);
16779             this.fireEvent("selectionchange", this, node, last);
16780         }
16781         return node;
16782     },
16783     
16784     /**
16785      * Deselect a node.
16786      * @param {TreeNode} node The node to unselect
16787      */
16788     unselect : function(node){
16789         if(this.selNode == node){
16790             this.clearSelections();
16791         }    
16792     },
16793     
16794     /**
16795      * Clear all selections
16796      */
16797     clearSelections : function(){
16798         var n = this.selNode;
16799         if(n){
16800             n.ui.onSelectedChange(false);
16801             this.selNode = null;
16802             this.fireEvent("selectionchange", this, null);
16803         }
16804         return n;
16805     },
16806     
16807     /**
16808      * Get the selected node
16809      * @return {TreeNode} The selected node
16810      */
16811     getSelectedNode : function(){
16812         return this.selNode;    
16813     },
16814     
16815     /**
16816      * Returns true if the node is selected
16817      * @param {TreeNode} node The node to check
16818      * @return {Boolean}
16819      */
16820     isSelected : function(node){
16821         return this.selNode == node;  
16822     },
16823
16824     /**
16825      * Selects the node above the selected node in the tree, intelligently walking the nodes
16826      * @return TreeNode The new selection
16827      */
16828     selectPrevious : function(){
16829         var s = this.selNode || this.lastSelNode;
16830         if(!s){
16831             return null;
16832         }
16833         var ps = s.previousSibling;
16834         if(ps){
16835             if(!ps.isExpanded() || ps.childNodes.length < 1){
16836                 return this.select(ps);
16837             } else{
16838                 var lc = ps.lastChild;
16839                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16840                     lc = lc.lastChild;
16841                 }
16842                 return this.select(lc);
16843             }
16844         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16845             return this.select(s.parentNode);
16846         }
16847         return null;
16848     },
16849
16850     /**
16851      * Selects the node above the selected node in the tree, intelligently walking the nodes
16852      * @return TreeNode The new selection
16853      */
16854     selectNext : function(){
16855         var s = this.selNode || this.lastSelNode;
16856         if(!s){
16857             return null;
16858         }
16859         if(s.firstChild && s.isExpanded()){
16860              return this.select(s.firstChild);
16861          }else if(s.nextSibling){
16862              return this.select(s.nextSibling);
16863          }else if(s.parentNode){
16864             var newS = null;
16865             s.parentNode.bubble(function(){
16866                 if(this.nextSibling){
16867                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16868                     return false;
16869                 }
16870             });
16871             return newS;
16872          }
16873         return null;
16874     },
16875
16876     onKeyDown : function(e){
16877         var s = this.selNode || this.lastSelNode;
16878         // undesirable, but required
16879         var sm = this;
16880         if(!s){
16881             return;
16882         }
16883         var k = e.getKey();
16884         switch(k){
16885              case e.DOWN:
16886                  e.stopEvent();
16887                  this.selectNext();
16888              break;
16889              case e.UP:
16890                  e.stopEvent();
16891                  this.selectPrevious();
16892              break;
16893              case e.RIGHT:
16894                  e.preventDefault();
16895                  if(s.hasChildNodes()){
16896                      if(!s.isExpanded()){
16897                          s.expand();
16898                      }else if(s.firstChild){
16899                          this.select(s.firstChild, e);
16900                      }
16901                  }
16902              break;
16903              case e.LEFT:
16904                  e.preventDefault();
16905                  if(s.hasChildNodes() && s.isExpanded()){
16906                      s.collapse();
16907                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16908                      this.select(s.parentNode, e);
16909                  }
16910              break;
16911         };
16912     }
16913 });
16914
16915 /**
16916  * @class Roo.tree.MultiSelectionModel
16917  * @extends Roo.util.Observable
16918  * Multi selection for a TreePanel.
16919  */
16920 Roo.tree.MultiSelectionModel = function(){
16921    this.selNodes = [];
16922    this.selMap = {};
16923    this.addEvents({
16924        /**
16925         * @event selectionchange
16926         * Fires when the selected nodes change
16927         * @param {MultiSelectionModel} this
16928         * @param {Array} nodes Array of the selected nodes
16929         */
16930        "selectionchange" : true
16931    });
16932 };
16933
16934 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16935     init : function(tree){
16936         this.tree = tree;
16937         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16938         tree.on("click", this.onNodeClick, this);
16939     },
16940     
16941     onNodeClick : function(node, e){
16942         this.select(node, e, e.ctrlKey);
16943     },
16944     
16945     /**
16946      * Select a node.
16947      * @param {TreeNode} node The node to select
16948      * @param {EventObject} e (optional) An event associated with the selection
16949      * @param {Boolean} keepExisting True to retain existing selections
16950      * @return {TreeNode} The selected node
16951      */
16952     select : function(node, e, keepExisting){
16953         if(keepExisting !== true){
16954             this.clearSelections(true);
16955         }
16956         if(this.isSelected(node)){
16957             this.lastSelNode = node;
16958             return node;
16959         }
16960         this.selNodes.push(node);
16961         this.selMap[node.id] = node;
16962         this.lastSelNode = node;
16963         node.ui.onSelectedChange(true);
16964         this.fireEvent("selectionchange", this, this.selNodes);
16965         return node;
16966     },
16967     
16968     /**
16969      * Deselect a node.
16970      * @param {TreeNode} node The node to unselect
16971      */
16972     unselect : function(node){
16973         if(this.selMap[node.id]){
16974             node.ui.onSelectedChange(false);
16975             var sn = this.selNodes;
16976             var index = -1;
16977             if(sn.indexOf){
16978                 index = sn.indexOf(node);
16979             }else{
16980                 for(var i = 0, len = sn.length; i < len; i++){
16981                     if(sn[i] == node){
16982                         index = i;
16983                         break;
16984                     }
16985                 }
16986             }
16987             if(index != -1){
16988                 this.selNodes.splice(index, 1);
16989             }
16990             delete this.selMap[node.id];
16991             this.fireEvent("selectionchange", this, this.selNodes);
16992         }
16993     },
16994     
16995     /**
16996      * Clear all selections
16997      */
16998     clearSelections : function(suppressEvent){
16999         var sn = this.selNodes;
17000         if(sn.length > 0){
17001             for(var i = 0, len = sn.length; i < len; i++){
17002                 sn[i].ui.onSelectedChange(false);
17003             }
17004             this.selNodes = [];
17005             this.selMap = {};
17006             if(suppressEvent !== true){
17007                 this.fireEvent("selectionchange", this, this.selNodes);
17008             }
17009         }
17010     },
17011     
17012     /**
17013      * Returns true if the node is selected
17014      * @param {TreeNode} node The node to check
17015      * @return {Boolean}
17016      */
17017     isSelected : function(node){
17018         return this.selMap[node.id] ? true : false;  
17019     },
17020     
17021     /**
17022      * Returns an array of the selected nodes
17023      * @return {Array}
17024      */
17025     getSelectedNodes : function(){
17026         return this.selNodes;    
17027     },
17028
17029     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17030
17031     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17032
17033     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17034 });/*
17035  * Based on:
17036  * Ext JS Library 1.1.1
17037  * Copyright(c) 2006-2007, Ext JS, LLC.
17038  *
17039  * Originally Released Under LGPL - original licence link has changed is not relivant.
17040  *
17041  * Fork - LGPL
17042  * <script type="text/javascript">
17043  */
17044  
17045 /**
17046  * @class Roo.tree.TreeNode
17047  * @extends Roo.data.Node
17048  * @cfg {String} text The text for this node
17049  * @cfg {Boolean} expanded true to start the node expanded
17050  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17051  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17052  * @cfg {Boolean} disabled true to start the node disabled
17053  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17054  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17055  * @cfg {String} cls A css class to be added to the node
17056  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17057  * @cfg {String} href URL of the link used for the node (defaults to #)
17058  * @cfg {String} hrefTarget target frame for the link
17059  * @cfg {String} qtip An Ext QuickTip for the node
17060  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17061  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17062  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17063  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17064  * (defaults to undefined with no checkbox rendered)
17065  * @constructor
17066  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17067  */
17068 Roo.tree.TreeNode = function(attributes){
17069     attributes = attributes || {};
17070     if(typeof attributes == "string"){
17071         attributes = {text: attributes};
17072     }
17073     this.childrenRendered = false;
17074     this.rendered = false;
17075     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17076     this.expanded = attributes.expanded === true;
17077     this.isTarget = attributes.isTarget !== false;
17078     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17079     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17080
17081     /**
17082      * Read-only. The text for this node. To change it use setText().
17083      * @type String
17084      */
17085     this.text = attributes.text;
17086     /**
17087      * True if this node is disabled.
17088      * @type Boolean
17089      */
17090     this.disabled = attributes.disabled === true;
17091
17092     this.addEvents({
17093         /**
17094         * @event textchange
17095         * Fires when the text for this node is changed
17096         * @param {Node} this This node
17097         * @param {String} text The new text
17098         * @param {String} oldText The old text
17099         */
17100         "textchange" : true,
17101         /**
17102         * @event beforeexpand
17103         * Fires before this node is expanded, return false to cancel.
17104         * @param {Node} this This node
17105         * @param {Boolean} deep
17106         * @param {Boolean} anim
17107         */
17108         "beforeexpand" : true,
17109         /**
17110         * @event beforecollapse
17111         * Fires before this node is collapsed, return false to cancel.
17112         * @param {Node} this This node
17113         * @param {Boolean} deep
17114         * @param {Boolean} anim
17115         */
17116         "beforecollapse" : true,
17117         /**
17118         * @event expand
17119         * Fires when this node is expanded
17120         * @param {Node} this This node
17121         */
17122         "expand" : true,
17123         /**
17124         * @event disabledchange
17125         * Fires when the disabled status of this node changes
17126         * @param {Node} this This node
17127         * @param {Boolean} disabled
17128         */
17129         "disabledchange" : true,
17130         /**
17131         * @event collapse
17132         * Fires when this node is collapsed
17133         * @param {Node} this This node
17134         */
17135         "collapse" : true,
17136         /**
17137         * @event beforeclick
17138         * Fires before click processing. Return false to cancel the default action.
17139         * @param {Node} this This node
17140         * @param {Roo.EventObject} e The event object
17141         */
17142         "beforeclick":true,
17143         /**
17144         * @event checkchange
17145         * Fires when a node with a checkbox's checked property changes
17146         * @param {Node} this This node
17147         * @param {Boolean} checked
17148         */
17149         "checkchange":true,
17150         /**
17151         * @event click
17152         * Fires when this node is clicked
17153         * @param {Node} this This node
17154         * @param {Roo.EventObject} e The event object
17155         */
17156         "click":true,
17157         /**
17158         * @event dblclick
17159         * Fires when this node is double clicked
17160         * @param {Node} this This node
17161         * @param {Roo.EventObject} e The event object
17162         */
17163         "dblclick":true,
17164         /**
17165         * @event contextmenu
17166         * Fires when this node is right clicked
17167         * @param {Node} this This node
17168         * @param {Roo.EventObject} e The event object
17169         */
17170         "contextmenu":true,
17171         /**
17172         * @event beforechildrenrendered
17173         * Fires right before the child nodes for this node are rendered
17174         * @param {Node} this This node
17175         */
17176         "beforechildrenrendered":true
17177     });
17178
17179     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17180
17181     /**
17182      * Read-only. The UI for this node
17183      * @type TreeNodeUI
17184      */
17185     this.ui = new uiClass(this);
17186 };
17187 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17188     preventHScroll: true,
17189     /**
17190      * Returns true if this node is expanded
17191      * @return {Boolean}
17192      */
17193     isExpanded : function(){
17194         return this.expanded;
17195     },
17196
17197     /**
17198      * Returns the UI object for this node
17199      * @return {TreeNodeUI}
17200      */
17201     getUI : function(){
17202         return this.ui;
17203     },
17204
17205     // private override
17206     setFirstChild : function(node){
17207         var of = this.firstChild;
17208         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17209         if(this.childrenRendered && of && node != of){
17210             of.renderIndent(true, true);
17211         }
17212         if(this.rendered){
17213             this.renderIndent(true, true);
17214         }
17215     },
17216
17217     // private override
17218     setLastChild : function(node){
17219         var ol = this.lastChild;
17220         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17221         if(this.childrenRendered && ol && node != ol){
17222             ol.renderIndent(true, true);
17223         }
17224         if(this.rendered){
17225             this.renderIndent(true, true);
17226         }
17227     },
17228
17229     // these methods are overridden to provide lazy rendering support
17230     // private override
17231     appendChild : function(){
17232         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17233         if(node && this.childrenRendered){
17234             node.render();
17235         }
17236         this.ui.updateExpandIcon();
17237         return node;
17238     },
17239
17240     // private override
17241     removeChild : function(node){
17242         this.ownerTree.getSelectionModel().unselect(node);
17243         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17244         // if it's been rendered remove dom node
17245         if(this.childrenRendered){
17246             node.ui.remove();
17247         }
17248         if(this.childNodes.length < 1){
17249             this.collapse(false, false);
17250         }else{
17251             this.ui.updateExpandIcon();
17252         }
17253         if(!this.firstChild) {
17254             this.childrenRendered = false;
17255         }
17256         return node;
17257     },
17258
17259     // private override
17260     insertBefore : function(node, refNode){
17261         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17262         if(newNode && refNode && this.childrenRendered){
17263             node.render();
17264         }
17265         this.ui.updateExpandIcon();
17266         return newNode;
17267     },
17268
17269     /**
17270      * Sets the text for this node
17271      * @param {String} text
17272      */
17273     setText : function(text){
17274         var oldText = this.text;
17275         this.text = text;
17276         this.attributes.text = text;
17277         if(this.rendered){ // event without subscribing
17278             this.ui.onTextChange(this, text, oldText);
17279         }
17280         this.fireEvent("textchange", this, text, oldText);
17281     },
17282
17283     /**
17284      * Triggers selection of this node
17285      */
17286     select : function(){
17287         this.getOwnerTree().getSelectionModel().select(this);
17288     },
17289
17290     /**
17291      * Triggers deselection of this node
17292      */
17293     unselect : function(){
17294         this.getOwnerTree().getSelectionModel().unselect(this);
17295     },
17296
17297     /**
17298      * Returns true if this node is selected
17299      * @return {Boolean}
17300      */
17301     isSelected : function(){
17302         return this.getOwnerTree().getSelectionModel().isSelected(this);
17303     },
17304
17305     /**
17306      * Expand this node.
17307      * @param {Boolean} deep (optional) True to expand all children as well
17308      * @param {Boolean} anim (optional) false to cancel the default animation
17309      * @param {Function} callback (optional) A callback to be called when
17310      * expanding this node completes (does not wait for deep expand to complete).
17311      * Called with 1 parameter, this node.
17312      */
17313     expand : function(deep, anim, callback){
17314         if(!this.expanded){
17315             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17316                 return;
17317             }
17318             if(!this.childrenRendered){
17319                 this.renderChildren();
17320             }
17321             this.expanded = true;
17322             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17323                 this.ui.animExpand(function(){
17324                     this.fireEvent("expand", this);
17325                     if(typeof callback == "function"){
17326                         callback(this);
17327                     }
17328                     if(deep === true){
17329                         this.expandChildNodes(true);
17330                     }
17331                 }.createDelegate(this));
17332                 return;
17333             }else{
17334                 this.ui.expand();
17335                 this.fireEvent("expand", this);
17336                 if(typeof callback == "function"){
17337                     callback(this);
17338                 }
17339             }
17340         }else{
17341            if(typeof callback == "function"){
17342                callback(this);
17343            }
17344         }
17345         if(deep === true){
17346             this.expandChildNodes(true);
17347         }
17348     },
17349
17350     isHiddenRoot : function(){
17351         return this.isRoot && !this.getOwnerTree().rootVisible;
17352     },
17353
17354     /**
17355      * Collapse this node.
17356      * @param {Boolean} deep (optional) True to collapse all children as well
17357      * @param {Boolean} anim (optional) false to cancel the default animation
17358      */
17359     collapse : function(deep, anim){
17360         if(this.expanded && !this.isHiddenRoot()){
17361             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17362                 return;
17363             }
17364             this.expanded = false;
17365             if((this.getOwnerTree().animate && anim !== false) || anim){
17366                 this.ui.animCollapse(function(){
17367                     this.fireEvent("collapse", this);
17368                     if(deep === true){
17369                         this.collapseChildNodes(true);
17370                     }
17371                 }.createDelegate(this));
17372                 return;
17373             }else{
17374                 this.ui.collapse();
17375                 this.fireEvent("collapse", this);
17376             }
17377         }
17378         if(deep === true){
17379             var cs = this.childNodes;
17380             for(var i = 0, len = cs.length; i < len; i++) {
17381                 cs[i].collapse(true, false);
17382             }
17383         }
17384     },
17385
17386     // private
17387     delayedExpand : function(delay){
17388         if(!this.expandProcId){
17389             this.expandProcId = this.expand.defer(delay, this);
17390         }
17391     },
17392
17393     // private
17394     cancelExpand : function(){
17395         if(this.expandProcId){
17396             clearTimeout(this.expandProcId);
17397         }
17398         this.expandProcId = false;
17399     },
17400
17401     /**
17402      * Toggles expanded/collapsed state of the node
17403      */
17404     toggle : function(){
17405         if(this.expanded){
17406             this.collapse();
17407         }else{
17408             this.expand();
17409         }
17410     },
17411
17412     /**
17413      * Ensures all parent nodes are expanded
17414      */
17415     ensureVisible : function(callback){
17416         var tree = this.getOwnerTree();
17417         tree.expandPath(this.parentNode.getPath(), false, function(){
17418             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17419             Roo.callback(callback);
17420         }.createDelegate(this));
17421     },
17422
17423     /**
17424      * Expand all child nodes
17425      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17426      */
17427     expandChildNodes : function(deep){
17428         var cs = this.childNodes;
17429         for(var i = 0, len = cs.length; i < len; i++) {
17430                 cs[i].expand(deep);
17431         }
17432     },
17433
17434     /**
17435      * Collapse all child nodes
17436      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17437      */
17438     collapseChildNodes : function(deep){
17439         var cs = this.childNodes;
17440         for(var i = 0, len = cs.length; i < len; i++) {
17441                 cs[i].collapse(deep);
17442         }
17443     },
17444
17445     /**
17446      * Disables this node
17447      */
17448     disable : function(){
17449         this.disabled = true;
17450         this.unselect();
17451         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17452             this.ui.onDisableChange(this, true);
17453         }
17454         this.fireEvent("disabledchange", this, true);
17455     },
17456
17457     /**
17458      * Enables this node
17459      */
17460     enable : function(){
17461         this.disabled = false;
17462         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17463             this.ui.onDisableChange(this, false);
17464         }
17465         this.fireEvent("disabledchange", this, false);
17466     },
17467
17468     // private
17469     renderChildren : function(suppressEvent){
17470         if(suppressEvent !== false){
17471             this.fireEvent("beforechildrenrendered", this);
17472         }
17473         var cs = this.childNodes;
17474         for(var i = 0, len = cs.length; i < len; i++){
17475             cs[i].render(true);
17476         }
17477         this.childrenRendered = true;
17478     },
17479
17480     // private
17481     sort : function(fn, scope){
17482         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17483         if(this.childrenRendered){
17484             var cs = this.childNodes;
17485             for(var i = 0, len = cs.length; i < len; i++){
17486                 cs[i].render(true);
17487             }
17488         }
17489     },
17490
17491     // private
17492     render : function(bulkRender){
17493         this.ui.render(bulkRender);
17494         if(!this.rendered){
17495             this.rendered = true;
17496             if(this.expanded){
17497                 this.expanded = false;
17498                 this.expand(false, false);
17499             }
17500         }
17501     },
17502
17503     // private
17504     renderIndent : function(deep, refresh){
17505         if(refresh){
17506             this.ui.childIndent = null;
17507         }
17508         this.ui.renderIndent();
17509         if(deep === true && this.childrenRendered){
17510             var cs = this.childNodes;
17511             for(var i = 0, len = cs.length; i < len; i++){
17512                 cs[i].renderIndent(true, refresh);
17513             }
17514         }
17515     }
17516 });/*
17517  * Based on:
17518  * Ext JS Library 1.1.1
17519  * Copyright(c) 2006-2007, Ext JS, LLC.
17520  *
17521  * Originally Released Under LGPL - original licence link has changed is not relivant.
17522  *
17523  * Fork - LGPL
17524  * <script type="text/javascript">
17525  */
17526  
17527 /**
17528  * @class Roo.tree.AsyncTreeNode
17529  * @extends Roo.tree.TreeNode
17530  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17531  * @constructor
17532  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17533  */
17534  Roo.tree.AsyncTreeNode = function(config){
17535     this.loaded = false;
17536     this.loading = false;
17537     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17538     /**
17539     * @event beforeload
17540     * Fires before this node is loaded, return false to cancel
17541     * @param {Node} this This node
17542     */
17543     this.addEvents({'beforeload':true, 'load': true});
17544     /**
17545     * @event load
17546     * Fires when this node is loaded
17547     * @param {Node} this This node
17548     */
17549     /**
17550      * The loader used by this node (defaults to using the tree's defined loader)
17551      * @type TreeLoader
17552      * @property loader
17553      */
17554 };
17555 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17556     expand : function(deep, anim, callback){
17557         if(this.loading){ // if an async load is already running, waiting til it's done
17558             var timer;
17559             var f = function(){
17560                 if(!this.loading){ // done loading
17561                     clearInterval(timer);
17562                     this.expand(deep, anim, callback);
17563                 }
17564             }.createDelegate(this);
17565             timer = setInterval(f, 200);
17566             return;
17567         }
17568         if(!this.loaded){
17569             if(this.fireEvent("beforeload", this) === false){
17570                 return;
17571             }
17572             this.loading = true;
17573             this.ui.beforeLoad(this);
17574             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17575             if(loader){
17576                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17577                 return;
17578             }
17579         }
17580         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17581     },
17582     
17583     /**
17584      * Returns true if this node is currently loading
17585      * @return {Boolean}
17586      */
17587     isLoading : function(){
17588         return this.loading;  
17589     },
17590     
17591     loadComplete : function(deep, anim, callback){
17592         this.loading = false;
17593         this.loaded = true;
17594         this.ui.afterLoad(this);
17595         this.fireEvent("load", this);
17596         this.expand(deep, anim, callback);
17597     },
17598     
17599     /**
17600      * Returns true if this node has been loaded
17601      * @return {Boolean}
17602      */
17603     isLoaded : function(){
17604         return this.loaded;
17605     },
17606     
17607     hasChildNodes : function(){
17608         if(!this.isLeaf() && !this.loaded){
17609             return true;
17610         }else{
17611             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17612         }
17613     },
17614
17615     /**
17616      * Trigger a reload for this node
17617      * @param {Function} callback
17618      */
17619     reload : function(callback){
17620         this.collapse(false, false);
17621         while(this.firstChild){
17622             this.removeChild(this.firstChild);
17623         }
17624         this.childrenRendered = false;
17625         this.loaded = false;
17626         if(this.isHiddenRoot()){
17627             this.expanded = false;
17628         }
17629         this.expand(false, false, callback);
17630     }
17631 });/*
17632  * Based on:
17633  * Ext JS Library 1.1.1
17634  * Copyright(c) 2006-2007, Ext JS, LLC.
17635  *
17636  * Originally Released Under LGPL - original licence link has changed is not relivant.
17637  *
17638  * Fork - LGPL
17639  * <script type="text/javascript">
17640  */
17641  
17642 /**
17643  * @class Roo.tree.TreeNodeUI
17644  * @constructor
17645  * @param {Object} node The node to render
17646  * The TreeNode UI implementation is separate from the
17647  * tree implementation. Unless you are customizing the tree UI,
17648  * you should never have to use this directly.
17649  */
17650 Roo.tree.TreeNodeUI = function(node){
17651     this.node = node;
17652     this.rendered = false;
17653     this.animating = false;
17654     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17655 };
17656
17657 Roo.tree.TreeNodeUI.prototype = {
17658     removeChild : function(node){
17659         if(this.rendered){
17660             this.ctNode.removeChild(node.ui.getEl());
17661         }
17662     },
17663
17664     beforeLoad : function(){
17665          this.addClass("x-tree-node-loading");
17666     },
17667
17668     afterLoad : function(){
17669          this.removeClass("x-tree-node-loading");
17670     },
17671
17672     onTextChange : function(node, text, oldText){
17673         if(this.rendered){
17674             this.textNode.innerHTML = text;
17675         }
17676     },
17677
17678     onDisableChange : function(node, state){
17679         this.disabled = state;
17680         if(state){
17681             this.addClass("x-tree-node-disabled");
17682         }else{
17683             this.removeClass("x-tree-node-disabled");
17684         }
17685     },
17686
17687     onSelectedChange : function(state){
17688         if(state){
17689             this.focus();
17690             this.addClass("x-tree-selected");
17691         }else{
17692             //this.blur();
17693             this.removeClass("x-tree-selected");
17694         }
17695     },
17696
17697     onMove : function(tree, node, oldParent, newParent, index, refNode){
17698         this.childIndent = null;
17699         if(this.rendered){
17700             var targetNode = newParent.ui.getContainer();
17701             if(!targetNode){//target not rendered
17702                 this.holder = document.createElement("div");
17703                 this.holder.appendChild(this.wrap);
17704                 return;
17705             }
17706             var insertBefore = refNode ? refNode.ui.getEl() : null;
17707             if(insertBefore){
17708                 targetNode.insertBefore(this.wrap, insertBefore);
17709             }else{
17710                 targetNode.appendChild(this.wrap);
17711             }
17712             this.node.renderIndent(true);
17713         }
17714     },
17715
17716     addClass : function(cls){
17717         if(this.elNode){
17718             Roo.fly(this.elNode).addClass(cls);
17719         }
17720     },
17721
17722     removeClass : function(cls){
17723         if(this.elNode){
17724             Roo.fly(this.elNode).removeClass(cls);
17725         }
17726     },
17727
17728     remove : function(){
17729         if(this.rendered){
17730             this.holder = document.createElement("div");
17731             this.holder.appendChild(this.wrap);
17732         }
17733     },
17734
17735     fireEvent : function(){
17736         return this.node.fireEvent.apply(this.node, arguments);
17737     },
17738
17739     initEvents : function(){
17740         this.node.on("move", this.onMove, this);
17741         var E = Roo.EventManager;
17742         var a = this.anchor;
17743
17744         var el = Roo.fly(a, '_treeui');
17745
17746         if(Roo.isOpera){ // opera render bug ignores the CSS
17747             el.setStyle("text-decoration", "none");
17748         }
17749
17750         el.on("click", this.onClick, this);
17751         el.on("dblclick", this.onDblClick, this);
17752
17753         if(this.checkbox){
17754             Roo.EventManager.on(this.checkbox,
17755                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17756         }
17757
17758         el.on("contextmenu", this.onContextMenu, this);
17759
17760         var icon = Roo.fly(this.iconNode);
17761         icon.on("click", this.onClick, this);
17762         icon.on("dblclick", this.onDblClick, this);
17763         icon.on("contextmenu", this.onContextMenu, this);
17764         E.on(this.ecNode, "click", this.ecClick, this, true);
17765
17766         if(this.node.disabled){
17767             this.addClass("x-tree-node-disabled");
17768         }
17769         if(this.node.hidden){
17770             this.addClass("x-tree-node-disabled");
17771         }
17772         var ot = this.node.getOwnerTree();
17773         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17774         if(dd && (!this.node.isRoot || ot.rootVisible)){
17775             Roo.dd.Registry.register(this.elNode, {
17776                 node: this.node,
17777                 handles: this.getDDHandles(),
17778                 isHandle: false
17779             });
17780         }
17781     },
17782
17783     getDDHandles : function(){
17784         return [this.iconNode, this.textNode];
17785     },
17786
17787     hide : function(){
17788         if(this.rendered){
17789             this.wrap.style.display = "none";
17790         }
17791     },
17792
17793     show : function(){
17794         if(this.rendered){
17795             this.wrap.style.display = "";
17796         }
17797     },
17798
17799     onContextMenu : function(e){
17800         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17801             e.preventDefault();
17802             this.focus();
17803             this.fireEvent("contextmenu", this.node, e);
17804         }
17805     },
17806
17807     onClick : function(e){
17808         if(this.dropping){
17809             e.stopEvent();
17810             return;
17811         }
17812         if(this.fireEvent("beforeclick", this.node, e) !== false){
17813             if(!this.disabled && this.node.attributes.href){
17814                 this.fireEvent("click", this.node, e);
17815                 return;
17816             }
17817             e.preventDefault();
17818             if(this.disabled){
17819                 return;
17820             }
17821
17822             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17823                 this.node.toggle();
17824             }
17825
17826             this.fireEvent("click", this.node, e);
17827         }else{
17828             e.stopEvent();
17829         }
17830     },
17831
17832     onDblClick : function(e){
17833         e.preventDefault();
17834         if(this.disabled){
17835             return;
17836         }
17837         if(this.checkbox){
17838             this.toggleCheck();
17839         }
17840         if(!this.animating && this.node.hasChildNodes()){
17841             this.node.toggle();
17842         }
17843         this.fireEvent("dblclick", this.node, e);
17844     },
17845
17846     onCheckChange : function(){
17847         var checked = this.checkbox.checked;
17848         this.node.attributes.checked = checked;
17849         this.fireEvent('checkchange', this.node, checked);
17850     },
17851
17852     ecClick : function(e){
17853         if(!this.animating && this.node.hasChildNodes()){
17854             this.node.toggle();
17855         }
17856     },
17857
17858     startDrop : function(){
17859         this.dropping = true;
17860     },
17861
17862     // delayed drop so the click event doesn't get fired on a drop
17863     endDrop : function(){
17864        setTimeout(function(){
17865            this.dropping = false;
17866        }.createDelegate(this), 50);
17867     },
17868
17869     expand : function(){
17870         this.updateExpandIcon();
17871         this.ctNode.style.display = "";
17872     },
17873
17874     focus : function(){
17875         if(!this.node.preventHScroll){
17876             try{this.anchor.focus();
17877             }catch(e){}
17878         }else if(!Roo.isIE){
17879             try{
17880                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17881                 var l = noscroll.scrollLeft;
17882                 this.anchor.focus();
17883                 noscroll.scrollLeft = l;
17884             }catch(e){}
17885         }
17886     },
17887
17888     toggleCheck : function(value){
17889         var cb = this.checkbox;
17890         if(cb){
17891             cb.checked = (value === undefined ? !cb.checked : value);
17892         }
17893     },
17894
17895     blur : function(){
17896         try{
17897             this.anchor.blur();
17898         }catch(e){}
17899     },
17900
17901     animExpand : function(callback){
17902         var ct = Roo.get(this.ctNode);
17903         ct.stopFx();
17904         if(!this.node.hasChildNodes()){
17905             this.updateExpandIcon();
17906             this.ctNode.style.display = "";
17907             Roo.callback(callback);
17908             return;
17909         }
17910         this.animating = true;
17911         this.updateExpandIcon();
17912
17913         ct.slideIn('t', {
17914            callback : function(){
17915                this.animating = false;
17916                Roo.callback(callback);
17917             },
17918             scope: this,
17919             duration: this.node.ownerTree.duration || .25
17920         });
17921     },
17922
17923     highlight : function(){
17924         var tree = this.node.getOwnerTree();
17925         Roo.fly(this.wrap).highlight(
17926             tree.hlColor || "C3DAF9",
17927             {endColor: tree.hlBaseColor}
17928         );
17929     },
17930
17931     collapse : function(){
17932         this.updateExpandIcon();
17933         this.ctNode.style.display = "none";
17934     },
17935
17936     animCollapse : function(callback){
17937         var ct = Roo.get(this.ctNode);
17938         ct.enableDisplayMode('block');
17939         ct.stopFx();
17940
17941         this.animating = true;
17942         this.updateExpandIcon();
17943
17944         ct.slideOut('t', {
17945             callback : function(){
17946                this.animating = false;
17947                Roo.callback(callback);
17948             },
17949             scope: this,
17950             duration: this.node.ownerTree.duration || .25
17951         });
17952     },
17953
17954     getContainer : function(){
17955         return this.ctNode;
17956     },
17957
17958     getEl : function(){
17959         return this.wrap;
17960     },
17961
17962     appendDDGhost : function(ghostNode){
17963         ghostNode.appendChild(this.elNode.cloneNode(true));
17964     },
17965
17966     getDDRepairXY : function(){
17967         return Roo.lib.Dom.getXY(this.iconNode);
17968     },
17969
17970     onRender : function(){
17971         this.render();
17972     },
17973
17974     render : function(bulkRender){
17975         var n = this.node, a = n.attributes;
17976         var targetNode = n.parentNode ?
17977               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17978
17979         if(!this.rendered){
17980             this.rendered = true;
17981
17982             this.renderElements(n, a, targetNode, bulkRender);
17983
17984             if(a.qtip){
17985                if(this.textNode.setAttributeNS){
17986                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17987                    if(a.qtipTitle){
17988                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17989                    }
17990                }else{
17991                    this.textNode.setAttribute("ext:qtip", a.qtip);
17992                    if(a.qtipTitle){
17993                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17994                    }
17995                }
17996             }else if(a.qtipCfg){
17997                 a.qtipCfg.target = Roo.id(this.textNode);
17998                 Roo.QuickTips.register(a.qtipCfg);
17999             }
18000             this.initEvents();
18001             if(!this.node.expanded){
18002                 this.updateExpandIcon();
18003             }
18004         }else{
18005             if(bulkRender === true) {
18006                 targetNode.appendChild(this.wrap);
18007             }
18008         }
18009     },
18010
18011     renderElements : function(n, a, targetNode, bulkRender){
18012         // add some indent caching, this helps performance when rendering a large tree
18013         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18014         var t = n.getOwnerTree();
18015         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18016         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18017         var cb = typeof a.checked == 'boolean';
18018         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18019         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18020             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18021             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18022             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18023             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18024             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18025              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18026                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18027             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18028             "</li>"];
18029
18030         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18031             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18032                                 n.nextSibling.ui.getEl(), buf.join(""));
18033         }else{
18034             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18035         }
18036
18037         this.elNode = this.wrap.childNodes[0];
18038         this.ctNode = this.wrap.childNodes[1];
18039         var cs = this.elNode.childNodes;
18040         this.indentNode = cs[0];
18041         this.ecNode = cs[1];
18042         this.iconNode = cs[2];
18043         var index = 3;
18044         if(cb){
18045             this.checkbox = cs[3];
18046             index++;
18047         }
18048         this.anchor = cs[index];
18049         this.textNode = cs[index].firstChild;
18050     },
18051
18052     getAnchor : function(){
18053         return this.anchor;
18054     },
18055
18056     getTextEl : function(){
18057         return this.textNode;
18058     },
18059
18060     getIconEl : function(){
18061         return this.iconNode;
18062     },
18063
18064     isChecked : function(){
18065         return this.checkbox ? this.checkbox.checked : false;
18066     },
18067
18068     updateExpandIcon : function(){
18069         if(this.rendered){
18070             var n = this.node, c1, c2;
18071             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18072             var hasChild = n.hasChildNodes();
18073             if(hasChild){
18074                 if(n.expanded){
18075                     cls += "-minus";
18076                     c1 = "x-tree-node-collapsed";
18077                     c2 = "x-tree-node-expanded";
18078                 }else{
18079                     cls += "-plus";
18080                     c1 = "x-tree-node-expanded";
18081                     c2 = "x-tree-node-collapsed";
18082                 }
18083                 if(this.wasLeaf){
18084                     this.removeClass("x-tree-node-leaf");
18085                     this.wasLeaf = false;
18086                 }
18087                 if(this.c1 != c1 || this.c2 != c2){
18088                     Roo.fly(this.elNode).replaceClass(c1, c2);
18089                     this.c1 = c1; this.c2 = c2;
18090                 }
18091             }else{
18092                 if(!this.wasLeaf){
18093                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18094                     delete this.c1;
18095                     delete this.c2;
18096                     this.wasLeaf = true;
18097                 }
18098             }
18099             var ecc = "x-tree-ec-icon "+cls;
18100             if(this.ecc != ecc){
18101                 this.ecNode.className = ecc;
18102                 this.ecc = ecc;
18103             }
18104         }
18105     },
18106
18107     getChildIndent : function(){
18108         if(!this.childIndent){
18109             var buf = [];
18110             var p = this.node;
18111             while(p){
18112                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18113                     if(!p.isLast()) {
18114                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18115                     } else {
18116                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18117                     }
18118                 }
18119                 p = p.parentNode;
18120             }
18121             this.childIndent = buf.join("");
18122         }
18123         return this.childIndent;
18124     },
18125
18126     renderIndent : function(){
18127         if(this.rendered){
18128             var indent = "";
18129             var p = this.node.parentNode;
18130             if(p){
18131                 indent = p.ui.getChildIndent();
18132             }
18133             if(this.indentMarkup != indent){ // don't rerender if not required
18134                 this.indentNode.innerHTML = indent;
18135                 this.indentMarkup = indent;
18136             }
18137             this.updateExpandIcon();
18138         }
18139     }
18140 };
18141
18142 Roo.tree.RootTreeNodeUI = function(){
18143     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18144 };
18145 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18146     render : function(){
18147         if(!this.rendered){
18148             var targetNode = this.node.ownerTree.innerCt.dom;
18149             this.node.expanded = true;
18150             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18151             this.wrap = this.ctNode = targetNode.firstChild;
18152         }
18153     },
18154     collapse : function(){
18155     },
18156     expand : function(){
18157     }
18158 });/*
18159  * Based on:
18160  * Ext JS Library 1.1.1
18161  * Copyright(c) 2006-2007, Ext JS, LLC.
18162  *
18163  * Originally Released Under LGPL - original licence link has changed is not relivant.
18164  *
18165  * Fork - LGPL
18166  * <script type="text/javascript">
18167  */
18168 /**
18169  * @class Roo.tree.TreeLoader
18170  * @extends Roo.util.Observable
18171  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18172  * nodes from a specified URL. The response must be a javascript Array definition
18173  * who's elements are node definition objects. eg:
18174  * <pre><code>
18175    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18176     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18177 </code></pre>
18178  * <br><br>
18179  * A server request is sent, and child nodes are loaded only when a node is expanded.
18180  * The loading node's id is passed to the server under the parameter name "node" to
18181  * enable the server to produce the correct child nodes.
18182  * <br><br>
18183  * To pass extra parameters, an event handler may be attached to the "beforeload"
18184  * event, and the parameters specified in the TreeLoader's baseParams property:
18185  * <pre><code>
18186     myTreeLoader.on("beforeload", function(treeLoader, node) {
18187         this.baseParams.category = node.attributes.category;
18188     }, this);
18189 </code></pre><
18190  * This would pass an HTTP parameter called "category" to the server containing
18191  * the value of the Node's "category" attribute.
18192  * @constructor
18193  * Creates a new Treeloader.
18194  * @param {Object} config A config object containing config properties.
18195  */
18196 Roo.tree.TreeLoader = function(config){
18197     this.baseParams = {};
18198     this.requestMethod = "POST";
18199     Roo.apply(this, config);
18200
18201     this.addEvents({
18202     
18203         /**
18204          * @event beforeload
18205          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18206          * @param {Object} This TreeLoader object.
18207          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18208          * @param {Object} callback The callback function specified in the {@link #load} call.
18209          */
18210         beforeload : true,
18211         /**
18212          * @event load
18213          * Fires when the node has been successfuly loaded.
18214          * @param {Object} This TreeLoader object.
18215          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18216          * @param {Object} response The response object containing the data from the server.
18217          */
18218         load : true,
18219         /**
18220          * @event loadexception
18221          * Fires if the network request failed.
18222          * @param {Object} This TreeLoader object.
18223          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18224          * @param {Object} response The response object containing the data from the server.
18225          */
18226         loadexception : true,
18227         /**
18228          * @event create
18229          * Fires before a node is created, enabling you to return custom Node types 
18230          * @param {Object} This TreeLoader object.
18231          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18232          */
18233         create : true
18234     });
18235
18236     Roo.tree.TreeLoader.superclass.constructor.call(this);
18237 };
18238
18239 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18240     /**
18241     * @cfg {String} dataUrl The URL from which to request a Json string which
18242     * specifies an array of node definition object representing the child nodes
18243     * to be loaded.
18244     */
18245     /**
18246     * @cfg {Object} baseParams (optional) An object containing properties which
18247     * specify HTTP parameters to be passed to each request for child nodes.
18248     */
18249     /**
18250     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18251     * created by this loader. If the attributes sent by the server have an attribute in this object,
18252     * they take priority.
18253     */
18254     /**
18255     * @cfg {Object} uiProviders (optional) An object containing properties which
18256     * 
18257     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18258     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18259     * <i>uiProvider</i> attribute of a returned child node is a string rather
18260     * than a reference to a TreeNodeUI implementation, this that string value
18261     * is used as a property name in the uiProviders object. You can define the provider named
18262     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18263     */
18264     uiProviders : {},
18265
18266     /**
18267     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18268     * child nodes before loading.
18269     */
18270     clearOnLoad : true,
18271
18272     /**
18273     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18274     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18275     * Grid query { data : [ .....] }
18276     */
18277     
18278     root : false,
18279      /**
18280     * @cfg {String} queryParam (optional) 
18281     * Name of the query as it will be passed on the querystring (defaults to 'node')
18282     * eg. the request will be ?node=[id]
18283     */
18284     
18285     
18286     queryParam: false,
18287     
18288     /**
18289      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18290      * This is called automatically when a node is expanded, but may be used to reload
18291      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18292      * @param {Roo.tree.TreeNode} node
18293      * @param {Function} callback
18294      */
18295     load : function(node, callback){
18296         if(this.clearOnLoad){
18297             while(node.firstChild){
18298                 node.removeChild(node.firstChild);
18299             }
18300         }
18301         if(node.attributes.children){ // preloaded json children
18302             var cs = node.attributes.children;
18303             for(var i = 0, len = cs.length; i < len; i++){
18304                 node.appendChild(this.createNode(cs[i]));
18305             }
18306             if(typeof callback == "function"){
18307                 callback();
18308             }
18309         }else if(this.dataUrl){
18310             this.requestData(node, callback);
18311         }
18312     },
18313
18314     getParams: function(node){
18315         var buf = [], bp = this.baseParams;
18316         for(var key in bp){
18317             if(typeof bp[key] != "function"){
18318                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18319             }
18320         }
18321         var n = this.queryParam === false ? 'node' : this.queryParam;
18322         buf.push(n + "=", encodeURIComponent(node.id));
18323         return buf.join("");
18324     },
18325
18326     requestData : function(node, callback){
18327         if(this.fireEvent("beforeload", this, node, callback) !== false){
18328             this.transId = Roo.Ajax.request({
18329                 method:this.requestMethod,
18330                 url: this.dataUrl||this.url,
18331                 success: this.handleResponse,
18332                 failure: this.handleFailure,
18333                 scope: this,
18334                 argument: {callback: callback, node: node},
18335                 params: this.getParams(node)
18336             });
18337         }else{
18338             // if the load is cancelled, make sure we notify
18339             // the node that we are done
18340             if(typeof callback == "function"){
18341                 callback();
18342             }
18343         }
18344     },
18345
18346     isLoading : function(){
18347         return this.transId ? true : false;
18348     },
18349
18350     abort : function(){
18351         if(this.isLoading()){
18352             Roo.Ajax.abort(this.transId);
18353         }
18354     },
18355
18356     // private
18357     createNode : function(attr){
18358         // apply baseAttrs, nice idea Corey!
18359         if(this.baseAttrs){
18360             Roo.applyIf(attr, this.baseAttrs);
18361         }
18362         if(this.applyLoader !== false){
18363             attr.loader = this;
18364         }
18365         // uiProvider = depreciated..
18366         
18367         if(typeof(attr.uiProvider) == 'string'){
18368            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18369                 /**  eval:var:attr */ eval(attr.uiProvider);
18370         }
18371         if(typeof(this.uiProviders['default']) != 'undefined') {
18372             attr.uiProvider = this.uiProviders['default'];
18373         }
18374         
18375         this.fireEvent('create', this, attr);
18376         
18377         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18378         return(attr.leaf ?
18379                         new Roo.tree.TreeNode(attr) :
18380                         new Roo.tree.AsyncTreeNode(attr));
18381     },
18382
18383     processResponse : function(response, node, callback){
18384         var json = response.responseText;
18385         try {
18386             
18387             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18388             if (this.root !== false) {
18389                 o = o[this.root];
18390             }
18391             
18392             for(var i = 0, len = o.length; i < len; i++){
18393                 var n = this.createNode(o[i]);
18394                 if(n){
18395                     node.appendChild(n);
18396                 }
18397             }
18398             if(typeof callback == "function"){
18399                 callback(this, node);
18400             }
18401         }catch(e){
18402             this.handleFailure(response);
18403         }
18404     },
18405
18406     handleResponse : function(response){
18407         this.transId = false;
18408         var a = response.argument;
18409         this.processResponse(response, a.node, a.callback);
18410         this.fireEvent("load", this, a.node, response);
18411     },
18412
18413     handleFailure : function(response){
18414         this.transId = false;
18415         var a = response.argument;
18416         this.fireEvent("loadexception", this, a.node, response);
18417         if(typeof a.callback == "function"){
18418             a.callback(this, a.node);
18419         }
18420     }
18421 });/*
18422  * Based on:
18423  * Ext JS Library 1.1.1
18424  * Copyright(c) 2006-2007, Ext JS, LLC.
18425  *
18426  * Originally Released Under LGPL - original licence link has changed is not relivant.
18427  *
18428  * Fork - LGPL
18429  * <script type="text/javascript">
18430  */
18431
18432 /**
18433 * @class Roo.tree.TreeFilter
18434 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18435 * @param {TreePanel} tree
18436 * @param {Object} config (optional)
18437  */
18438 Roo.tree.TreeFilter = function(tree, config){
18439     this.tree = tree;
18440     this.filtered = {};
18441     Roo.apply(this, config);
18442 };
18443
18444 Roo.tree.TreeFilter.prototype = {
18445     clearBlank:false,
18446     reverse:false,
18447     autoClear:false,
18448     remove:false,
18449
18450      /**
18451      * Filter the data by a specific attribute.
18452      * @param {String/RegExp} value Either string that the attribute value
18453      * should start with or a RegExp to test against the attribute
18454      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18455      * @param {TreeNode} startNode (optional) The node to start the filter at.
18456      */
18457     filter : function(value, attr, startNode){
18458         attr = attr || "text";
18459         var f;
18460         if(typeof value == "string"){
18461             var vlen = value.length;
18462             // auto clear empty filter
18463             if(vlen == 0 && this.clearBlank){
18464                 this.clear();
18465                 return;
18466             }
18467             value = value.toLowerCase();
18468             f = function(n){
18469                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18470             };
18471         }else if(value.exec){ // regex?
18472             f = function(n){
18473                 return value.test(n.attributes[attr]);
18474             };
18475         }else{
18476             throw 'Illegal filter type, must be string or regex';
18477         }
18478         this.filterBy(f, null, startNode);
18479         },
18480
18481     /**
18482      * Filter by a function. The passed function will be called with each
18483      * node in the tree (or from the startNode). If the function returns true, the node is kept
18484      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18485      * @param {Function} fn The filter function
18486      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18487      */
18488     filterBy : function(fn, scope, startNode){
18489         startNode = startNode || this.tree.root;
18490         if(this.autoClear){
18491             this.clear();
18492         }
18493         var af = this.filtered, rv = this.reverse;
18494         var f = function(n){
18495             if(n == startNode){
18496                 return true;
18497             }
18498             if(af[n.id]){
18499                 return false;
18500             }
18501             var m = fn.call(scope || n, n);
18502             if(!m || rv){
18503                 af[n.id] = n;
18504                 n.ui.hide();
18505                 return false;
18506             }
18507             return true;
18508         };
18509         startNode.cascade(f);
18510         if(this.remove){
18511            for(var id in af){
18512                if(typeof id != "function"){
18513                    var n = af[id];
18514                    if(n && n.parentNode){
18515                        n.parentNode.removeChild(n);
18516                    }
18517                }
18518            }
18519         }
18520     },
18521
18522     /**
18523      * Clears the current filter. Note: with the "remove" option
18524      * set a filter cannot be cleared.
18525      */
18526     clear : function(){
18527         var t = this.tree;
18528         var af = this.filtered;
18529         for(var id in af){
18530             if(typeof id != "function"){
18531                 var n = af[id];
18532                 if(n){
18533                     n.ui.show();
18534                 }
18535             }
18536         }
18537         this.filtered = {};
18538     }
18539 };
18540 /*
18541  * Based on:
18542  * Ext JS Library 1.1.1
18543  * Copyright(c) 2006-2007, Ext JS, LLC.
18544  *
18545  * Originally Released Under LGPL - original licence link has changed is not relivant.
18546  *
18547  * Fork - LGPL
18548  * <script type="text/javascript">
18549  */
18550  
18551
18552 /**
18553  * @class Roo.tree.TreeSorter
18554  * Provides sorting of nodes in a TreePanel
18555  * 
18556  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18557  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18558  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18559  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18560  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18561  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18562  * @constructor
18563  * @param {TreePanel} tree
18564  * @param {Object} config
18565  */
18566 Roo.tree.TreeSorter = function(tree, config){
18567     Roo.apply(this, config);
18568     tree.on("beforechildrenrendered", this.doSort, this);
18569     tree.on("append", this.updateSort, this);
18570     tree.on("insert", this.updateSort, this);
18571     
18572     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18573     var p = this.property || "text";
18574     var sortType = this.sortType;
18575     var fs = this.folderSort;
18576     var cs = this.caseSensitive === true;
18577     var leafAttr = this.leafAttr || 'leaf';
18578
18579     this.sortFn = function(n1, n2){
18580         if(fs){
18581             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18582                 return 1;
18583             }
18584             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18585                 return -1;
18586             }
18587         }
18588         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18589         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18590         if(v1 < v2){
18591                         return dsc ? +1 : -1;
18592                 }else if(v1 > v2){
18593                         return dsc ? -1 : +1;
18594         }else{
18595                 return 0;
18596         }
18597     };
18598 };
18599
18600 Roo.tree.TreeSorter.prototype = {
18601     doSort : function(node){
18602         node.sort(this.sortFn);
18603     },
18604     
18605     compareNodes : function(n1, n2){
18606         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18607     },
18608     
18609     updateSort : function(tree, node){
18610         if(node.childrenRendered){
18611             this.doSort.defer(1, this, [node]);
18612         }
18613     }
18614 };/*
18615  * Based on:
18616  * Ext JS Library 1.1.1
18617  * Copyright(c) 2006-2007, Ext JS, LLC.
18618  *
18619  * Originally Released Under LGPL - original licence link has changed is not relivant.
18620  *
18621  * Fork - LGPL
18622  * <script type="text/javascript">
18623  */
18624
18625 if(Roo.dd.DropZone){
18626     
18627 Roo.tree.TreeDropZone = function(tree, config){
18628     this.allowParentInsert = false;
18629     this.allowContainerDrop = false;
18630     this.appendOnly = false;
18631     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18632     this.tree = tree;
18633     this.lastInsertClass = "x-tree-no-status";
18634     this.dragOverData = {};
18635 };
18636
18637 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18638     ddGroup : "TreeDD",
18639     
18640     expandDelay : 1000,
18641     
18642     expandNode : function(node){
18643         if(node.hasChildNodes() && !node.isExpanded()){
18644             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18645         }
18646     },
18647     
18648     queueExpand : function(node){
18649         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18650     },
18651     
18652     cancelExpand : function(){
18653         if(this.expandProcId){
18654             clearTimeout(this.expandProcId);
18655             this.expandProcId = false;
18656         }
18657     },
18658     
18659     isValidDropPoint : function(n, pt, dd, e, data){
18660         if(!n || !data){ return false; }
18661         var targetNode = n.node;
18662         var dropNode = data.node;
18663         // default drop rules
18664         if(!(targetNode && targetNode.isTarget && pt)){
18665             return false;
18666         }
18667         if(pt == "append" && targetNode.allowChildren === false){
18668             return false;
18669         }
18670         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18671             return false;
18672         }
18673         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18674             return false;
18675         }
18676         // reuse the object
18677         var overEvent = this.dragOverData;
18678         overEvent.tree = this.tree;
18679         overEvent.target = targetNode;
18680         overEvent.data = data;
18681         overEvent.point = pt;
18682         overEvent.source = dd;
18683         overEvent.rawEvent = e;
18684         overEvent.dropNode = dropNode;
18685         overEvent.cancel = false;  
18686         var result = this.tree.fireEvent("nodedragover", overEvent);
18687         return overEvent.cancel === false && result !== false;
18688     },
18689     
18690     getDropPoint : function(e, n, dd){
18691         var tn = n.node;
18692         if(tn.isRoot){
18693             return tn.allowChildren !== false ? "append" : false; // always append for root
18694         }
18695         var dragEl = n.ddel;
18696         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18697         var y = Roo.lib.Event.getPageY(e);
18698         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18699         
18700         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18701         var noAppend = tn.allowChildren === false;
18702         if(this.appendOnly || tn.parentNode.allowChildren === false){
18703             return noAppend ? false : "append";
18704         }
18705         var noBelow = false;
18706         if(!this.allowParentInsert){
18707             noBelow = tn.hasChildNodes() && tn.isExpanded();
18708         }
18709         var q = (b - t) / (noAppend ? 2 : 3);
18710         if(y >= t && y < (t + q)){
18711             return "above";
18712         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18713             return "below";
18714         }else{
18715             return "append";
18716         }
18717     },
18718     
18719     onNodeEnter : function(n, dd, e, data){
18720         this.cancelExpand();
18721     },
18722     
18723     onNodeOver : function(n, dd, e, data){
18724         var pt = this.getDropPoint(e, n, dd);
18725         var node = n.node;
18726         
18727         // auto node expand check
18728         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18729             this.queueExpand(node);
18730         }else if(pt != "append"){
18731             this.cancelExpand();
18732         }
18733         
18734         // set the insert point style on the target node
18735         var returnCls = this.dropNotAllowed;
18736         if(this.isValidDropPoint(n, pt, dd, e, data)){
18737            if(pt){
18738                var el = n.ddel;
18739                var cls;
18740                if(pt == "above"){
18741                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18742                    cls = "x-tree-drag-insert-above";
18743                }else if(pt == "below"){
18744                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18745                    cls = "x-tree-drag-insert-below";
18746                }else{
18747                    returnCls = "x-tree-drop-ok-append";
18748                    cls = "x-tree-drag-append";
18749                }
18750                if(this.lastInsertClass != cls){
18751                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18752                    this.lastInsertClass = cls;
18753                }
18754            }
18755        }
18756        return returnCls;
18757     },
18758     
18759     onNodeOut : function(n, dd, e, data){
18760         this.cancelExpand();
18761         this.removeDropIndicators(n);
18762     },
18763     
18764     onNodeDrop : function(n, dd, e, data){
18765         var point = this.getDropPoint(e, n, dd);
18766         var targetNode = n.node;
18767         targetNode.ui.startDrop();
18768         if(!this.isValidDropPoint(n, point, dd, e, data)){
18769             targetNode.ui.endDrop();
18770             return false;
18771         }
18772         // first try to find the drop node
18773         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18774         var dropEvent = {
18775             tree : this.tree,
18776             target: targetNode,
18777             data: data,
18778             point: point,
18779             source: dd,
18780             rawEvent: e,
18781             dropNode: dropNode,
18782             cancel: !dropNode   
18783         };
18784         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18785         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18786             targetNode.ui.endDrop();
18787             return false;
18788         }
18789         // allow target changing
18790         targetNode = dropEvent.target;
18791         if(point == "append" && !targetNode.isExpanded()){
18792             targetNode.expand(false, null, function(){
18793                 this.completeDrop(dropEvent);
18794             }.createDelegate(this));
18795         }else{
18796             this.completeDrop(dropEvent);
18797         }
18798         return true;
18799     },
18800     
18801     completeDrop : function(de){
18802         var ns = de.dropNode, p = de.point, t = de.target;
18803         if(!(ns instanceof Array)){
18804             ns = [ns];
18805         }
18806         var n;
18807         for(var i = 0, len = ns.length; i < len; i++){
18808             n = ns[i];
18809             if(p == "above"){
18810                 t.parentNode.insertBefore(n, t);
18811             }else if(p == "below"){
18812                 t.parentNode.insertBefore(n, t.nextSibling);
18813             }else{
18814                 t.appendChild(n);
18815             }
18816         }
18817         n.ui.focus();
18818         if(this.tree.hlDrop){
18819             n.ui.highlight();
18820         }
18821         t.ui.endDrop();
18822         this.tree.fireEvent("nodedrop", de);
18823     },
18824     
18825     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18826         if(this.tree.hlDrop){
18827             dropNode.ui.focus();
18828             dropNode.ui.highlight();
18829         }
18830         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18831     },
18832     
18833     getTree : function(){
18834         return this.tree;
18835     },
18836     
18837     removeDropIndicators : function(n){
18838         if(n && n.ddel){
18839             var el = n.ddel;
18840             Roo.fly(el).removeClass([
18841                     "x-tree-drag-insert-above",
18842                     "x-tree-drag-insert-below",
18843                     "x-tree-drag-append"]);
18844             this.lastInsertClass = "_noclass";
18845         }
18846     },
18847     
18848     beforeDragDrop : function(target, e, id){
18849         this.cancelExpand();
18850         return true;
18851     },
18852     
18853     afterRepair : function(data){
18854         if(data && Roo.enableFx){
18855             data.node.ui.highlight();
18856         }
18857         this.hideProxy();
18858     }    
18859 });
18860
18861 }
18862 /*
18863  * Based on:
18864  * Ext JS Library 1.1.1
18865  * Copyright(c) 2006-2007, Ext JS, LLC.
18866  *
18867  * Originally Released Under LGPL - original licence link has changed is not relivant.
18868  *
18869  * Fork - LGPL
18870  * <script type="text/javascript">
18871  */
18872  
18873
18874 if(Roo.dd.DragZone){
18875 Roo.tree.TreeDragZone = function(tree, config){
18876     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18877     this.tree = tree;
18878 };
18879
18880 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18881     ddGroup : "TreeDD",
18882     
18883     onBeforeDrag : function(data, e){
18884         var n = data.node;
18885         return n && n.draggable && !n.disabled;
18886     },
18887     
18888     onInitDrag : function(e){
18889         var data = this.dragData;
18890         this.tree.getSelectionModel().select(data.node);
18891         this.proxy.update("");
18892         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18893         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18894     },
18895     
18896     getRepairXY : function(e, data){
18897         return data.node.ui.getDDRepairXY();
18898     },
18899     
18900     onEndDrag : function(data, e){
18901         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18902     },
18903     
18904     onValidDrop : function(dd, e, id){
18905         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18906         this.hideProxy();
18907     },
18908     
18909     beforeInvalidDrop : function(e, id){
18910         // this scrolls the original position back into view
18911         var sm = this.tree.getSelectionModel();
18912         sm.clearSelections();
18913         sm.select(this.dragData.node);
18914     }
18915 });
18916 }/*
18917  * Based on:
18918  * Ext JS Library 1.1.1
18919  * Copyright(c) 2006-2007, Ext JS, LLC.
18920  *
18921  * Originally Released Under LGPL - original licence link has changed is not relivant.
18922  *
18923  * Fork - LGPL
18924  * <script type="text/javascript">
18925  */
18926 /**
18927  * @class Roo.tree.TreeEditor
18928  * @extends Roo.Editor
18929  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18930  * as the editor field.
18931  * @constructor
18932  * @param {TreePanel} tree
18933  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18934  */
18935 Roo.tree.TreeEditor = function(tree, config){
18936     config = config || {};
18937     var field = config.events ? config : new Roo.form.TextField(config);
18938     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18939
18940     this.tree = tree;
18941
18942     tree.on('beforeclick', this.beforeNodeClick, this);
18943     tree.getTreeEl().on('mousedown', this.hide, this);
18944     this.on('complete', this.updateNode, this);
18945     this.on('beforestartedit', this.fitToTree, this);
18946     this.on('startedit', this.bindScroll, this, {delay:10});
18947     this.on('specialkey', this.onSpecialKey, this);
18948 };
18949
18950 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18951     /**
18952      * @cfg {String} alignment
18953      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18954      */
18955     alignment: "l-l",
18956     // inherit
18957     autoSize: false,
18958     /**
18959      * @cfg {Boolean} hideEl
18960      * True to hide the bound element while the editor is displayed (defaults to false)
18961      */
18962     hideEl : false,
18963     /**
18964      * @cfg {String} cls
18965      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18966      */
18967     cls: "x-small-editor x-tree-editor",
18968     /**
18969      * @cfg {Boolean} shim
18970      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18971      */
18972     shim:false,
18973     // inherit
18974     shadow:"frame",
18975     /**
18976      * @cfg {Number} maxWidth
18977      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18978      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18979      * scroll and client offsets into account prior to each edit.
18980      */
18981     maxWidth: 250,
18982
18983     editDelay : 350,
18984
18985     // private
18986     fitToTree : function(ed, el){
18987         var td = this.tree.getTreeEl().dom, nd = el.dom;
18988         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18989             td.scrollLeft = nd.offsetLeft;
18990         }
18991         var w = Math.min(
18992                 this.maxWidth,
18993                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18994         this.setSize(w, '');
18995     },
18996
18997     // private
18998     triggerEdit : function(node){
18999         this.completeEdit();
19000         this.editNode = node;
19001         this.startEdit(node.ui.textNode, node.text);
19002     },
19003
19004     // private
19005     bindScroll : function(){
19006         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19007     },
19008
19009     // private
19010     beforeNodeClick : function(node, e){
19011         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19012         this.lastClick = new Date();
19013         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19014             e.stopEvent();
19015             this.triggerEdit(node);
19016             return false;
19017         }
19018     },
19019
19020     // private
19021     updateNode : function(ed, value){
19022         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19023         this.editNode.setText(value);
19024     },
19025
19026     // private
19027     onHide : function(){
19028         Roo.tree.TreeEditor.superclass.onHide.call(this);
19029         if(this.editNode){
19030             this.editNode.ui.focus();
19031         }
19032     },
19033
19034     // private
19035     onSpecialKey : function(field, e){
19036         var k = e.getKey();
19037         if(k == e.ESC){
19038             e.stopEvent();
19039             this.cancelEdit();
19040         }else if(k == e.ENTER && !e.hasModifier()){
19041             e.stopEvent();
19042             this.completeEdit();
19043         }
19044     }
19045 });//<Script type="text/javascript">
19046 /*
19047  * Based on:
19048  * Ext JS Library 1.1.1
19049  * Copyright(c) 2006-2007, Ext JS, LLC.
19050  *
19051  * Originally Released Under LGPL - original licence link has changed is not relivant.
19052  *
19053  * Fork - LGPL
19054  * <script type="text/javascript">
19055  */
19056  
19057 /**
19058  * Not documented??? - probably should be...
19059  */
19060
19061 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19062     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19063     
19064     renderElements : function(n, a, targetNode, bulkRender){
19065         //consel.log("renderElements?");
19066         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19067
19068         var t = n.getOwnerTree();
19069         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19070         
19071         var cols = t.columns;
19072         var bw = t.borderWidth;
19073         var c = cols[0];
19074         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19075          var cb = typeof a.checked == "boolean";
19076         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19077         var colcls = 'x-t-' + tid + '-c0';
19078         var buf = [
19079             '<li class="x-tree-node">',
19080             
19081                 
19082                 '<div class="x-tree-node-el ', a.cls,'">',
19083                     // extran...
19084                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19085                 
19086                 
19087                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19088                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19089                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19090                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19091                            (a.iconCls ? ' '+a.iconCls : ''),
19092                            '" unselectable="on" />',
19093                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19094                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19095                              
19096                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19097                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19098                             '<span unselectable="on" qtip="' + tx + '">',
19099                              tx,
19100                              '</span></a>' ,
19101                     '</div>',
19102                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19103                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19104                  ];
19105         for(var i = 1, len = cols.length; i < len; i++){
19106             c = cols[i];
19107             colcls = 'x-t-' + tid + '-c' +i;
19108             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19109             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19110                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19111                       "</div>");
19112          }
19113          
19114          buf.push(
19115             '</a>',
19116             '<div class="x-clear"></div></div>',
19117             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19118             "</li>");
19119         
19120         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19121             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19122                                 n.nextSibling.ui.getEl(), buf.join(""));
19123         }else{
19124             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19125         }
19126         var el = this.wrap.firstChild;
19127         this.elRow = el;
19128         this.elNode = el.firstChild;
19129         this.ranchor = el.childNodes[1];
19130         this.ctNode = this.wrap.childNodes[1];
19131         var cs = el.firstChild.childNodes;
19132         this.indentNode = cs[0];
19133         this.ecNode = cs[1];
19134         this.iconNode = cs[2];
19135         var index = 3;
19136         if(cb){
19137             this.checkbox = cs[3];
19138             index++;
19139         }
19140         this.anchor = cs[index];
19141         
19142         this.textNode = cs[index].firstChild;
19143         
19144         //el.on("click", this.onClick, this);
19145         //el.on("dblclick", this.onDblClick, this);
19146         
19147         
19148        // console.log(this);
19149     },
19150     initEvents : function(){
19151         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19152         
19153             
19154         var a = this.ranchor;
19155
19156         var el = Roo.get(a);
19157
19158         if(Roo.isOpera){ // opera render bug ignores the CSS
19159             el.setStyle("text-decoration", "none");
19160         }
19161
19162         el.on("click", this.onClick, this);
19163         el.on("dblclick", this.onDblClick, this);
19164         el.on("contextmenu", this.onContextMenu, this);
19165         
19166     },
19167     
19168     /*onSelectedChange : function(state){
19169         if(state){
19170             this.focus();
19171             this.addClass("x-tree-selected");
19172         }else{
19173             //this.blur();
19174             this.removeClass("x-tree-selected");
19175         }
19176     },*/
19177     addClass : function(cls){
19178         if(this.elRow){
19179             Roo.fly(this.elRow).addClass(cls);
19180         }
19181         
19182     },
19183     
19184     
19185     removeClass : function(cls){
19186         if(this.elRow){
19187             Roo.fly(this.elRow).removeClass(cls);
19188         }
19189     }
19190
19191     
19192     
19193 });//<Script type="text/javascript">
19194
19195 /*
19196  * Based on:
19197  * Ext JS Library 1.1.1
19198  * Copyright(c) 2006-2007, Ext JS, LLC.
19199  *
19200  * Originally Released Under LGPL - original licence link has changed is not relivant.
19201  *
19202  * Fork - LGPL
19203  * <script type="text/javascript">
19204  */
19205  
19206
19207 /**
19208  * @class Roo.tree.ColumnTree
19209  * @extends Roo.data.TreePanel
19210  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19211  * @cfg {int} borderWidth  compined right/left border allowance
19212  * @constructor
19213  * @param {String/HTMLElement/Element} el The container element
19214  * @param {Object} config
19215  */
19216 Roo.tree.ColumnTree =  function(el, config)
19217 {
19218    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19219    this.addEvents({
19220         /**
19221         * @event resize
19222         * Fire this event on a container when it resizes
19223         * @param {int} w Width
19224         * @param {int} h Height
19225         */
19226        "resize" : true
19227     });
19228     this.on('resize', this.onResize, this);
19229 };
19230
19231 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19232     //lines:false,
19233     
19234     
19235     borderWidth: Roo.isBorderBox ? 0 : 2, 
19236     headEls : false,
19237     
19238     render : function(){
19239         // add the header.....
19240        
19241         Roo.tree.ColumnTree.superclass.render.apply(this);
19242         
19243         this.el.addClass('x-column-tree');
19244         
19245         this.headers = this.el.createChild(
19246             {cls:'x-tree-headers'},this.innerCt.dom);
19247    
19248         var cols = this.columns, c;
19249         var totalWidth = 0;
19250         this.headEls = [];
19251         var  len = cols.length;
19252         for(var i = 0; i < len; i++){
19253              c = cols[i];
19254              totalWidth += c.width;
19255             this.headEls.push(this.headers.createChild({
19256                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19257                  cn: {
19258                      cls:'x-tree-hd-text',
19259                      html: c.header
19260                  },
19261                  style:'width:'+(c.width-this.borderWidth)+'px;'
19262              }));
19263         }
19264         this.headers.createChild({cls:'x-clear'});
19265         // prevent floats from wrapping when clipped
19266         this.headers.setWidth(totalWidth);
19267         //this.innerCt.setWidth(totalWidth);
19268         this.innerCt.setStyle({ overflow: 'auto' });
19269         this.onResize(this.width, this.height);
19270              
19271         
19272     },
19273     onResize : function(w,h)
19274     {
19275         this.height = h;
19276         this.width = w;
19277         // resize cols..
19278         this.innerCt.setWidth(this.width);
19279         this.innerCt.setHeight(this.height-20);
19280         
19281         // headers...
19282         var cols = this.columns, c;
19283         var totalWidth = 0;
19284         var expEl = false;
19285         var len = cols.length;
19286         for(var i = 0; i < len; i++){
19287             c = cols[i];
19288             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19289                 // it's the expander..
19290                 expEl  = this.headEls[i];
19291                 continue;
19292             }
19293             totalWidth += c.width;
19294             
19295         }
19296         if (expEl) {
19297             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19298         }
19299         this.headers.setWidth(w-20);
19300
19301         
19302         
19303         
19304     }
19305 });
19306 /*
19307  * Based on:
19308  * Ext JS Library 1.1.1
19309  * Copyright(c) 2006-2007, Ext JS, LLC.
19310  *
19311  * Originally Released Under LGPL - original licence link has changed is not relivant.
19312  *
19313  * Fork - LGPL
19314  * <script type="text/javascript">
19315  */
19316  
19317 /**
19318  * @class Roo.menu.Menu
19319  * @extends Roo.util.Observable
19320  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19321  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19322  * @constructor
19323  * Creates a new Menu
19324  * @param {Object} config Configuration options
19325  */
19326 Roo.menu.Menu = function(config){
19327     Roo.apply(this, config);
19328     this.id = this.id || Roo.id();
19329     this.addEvents({
19330         /**
19331          * @event beforeshow
19332          * Fires before this menu is displayed
19333          * @param {Roo.menu.Menu} this
19334          */
19335         beforeshow : true,
19336         /**
19337          * @event beforehide
19338          * Fires before this menu is hidden
19339          * @param {Roo.menu.Menu} this
19340          */
19341         beforehide : true,
19342         /**
19343          * @event show
19344          * Fires after this menu is displayed
19345          * @param {Roo.menu.Menu} this
19346          */
19347         show : true,
19348         /**
19349          * @event hide
19350          * Fires after this menu is hidden
19351          * @param {Roo.menu.Menu} this
19352          */
19353         hide : true,
19354         /**
19355          * @event click
19356          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19357          * @param {Roo.menu.Menu} this
19358          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19359          * @param {Roo.EventObject} e
19360          */
19361         click : true,
19362         /**
19363          * @event mouseover
19364          * Fires when the mouse is hovering over this menu
19365          * @param {Roo.menu.Menu} this
19366          * @param {Roo.EventObject} e
19367          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19368          */
19369         mouseover : true,
19370         /**
19371          * @event mouseout
19372          * Fires when the mouse exits this menu
19373          * @param {Roo.menu.Menu} this
19374          * @param {Roo.EventObject} e
19375          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19376          */
19377         mouseout : true,
19378         /**
19379          * @event itemclick
19380          * Fires when a menu item contained in this menu is clicked
19381          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19382          * @param {Roo.EventObject} e
19383          */
19384         itemclick: true
19385     });
19386     if (this.registerMenu) {
19387         Roo.menu.MenuMgr.register(this);
19388     }
19389     
19390     var mis = this.items;
19391     this.items = new Roo.util.MixedCollection();
19392     if(mis){
19393         this.add.apply(this, mis);
19394     }
19395 };
19396
19397 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19398     /**
19399      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19400      */
19401     minWidth : 120,
19402     /**
19403      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19404      * for bottom-right shadow (defaults to "sides")
19405      */
19406     shadow : "sides",
19407     /**
19408      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19409      * this menu (defaults to "tl-tr?")
19410      */
19411     subMenuAlign : "tl-tr?",
19412     /**
19413      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19414      * relative to its element of origin (defaults to "tl-bl?")
19415      */
19416     defaultAlign : "tl-bl?",
19417     /**
19418      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19419      */
19420     allowOtherMenus : false,
19421     /**
19422      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19423      */
19424     registerMenu : true,
19425
19426     hidden:true,
19427
19428     // private
19429     render : function(){
19430         if(this.el){
19431             return;
19432         }
19433         var el = this.el = new Roo.Layer({
19434             cls: "x-menu",
19435             shadow:this.shadow,
19436             constrain: false,
19437             parentEl: this.parentEl || document.body,
19438             zindex:15000
19439         });
19440
19441         this.keyNav = new Roo.menu.MenuNav(this);
19442
19443         if(this.plain){
19444             el.addClass("x-menu-plain");
19445         }
19446         if(this.cls){
19447             el.addClass(this.cls);
19448         }
19449         // generic focus element
19450         this.focusEl = el.createChild({
19451             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19452         });
19453         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19454         ul.on("click", this.onClick, this);
19455         ul.on("mouseover", this.onMouseOver, this);
19456         ul.on("mouseout", this.onMouseOut, this);
19457         this.items.each(function(item){
19458             var li = document.createElement("li");
19459             li.className = "x-menu-list-item";
19460             ul.dom.appendChild(li);
19461             item.render(li, this);
19462         }, this);
19463         this.ul = ul;
19464         this.autoWidth();
19465     },
19466
19467     // private
19468     autoWidth : function(){
19469         var el = this.el, ul = this.ul;
19470         if(!el){
19471             return;
19472         }
19473         var w = this.width;
19474         if(w){
19475             el.setWidth(w);
19476         }else if(Roo.isIE){
19477             el.setWidth(this.minWidth);
19478             var t = el.dom.offsetWidth; // force recalc
19479             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19480         }
19481     },
19482
19483     // private
19484     delayAutoWidth : function(){
19485         if(this.rendered){
19486             if(!this.awTask){
19487                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19488             }
19489             this.awTask.delay(20);
19490         }
19491     },
19492
19493     // private
19494     findTargetItem : function(e){
19495         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19496         if(t && t.menuItemId){
19497             return this.items.get(t.menuItemId);
19498         }
19499     },
19500
19501     // private
19502     onClick : function(e){
19503         var t;
19504         if(t = this.findTargetItem(e)){
19505             t.onClick(e);
19506             this.fireEvent("click", this, t, e);
19507         }
19508     },
19509
19510     // private
19511     setActiveItem : function(item, autoExpand){
19512         if(item != this.activeItem){
19513             if(this.activeItem){
19514                 this.activeItem.deactivate();
19515             }
19516             this.activeItem = item;
19517             item.activate(autoExpand);
19518         }else if(autoExpand){
19519             item.expandMenu();
19520         }
19521     },
19522
19523     // private
19524     tryActivate : function(start, step){
19525         var items = this.items;
19526         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19527             var item = items.get(i);
19528             if(!item.disabled && item.canActivate){
19529                 this.setActiveItem(item, false);
19530                 return item;
19531             }
19532         }
19533         return false;
19534     },
19535
19536     // private
19537     onMouseOver : function(e){
19538         var t;
19539         if(t = this.findTargetItem(e)){
19540             if(t.canActivate && !t.disabled){
19541                 this.setActiveItem(t, true);
19542             }
19543         }
19544         this.fireEvent("mouseover", this, e, t);
19545     },
19546
19547     // private
19548     onMouseOut : function(e){
19549         var t;
19550         if(t = this.findTargetItem(e)){
19551             if(t == this.activeItem && t.shouldDeactivate(e)){
19552                 this.activeItem.deactivate();
19553                 delete this.activeItem;
19554             }
19555         }
19556         this.fireEvent("mouseout", this, e, t);
19557     },
19558
19559     /**
19560      * Read-only.  Returns true if the menu is currently displayed, else false.
19561      * @type Boolean
19562      */
19563     isVisible : function(){
19564         return this.el && !this.hidden;
19565     },
19566
19567     /**
19568      * Displays this menu relative to another element
19569      * @param {String/HTMLElement/Roo.Element} element The element to align to
19570      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19571      * the element (defaults to this.defaultAlign)
19572      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19573      */
19574     show : function(el, pos, parentMenu){
19575         this.parentMenu = parentMenu;
19576         if(!this.el){
19577             this.render();
19578         }
19579         this.fireEvent("beforeshow", this);
19580         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19581     },
19582
19583     /**
19584      * Displays this menu at a specific xy position
19585      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19586      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19587      */
19588     showAt : function(xy, parentMenu, /* private: */_e){
19589         this.parentMenu = parentMenu;
19590         if(!this.el){
19591             this.render();
19592         }
19593         if(_e !== false){
19594             this.fireEvent("beforeshow", this);
19595             xy = this.el.adjustForConstraints(xy);
19596         }
19597         this.el.setXY(xy);
19598         this.el.show();
19599         this.hidden = false;
19600         this.focus();
19601         this.fireEvent("show", this);
19602     },
19603
19604     focus : function(){
19605         if(!this.hidden){
19606             this.doFocus.defer(50, this);
19607         }
19608     },
19609
19610     doFocus : function(){
19611         if(!this.hidden){
19612             this.focusEl.focus();
19613         }
19614     },
19615
19616     /**
19617      * Hides this menu and optionally all parent menus
19618      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19619      */
19620     hide : function(deep){
19621         if(this.el && this.isVisible()){
19622             this.fireEvent("beforehide", this);
19623             if(this.activeItem){
19624                 this.activeItem.deactivate();
19625                 this.activeItem = null;
19626             }
19627             this.el.hide();
19628             this.hidden = true;
19629             this.fireEvent("hide", this);
19630         }
19631         if(deep === true && this.parentMenu){
19632             this.parentMenu.hide(true);
19633         }
19634     },
19635
19636     /**
19637      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19638      * Any of the following are valid:
19639      * <ul>
19640      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19641      * <li>An HTMLElement object which will be converted to a menu item</li>
19642      * <li>A menu item config object that will be created as a new menu item</li>
19643      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19644      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19645      * </ul>
19646      * Usage:
19647      * <pre><code>
19648 // Create the menu
19649 var menu = new Roo.menu.Menu();
19650
19651 // Create a menu item to add by reference
19652 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19653
19654 // Add a bunch of items at once using different methods.
19655 // Only the last item added will be returned.
19656 var item = menu.add(
19657     menuItem,                // add existing item by ref
19658     'Dynamic Item',          // new TextItem
19659     '-',                     // new separator
19660     { text: 'Config Item' }  // new item by config
19661 );
19662 </code></pre>
19663      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19664      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19665      */
19666     add : function(){
19667         var a = arguments, l = a.length, item;
19668         for(var i = 0; i < l; i++){
19669             var el = a[i];
19670             if ((typeof(el) == "object") && el.xtype && el.xns) {
19671                 el = Roo.factory(el, Roo.menu);
19672             }
19673             
19674             if(el.render){ // some kind of Item
19675                 item = this.addItem(el);
19676             }else if(typeof el == "string"){ // string
19677                 if(el == "separator" || el == "-"){
19678                     item = this.addSeparator();
19679                 }else{
19680                     item = this.addText(el);
19681                 }
19682             }else if(el.tagName || el.el){ // element
19683                 item = this.addElement(el);
19684             }else if(typeof el == "object"){ // must be menu item config?
19685                 item = this.addMenuItem(el);
19686             }
19687         }
19688         return item;
19689     },
19690
19691     /**
19692      * Returns this menu's underlying {@link Roo.Element} object
19693      * @return {Roo.Element} The element
19694      */
19695     getEl : function(){
19696         if(!this.el){
19697             this.render();
19698         }
19699         return this.el;
19700     },
19701
19702     /**
19703      * Adds a separator bar to the menu
19704      * @return {Roo.menu.Item} The menu item that was added
19705      */
19706     addSeparator : function(){
19707         return this.addItem(new Roo.menu.Separator());
19708     },
19709
19710     /**
19711      * Adds an {@link Roo.Element} object to the menu
19712      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19713      * @return {Roo.menu.Item} The menu item that was added
19714      */
19715     addElement : function(el){
19716         return this.addItem(new Roo.menu.BaseItem(el));
19717     },
19718
19719     /**
19720      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19721      * @param {Roo.menu.Item} item The menu item to add
19722      * @return {Roo.menu.Item} The menu item that was added
19723      */
19724     addItem : function(item){
19725         this.items.add(item);
19726         if(this.ul){
19727             var li = document.createElement("li");
19728             li.className = "x-menu-list-item";
19729             this.ul.dom.appendChild(li);
19730             item.render(li, this);
19731             this.delayAutoWidth();
19732         }
19733         return item;
19734     },
19735
19736     /**
19737      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19738      * @param {Object} config A MenuItem config object
19739      * @return {Roo.menu.Item} The menu item that was added
19740      */
19741     addMenuItem : function(config){
19742         if(!(config instanceof Roo.menu.Item)){
19743             if(typeof config.checked == "boolean"){ // must be check menu item config?
19744                 config = new Roo.menu.CheckItem(config);
19745             }else{
19746                 config = new Roo.menu.Item(config);
19747             }
19748         }
19749         return this.addItem(config);
19750     },
19751
19752     /**
19753      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19754      * @param {String} text The text to display in the menu item
19755      * @return {Roo.menu.Item} The menu item that was added
19756      */
19757     addText : function(text){
19758         return this.addItem(new Roo.menu.TextItem({ text : text }));
19759     },
19760
19761     /**
19762      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19763      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19764      * @param {Roo.menu.Item} item The menu item to add
19765      * @return {Roo.menu.Item} The menu item that was added
19766      */
19767     insert : function(index, item){
19768         this.items.insert(index, item);
19769         if(this.ul){
19770             var li = document.createElement("li");
19771             li.className = "x-menu-list-item";
19772             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19773             item.render(li, this);
19774             this.delayAutoWidth();
19775         }
19776         return item;
19777     },
19778
19779     /**
19780      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19781      * @param {Roo.menu.Item} item The menu item to remove
19782      */
19783     remove : function(item){
19784         this.items.removeKey(item.id);
19785         item.destroy();
19786     },
19787
19788     /**
19789      * Removes and destroys all items in the menu
19790      */
19791     removeAll : function(){
19792         var f;
19793         while(f = this.items.first()){
19794             this.remove(f);
19795         }
19796     }
19797 });
19798
19799 // MenuNav is a private utility class used internally by the Menu
19800 Roo.menu.MenuNav = function(menu){
19801     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19802     this.scope = this.menu = menu;
19803 };
19804
19805 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19806     doRelay : function(e, h){
19807         var k = e.getKey();
19808         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19809             this.menu.tryActivate(0, 1);
19810             return false;
19811         }
19812         return h.call(this.scope || this, e, this.menu);
19813     },
19814
19815     up : function(e, m){
19816         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19817             m.tryActivate(m.items.length-1, -1);
19818         }
19819     },
19820
19821     down : function(e, m){
19822         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19823             m.tryActivate(0, 1);
19824         }
19825     },
19826
19827     right : function(e, m){
19828         if(m.activeItem){
19829             m.activeItem.expandMenu(true);
19830         }
19831     },
19832
19833     left : function(e, m){
19834         m.hide();
19835         if(m.parentMenu && m.parentMenu.activeItem){
19836             m.parentMenu.activeItem.activate();
19837         }
19838     },
19839
19840     enter : function(e, m){
19841         if(m.activeItem){
19842             e.stopPropagation();
19843             m.activeItem.onClick(e);
19844             m.fireEvent("click", this, m.activeItem);
19845             return true;
19846         }
19847     }
19848 });/*
19849  * Based on:
19850  * Ext JS Library 1.1.1
19851  * Copyright(c) 2006-2007, Ext JS, LLC.
19852  *
19853  * Originally Released Under LGPL - original licence link has changed is not relivant.
19854  *
19855  * Fork - LGPL
19856  * <script type="text/javascript">
19857  */
19858  
19859 /**
19860  * @class Roo.menu.MenuMgr
19861  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19862  * @singleton
19863  */
19864 Roo.menu.MenuMgr = function(){
19865    var menus, active, groups = {}, attached = false, lastShow = new Date();
19866
19867    // private - called when first menu is created
19868    function init(){
19869        menus = {};
19870        active = new Roo.util.MixedCollection();
19871        Roo.get(document).addKeyListener(27, function(){
19872            if(active.length > 0){
19873                hideAll();
19874            }
19875        });
19876    }
19877
19878    // private
19879    function hideAll(){
19880        if(active && active.length > 0){
19881            var c = active.clone();
19882            c.each(function(m){
19883                m.hide();
19884            });
19885        }
19886    }
19887
19888    // private
19889    function onHide(m){
19890        active.remove(m);
19891        if(active.length < 1){
19892            Roo.get(document).un("mousedown", onMouseDown);
19893            attached = false;
19894        }
19895    }
19896
19897    // private
19898    function onShow(m){
19899        var last = active.last();
19900        lastShow = new Date();
19901        active.add(m);
19902        if(!attached){
19903            Roo.get(document).on("mousedown", onMouseDown);
19904            attached = true;
19905        }
19906        if(m.parentMenu){
19907           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19908           m.parentMenu.activeChild = m;
19909        }else if(last && last.isVisible()){
19910           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19911        }
19912    }
19913
19914    // private
19915    function onBeforeHide(m){
19916        if(m.activeChild){
19917            m.activeChild.hide();
19918        }
19919        if(m.autoHideTimer){
19920            clearTimeout(m.autoHideTimer);
19921            delete m.autoHideTimer;
19922        }
19923    }
19924
19925    // private
19926    function onBeforeShow(m){
19927        var pm = m.parentMenu;
19928        if(!pm && !m.allowOtherMenus){
19929            hideAll();
19930        }else if(pm && pm.activeChild && active != m){
19931            pm.activeChild.hide();
19932        }
19933    }
19934
19935    // private
19936    function onMouseDown(e){
19937        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19938            hideAll();
19939        }
19940    }
19941
19942    // private
19943    function onBeforeCheck(mi, state){
19944        if(state){
19945            var g = groups[mi.group];
19946            for(var i = 0, l = g.length; i < l; i++){
19947                if(g[i] != mi){
19948                    g[i].setChecked(false);
19949                }
19950            }
19951        }
19952    }
19953
19954    return {
19955
19956        /**
19957         * Hides all menus that are currently visible
19958         */
19959        hideAll : function(){
19960             hideAll();  
19961        },
19962
19963        // private
19964        register : function(menu){
19965            if(!menus){
19966                init();
19967            }
19968            menus[menu.id] = menu;
19969            menu.on("beforehide", onBeforeHide);
19970            menu.on("hide", onHide);
19971            menu.on("beforeshow", onBeforeShow);
19972            menu.on("show", onShow);
19973            var g = menu.group;
19974            if(g && menu.events["checkchange"]){
19975                if(!groups[g]){
19976                    groups[g] = [];
19977                }
19978                groups[g].push(menu);
19979                menu.on("checkchange", onCheck);
19980            }
19981        },
19982
19983         /**
19984          * Returns a {@link Roo.menu.Menu} object
19985          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19986          * be used to generate and return a new Menu instance.
19987          */
19988        get : function(menu){
19989            if(typeof menu == "string"){ // menu id
19990                return menus[menu];
19991            }else if(menu.events){  // menu instance
19992                return menu;
19993            }else if(typeof menu.length == 'number'){ // array of menu items?
19994                return new Roo.menu.Menu({items:menu});
19995            }else{ // otherwise, must be a config
19996                return new Roo.menu.Menu(menu);
19997            }
19998        },
19999
20000        // private
20001        unregister : function(menu){
20002            delete menus[menu.id];
20003            menu.un("beforehide", onBeforeHide);
20004            menu.un("hide", onHide);
20005            menu.un("beforeshow", onBeforeShow);
20006            menu.un("show", onShow);
20007            var g = menu.group;
20008            if(g && menu.events["checkchange"]){
20009                groups[g].remove(menu);
20010                menu.un("checkchange", onCheck);
20011            }
20012        },
20013
20014        // private
20015        registerCheckable : function(menuItem){
20016            var g = menuItem.group;
20017            if(g){
20018                if(!groups[g]){
20019                    groups[g] = [];
20020                }
20021                groups[g].push(menuItem);
20022                menuItem.on("beforecheckchange", onBeforeCheck);
20023            }
20024        },
20025
20026        // private
20027        unregisterCheckable : function(menuItem){
20028            var g = menuItem.group;
20029            if(g){
20030                groups[g].remove(menuItem);
20031                menuItem.un("beforecheckchange", onBeforeCheck);
20032            }
20033        }
20034    };
20035 }();/*
20036  * Based on:
20037  * Ext JS Library 1.1.1
20038  * Copyright(c) 2006-2007, Ext JS, LLC.
20039  *
20040  * Originally Released Under LGPL - original licence link has changed is not relivant.
20041  *
20042  * Fork - LGPL
20043  * <script type="text/javascript">
20044  */
20045  
20046
20047 /**
20048  * @class Roo.menu.BaseItem
20049  * @extends Roo.Component
20050  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20051  * management and base configuration options shared by all menu components.
20052  * @constructor
20053  * Creates a new BaseItem
20054  * @param {Object} config Configuration options
20055  */
20056 Roo.menu.BaseItem = function(config){
20057     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20058
20059     this.addEvents({
20060         /**
20061          * @event click
20062          * Fires when this item is clicked
20063          * @param {Roo.menu.BaseItem} this
20064          * @param {Roo.EventObject} e
20065          */
20066         click: true,
20067         /**
20068          * @event activate
20069          * Fires when this item is activated
20070          * @param {Roo.menu.BaseItem} this
20071          */
20072         activate : true,
20073         /**
20074          * @event deactivate
20075          * Fires when this item is deactivated
20076          * @param {Roo.menu.BaseItem} this
20077          */
20078         deactivate : true
20079     });
20080
20081     if(this.handler){
20082         this.on("click", this.handler, this.scope, true);
20083     }
20084 };
20085
20086 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20087     /**
20088      * @cfg {Function} handler
20089      * A function that will handle the click event of this menu item (defaults to undefined)
20090      */
20091     /**
20092      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20093      */
20094     canActivate : false,
20095     /**
20096      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20097      */
20098     activeClass : "x-menu-item-active",
20099     /**
20100      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20101      */
20102     hideOnClick : true,
20103     /**
20104      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20105      */
20106     hideDelay : 100,
20107
20108     // private
20109     ctype: "Roo.menu.BaseItem",
20110
20111     // private
20112     actionMode : "container",
20113
20114     // private
20115     render : function(container, parentMenu){
20116         this.parentMenu = parentMenu;
20117         Roo.menu.BaseItem.superclass.render.call(this, container);
20118         this.container.menuItemId = this.id;
20119     },
20120
20121     // private
20122     onRender : function(container, position){
20123         this.el = Roo.get(this.el);
20124         container.dom.appendChild(this.el.dom);
20125     },
20126
20127     // private
20128     onClick : function(e){
20129         if(!this.disabled && this.fireEvent("click", this, e) !== false
20130                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20131             this.handleClick(e);
20132         }else{
20133             e.stopEvent();
20134         }
20135     },
20136
20137     // private
20138     activate : function(){
20139         if(this.disabled){
20140             return false;
20141         }
20142         var li = this.container;
20143         li.addClass(this.activeClass);
20144         this.region = li.getRegion().adjust(2, 2, -2, -2);
20145         this.fireEvent("activate", this);
20146         return true;
20147     },
20148
20149     // private
20150     deactivate : function(){
20151         this.container.removeClass(this.activeClass);
20152         this.fireEvent("deactivate", this);
20153     },
20154
20155     // private
20156     shouldDeactivate : function(e){
20157         return !this.region || !this.region.contains(e.getPoint());
20158     },
20159
20160     // private
20161     handleClick : function(e){
20162         if(this.hideOnClick){
20163             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20164         }
20165     },
20166
20167     // private
20168     expandMenu : function(autoActivate){
20169         // do nothing
20170     },
20171
20172     // private
20173     hideMenu : function(){
20174         // do nothing
20175     }
20176 });/*
20177  * Based on:
20178  * Ext JS Library 1.1.1
20179  * Copyright(c) 2006-2007, Ext JS, LLC.
20180  *
20181  * Originally Released Under LGPL - original licence link has changed is not relivant.
20182  *
20183  * Fork - LGPL
20184  * <script type="text/javascript">
20185  */
20186  
20187 /**
20188  * @class Roo.menu.Adapter
20189  * @extends Roo.menu.BaseItem
20190  * 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.
20191  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20192  * @constructor
20193  * Creates a new Adapter
20194  * @param {Object} config Configuration options
20195  */
20196 Roo.menu.Adapter = function(component, config){
20197     Roo.menu.Adapter.superclass.constructor.call(this, config);
20198     this.component = component;
20199 };
20200 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20201     // private
20202     canActivate : true,
20203
20204     // private
20205     onRender : function(container, position){
20206         this.component.render(container);
20207         this.el = this.component.getEl();
20208     },
20209
20210     // private
20211     activate : function(){
20212         if(this.disabled){
20213             return false;
20214         }
20215         this.component.focus();
20216         this.fireEvent("activate", this);
20217         return true;
20218     },
20219
20220     // private
20221     deactivate : function(){
20222         this.fireEvent("deactivate", this);
20223     },
20224
20225     // private
20226     disable : function(){
20227         this.component.disable();
20228         Roo.menu.Adapter.superclass.disable.call(this);
20229     },
20230
20231     // private
20232     enable : function(){
20233         this.component.enable();
20234         Roo.menu.Adapter.superclass.enable.call(this);
20235     }
20236 });/*
20237  * Based on:
20238  * Ext JS Library 1.1.1
20239  * Copyright(c) 2006-2007, Ext JS, LLC.
20240  *
20241  * Originally Released Under LGPL - original licence link has changed is not relivant.
20242  *
20243  * Fork - LGPL
20244  * <script type="text/javascript">
20245  */
20246
20247 /**
20248  * @class Roo.menu.TextItem
20249  * @extends Roo.menu.BaseItem
20250  * Adds a static text string to a menu, usually used as either a heading or group separator.
20251  * Note: old style constructor with text is still supported.
20252  * 
20253  * @constructor
20254  * Creates a new TextItem
20255  * @param {Object} cfg Configuration
20256  */
20257 Roo.menu.TextItem = function(cfg){
20258     if (typeof(cfg) == 'string') {
20259         this.text = cfg;
20260     } else {
20261         Roo.apply(this,cfg);
20262     }
20263     
20264     Roo.menu.TextItem.superclass.constructor.call(this);
20265 };
20266
20267 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20268     /**
20269      * @cfg {Boolean} text Text to show on item.
20270      */
20271     text : '',
20272     
20273     /**
20274      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20275      */
20276     hideOnClick : false,
20277     /**
20278      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20279      */
20280     itemCls : "x-menu-text",
20281
20282     // private
20283     onRender : function(){
20284         var s = document.createElement("span");
20285         s.className = this.itemCls;
20286         s.innerHTML = this.text;
20287         this.el = s;
20288         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20289     }
20290 });/*
20291  * Based on:
20292  * Ext JS Library 1.1.1
20293  * Copyright(c) 2006-2007, Ext JS, LLC.
20294  *
20295  * Originally Released Under LGPL - original licence link has changed is not relivant.
20296  *
20297  * Fork - LGPL
20298  * <script type="text/javascript">
20299  */
20300
20301 /**
20302  * @class Roo.menu.Separator
20303  * @extends Roo.menu.BaseItem
20304  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20305  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20306  * @constructor
20307  * @param {Object} config Configuration options
20308  */
20309 Roo.menu.Separator = function(config){
20310     Roo.menu.Separator.superclass.constructor.call(this, config);
20311 };
20312
20313 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20314     /**
20315      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20316      */
20317     itemCls : "x-menu-sep",
20318     /**
20319      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20320      */
20321     hideOnClick : false,
20322
20323     // private
20324     onRender : function(li){
20325         var s = document.createElement("span");
20326         s.className = this.itemCls;
20327         s.innerHTML = "&#160;";
20328         this.el = s;
20329         li.addClass("x-menu-sep-li");
20330         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20331     }
20332 });/*
20333  * Based on:
20334  * Ext JS Library 1.1.1
20335  * Copyright(c) 2006-2007, Ext JS, LLC.
20336  *
20337  * Originally Released Under LGPL - original licence link has changed is not relivant.
20338  *
20339  * Fork - LGPL
20340  * <script type="text/javascript">
20341  */
20342 /**
20343  * @class Roo.menu.Item
20344  * @extends Roo.menu.BaseItem
20345  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20346  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20347  * activation and click handling.
20348  * @constructor
20349  * Creates a new Item
20350  * @param {Object} config Configuration options
20351  */
20352 Roo.menu.Item = function(config){
20353     Roo.menu.Item.superclass.constructor.call(this, config);
20354     if(this.menu){
20355         this.menu = Roo.menu.MenuMgr.get(this.menu);
20356     }
20357 };
20358 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20359     
20360     /**
20361      * @cfg {String} text
20362      * The text to show on the menu item.
20363      */
20364     text: '',
20365      /**
20366      * @cfg {String} HTML to render in menu
20367      * The text to show on the menu item (HTML version).
20368      */
20369     html: '',
20370     /**
20371      * @cfg {String} icon
20372      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20373      */
20374     icon: undefined,
20375     /**
20376      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20377      */
20378     itemCls : "x-menu-item",
20379     /**
20380      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20381      */
20382     canActivate : true,
20383     /**
20384      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20385      */
20386     showDelay: 200,
20387     // doc'd in BaseItem
20388     hideDelay: 200,
20389
20390     // private
20391     ctype: "Roo.menu.Item",
20392     
20393     // private
20394     onRender : function(container, position){
20395         var el = document.createElement("a");
20396         el.hideFocus = true;
20397         el.unselectable = "on";
20398         el.href = this.href || "#";
20399         if(this.hrefTarget){
20400             el.target = this.hrefTarget;
20401         }
20402         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20403         
20404         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20405         
20406         el.innerHTML = String.format(
20407                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20408                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20409         this.el = el;
20410         Roo.menu.Item.superclass.onRender.call(this, container, position);
20411     },
20412
20413     /**
20414      * Sets the text to display in this menu item
20415      * @param {String} text The text to display
20416      * @param {Boolean} isHTML true to indicate text is pure html.
20417      */
20418     setText : function(text, isHTML){
20419         if (isHTML) {
20420             this.html = text;
20421         } else {
20422             this.text = text;
20423             this.html = '';
20424         }
20425         if(this.rendered){
20426             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20427      
20428             this.el.update(String.format(
20429                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20430                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20431             this.parentMenu.autoWidth();
20432         }
20433     },
20434
20435     // private
20436     handleClick : function(e){
20437         if(!this.href){ // if no link defined, stop the event automatically
20438             e.stopEvent();
20439         }
20440         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20441     },
20442
20443     // private
20444     activate : function(autoExpand){
20445         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20446             this.focus();
20447             if(autoExpand){
20448                 this.expandMenu();
20449             }
20450         }
20451         return true;
20452     },
20453
20454     // private
20455     shouldDeactivate : function(e){
20456         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20457             if(this.menu && this.menu.isVisible()){
20458                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20459             }
20460             return true;
20461         }
20462         return false;
20463     },
20464
20465     // private
20466     deactivate : function(){
20467         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20468         this.hideMenu();
20469     },
20470
20471     // private
20472     expandMenu : function(autoActivate){
20473         if(!this.disabled && this.menu){
20474             clearTimeout(this.hideTimer);
20475             delete this.hideTimer;
20476             if(!this.menu.isVisible() && !this.showTimer){
20477                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20478             }else if (this.menu.isVisible() && autoActivate){
20479                 this.menu.tryActivate(0, 1);
20480             }
20481         }
20482     },
20483
20484     // private
20485     deferExpand : function(autoActivate){
20486         delete this.showTimer;
20487         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20488         if(autoActivate){
20489             this.menu.tryActivate(0, 1);
20490         }
20491     },
20492
20493     // private
20494     hideMenu : function(){
20495         clearTimeout(this.showTimer);
20496         delete this.showTimer;
20497         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20498             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20499         }
20500     },
20501
20502     // private
20503     deferHide : function(){
20504         delete this.hideTimer;
20505         this.menu.hide();
20506     }
20507 });/*
20508  * Based on:
20509  * Ext JS Library 1.1.1
20510  * Copyright(c) 2006-2007, Ext JS, LLC.
20511  *
20512  * Originally Released Under LGPL - original licence link has changed is not relivant.
20513  *
20514  * Fork - LGPL
20515  * <script type="text/javascript">
20516  */
20517  
20518 /**
20519  * @class Roo.menu.CheckItem
20520  * @extends Roo.menu.Item
20521  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20522  * @constructor
20523  * Creates a new CheckItem
20524  * @param {Object} config Configuration options
20525  */
20526 Roo.menu.CheckItem = function(config){
20527     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20528     this.addEvents({
20529         /**
20530          * @event beforecheckchange
20531          * Fires before the checked value is set, providing an opportunity to cancel if needed
20532          * @param {Roo.menu.CheckItem} this
20533          * @param {Boolean} checked The new checked value that will be set
20534          */
20535         "beforecheckchange" : true,
20536         /**
20537          * @event checkchange
20538          * Fires after the checked value has been set
20539          * @param {Roo.menu.CheckItem} this
20540          * @param {Boolean} checked The checked value that was set
20541          */
20542         "checkchange" : true
20543     });
20544     if(this.checkHandler){
20545         this.on('checkchange', this.checkHandler, this.scope);
20546     }
20547 };
20548 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20549     /**
20550      * @cfg {String} group
20551      * All check items with the same group name will automatically be grouped into a single-select
20552      * radio button group (defaults to '')
20553      */
20554     /**
20555      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20556      */
20557     itemCls : "x-menu-item x-menu-check-item",
20558     /**
20559      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20560      */
20561     groupClass : "x-menu-group-item",
20562
20563     /**
20564      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20565      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20566      * initialized with checked = true will be rendered as checked.
20567      */
20568     checked: false,
20569
20570     // private
20571     ctype: "Roo.menu.CheckItem",
20572
20573     // private
20574     onRender : function(c){
20575         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20576         if(this.group){
20577             this.el.addClass(this.groupClass);
20578         }
20579         Roo.menu.MenuMgr.registerCheckable(this);
20580         if(this.checked){
20581             this.checked = false;
20582             this.setChecked(true, true);
20583         }
20584     },
20585
20586     // private
20587     destroy : function(){
20588         if(this.rendered){
20589             Roo.menu.MenuMgr.unregisterCheckable(this);
20590         }
20591         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20592     },
20593
20594     /**
20595      * Set the checked state of this item
20596      * @param {Boolean} checked The new checked value
20597      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20598      */
20599     setChecked : function(state, suppressEvent){
20600         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20601             if(this.container){
20602                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20603             }
20604             this.checked = state;
20605             if(suppressEvent !== true){
20606                 this.fireEvent("checkchange", this, state);
20607             }
20608         }
20609     },
20610
20611     // private
20612     handleClick : function(e){
20613        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20614            this.setChecked(!this.checked);
20615        }
20616        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20617     }
20618 });/*
20619  * Based on:
20620  * Ext JS Library 1.1.1
20621  * Copyright(c) 2006-2007, Ext JS, LLC.
20622  *
20623  * Originally Released Under LGPL - original licence link has changed is not relivant.
20624  *
20625  * Fork - LGPL
20626  * <script type="text/javascript">
20627  */
20628  
20629 /**
20630  * @class Roo.menu.DateItem
20631  * @extends Roo.menu.Adapter
20632  * A menu item that wraps the {@link Roo.DatPicker} component.
20633  * @constructor
20634  * Creates a new DateItem
20635  * @param {Object} config Configuration options
20636  */
20637 Roo.menu.DateItem = function(config){
20638     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20639     /** The Roo.DatePicker object @type Roo.DatePicker */
20640     this.picker = this.component;
20641     this.addEvents({select: true});
20642     
20643     this.picker.on("render", function(picker){
20644         picker.getEl().swallowEvent("click");
20645         picker.container.addClass("x-menu-date-item");
20646     });
20647
20648     this.picker.on("select", this.onSelect, this);
20649 };
20650
20651 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20652     // private
20653     onSelect : function(picker, date){
20654         this.fireEvent("select", this, date, picker);
20655         Roo.menu.DateItem.superclass.handleClick.call(this);
20656     }
20657 });/*
20658  * Based on:
20659  * Ext JS Library 1.1.1
20660  * Copyright(c) 2006-2007, Ext JS, LLC.
20661  *
20662  * Originally Released Under LGPL - original licence link has changed is not relivant.
20663  *
20664  * Fork - LGPL
20665  * <script type="text/javascript">
20666  */
20667  
20668 /**
20669  * @class Roo.menu.ColorItem
20670  * @extends Roo.menu.Adapter
20671  * A menu item that wraps the {@link Roo.ColorPalette} component.
20672  * @constructor
20673  * Creates a new ColorItem
20674  * @param {Object} config Configuration options
20675  */
20676 Roo.menu.ColorItem = function(config){
20677     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20678     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20679     this.palette = this.component;
20680     this.relayEvents(this.palette, ["select"]);
20681     if(this.selectHandler){
20682         this.on('select', this.selectHandler, this.scope);
20683     }
20684 };
20685 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20686  * Based on:
20687  * Ext JS Library 1.1.1
20688  * Copyright(c) 2006-2007, Ext JS, LLC.
20689  *
20690  * Originally Released Under LGPL - original licence link has changed is not relivant.
20691  *
20692  * Fork - LGPL
20693  * <script type="text/javascript">
20694  */
20695  
20696
20697 /**
20698  * @class Roo.menu.DateMenu
20699  * @extends Roo.menu.Menu
20700  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20701  * @constructor
20702  * Creates a new DateMenu
20703  * @param {Object} config Configuration options
20704  */
20705 Roo.menu.DateMenu = function(config){
20706     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20707     this.plain = true;
20708     var di = new Roo.menu.DateItem(config);
20709     this.add(di);
20710     /**
20711      * The {@link Roo.DatePicker} instance for this DateMenu
20712      * @type DatePicker
20713      */
20714     this.picker = di.picker;
20715     /**
20716      * @event select
20717      * @param {DatePicker} picker
20718      * @param {Date} date
20719      */
20720     this.relayEvents(di, ["select"]);
20721
20722     this.on('beforeshow', function(){
20723         if(this.picker){
20724             this.picker.hideMonthPicker(true);
20725         }
20726     }, this);
20727 };
20728 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20729     cls:'x-date-menu'
20730 });/*
20731  * Based on:
20732  * Ext JS Library 1.1.1
20733  * Copyright(c) 2006-2007, Ext JS, LLC.
20734  *
20735  * Originally Released Under LGPL - original licence link has changed is not relivant.
20736  *
20737  * Fork - LGPL
20738  * <script type="text/javascript">
20739  */
20740  
20741
20742 /**
20743  * @class Roo.menu.ColorMenu
20744  * @extends Roo.menu.Menu
20745  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20746  * @constructor
20747  * Creates a new ColorMenu
20748  * @param {Object} config Configuration options
20749  */
20750 Roo.menu.ColorMenu = function(config){
20751     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20752     this.plain = true;
20753     var ci = new Roo.menu.ColorItem(config);
20754     this.add(ci);
20755     /**
20756      * The {@link Roo.ColorPalette} instance for this ColorMenu
20757      * @type ColorPalette
20758      */
20759     this.palette = ci.palette;
20760     /**
20761      * @event select
20762      * @param {ColorPalette} palette
20763      * @param {String} color
20764      */
20765     this.relayEvents(ci, ["select"]);
20766 };
20767 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20768  * Based on:
20769  * Ext JS Library 1.1.1
20770  * Copyright(c) 2006-2007, Ext JS, LLC.
20771  *
20772  * Originally Released Under LGPL - original licence link has changed is not relivant.
20773  *
20774  * Fork - LGPL
20775  * <script type="text/javascript">
20776  */
20777  
20778 /**
20779  * @class Roo.form.Field
20780  * @extends Roo.BoxComponent
20781  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20782  * @constructor
20783  * Creates a new Field
20784  * @param {Object} config Configuration options
20785  */
20786 Roo.form.Field = function(config){
20787     Roo.form.Field.superclass.constructor.call(this, config);
20788 };
20789
20790 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20791     /**
20792      * @cfg {String} fieldLabel Label to use when rendering a form.
20793      */
20794        /**
20795      * @cfg {String} qtip Mouse over tip
20796      */
20797      
20798     /**
20799      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20800      */
20801     invalidClass : "x-form-invalid",
20802     /**
20803      * @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")
20804      */
20805     invalidText : "The value in this field is invalid",
20806     /**
20807      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20808      */
20809     focusClass : "x-form-focus",
20810     /**
20811      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20812       automatic validation (defaults to "keyup").
20813      */
20814     validationEvent : "keyup",
20815     /**
20816      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20817      */
20818     validateOnBlur : true,
20819     /**
20820      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20821      */
20822     validationDelay : 250,
20823     /**
20824      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20825      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20826      */
20827     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20828     /**
20829      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20830      */
20831     fieldClass : "x-form-field",
20832     /**
20833      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20834      *<pre>
20835 Value         Description
20836 -----------   ----------------------------------------------------------------------
20837 qtip          Display a quick tip when the user hovers over the field
20838 title         Display a default browser title attribute popup
20839 under         Add a block div beneath the field containing the error text
20840 side          Add an error icon to the right of the field with a popup on hover
20841 [element id]  Add the error text directly to the innerHTML of the specified element
20842 </pre>
20843      */
20844     msgTarget : 'qtip',
20845     /**
20846      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20847      */
20848     msgFx : 'normal',
20849
20850     /**
20851      * @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.
20852      */
20853     readOnly : false,
20854
20855     /**
20856      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20857      */
20858     disabled : false,
20859
20860     /**
20861      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20862      */
20863     inputType : undefined,
20864     
20865     /**
20866      * @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).
20867          */
20868         tabIndex : undefined,
20869         
20870     // private
20871     isFormField : true,
20872
20873     // private
20874     hasFocus : false,
20875     /**
20876      * @property {Roo.Element} fieldEl
20877      * Element Containing the rendered Field (with label etc.)
20878      */
20879     /**
20880      * @cfg {Mixed} value A value to initialize this field with.
20881      */
20882     value : undefined,
20883
20884     /**
20885      * @cfg {String} name The field's HTML name attribute.
20886      */
20887     /**
20888      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20889      */
20890
20891         // private ??
20892         initComponent : function(){
20893         Roo.form.Field.superclass.initComponent.call(this);
20894         this.addEvents({
20895             /**
20896              * @event focus
20897              * Fires when this field receives input focus.
20898              * @param {Roo.form.Field} this
20899              */
20900             focus : true,
20901             /**
20902              * @event blur
20903              * Fires when this field loses input focus.
20904              * @param {Roo.form.Field} this
20905              */
20906             blur : true,
20907             /**
20908              * @event specialkey
20909              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20910              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20911              * @param {Roo.form.Field} this
20912              * @param {Roo.EventObject} e The event object
20913              */
20914             specialkey : true,
20915             /**
20916              * @event change
20917              * Fires just before the field blurs if the field value has changed.
20918              * @param {Roo.form.Field} this
20919              * @param {Mixed} newValue The new value
20920              * @param {Mixed} oldValue The original value
20921              */
20922             change : true,
20923             /**
20924              * @event invalid
20925              * Fires after the field has been marked as invalid.
20926              * @param {Roo.form.Field} this
20927              * @param {String} msg The validation message
20928              */
20929             invalid : true,
20930             /**
20931              * @event valid
20932              * Fires after the field has been validated with no errors.
20933              * @param {Roo.form.Field} this
20934              */
20935             valid : true,
20936              /**
20937              * @event keyup
20938              * Fires after the key up
20939              * @param {Roo.form.Field} this
20940              * @param {Roo.EventObject}  e The event Object
20941              */
20942             keyup : true
20943         });
20944     },
20945
20946     /**
20947      * Returns the name attribute of the field if available
20948      * @return {String} name The field name
20949      */
20950     getName: function(){
20951          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20952     },
20953
20954     // private
20955     onRender : function(ct, position){
20956         Roo.form.Field.superclass.onRender.call(this, ct, position);
20957         if(!this.el){
20958             var cfg = this.getAutoCreate();
20959             if(!cfg.name){
20960                 cfg.name = this.name || this.id;
20961             }
20962             if(this.inputType){
20963                 cfg.type = this.inputType;
20964             }
20965             this.el = ct.createChild(cfg, position);
20966         }
20967         var type = this.el.dom.type;
20968         if(type){
20969             if(type == 'password'){
20970                 type = 'text';
20971             }
20972             this.el.addClass('x-form-'+type);
20973         }
20974         if(this.readOnly){
20975             this.el.dom.readOnly = true;
20976         }
20977         if(this.tabIndex !== undefined){
20978             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20979         }
20980
20981         this.el.addClass([this.fieldClass, this.cls]);
20982         this.initValue();
20983     },
20984
20985     /**
20986      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20987      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20988      * @return {Roo.form.Field} this
20989      */
20990     applyTo : function(target){
20991         this.allowDomMove = false;
20992         this.el = Roo.get(target);
20993         this.render(this.el.dom.parentNode);
20994         return this;
20995     },
20996
20997     // private
20998     initValue : function(){
20999         if(this.value !== undefined){
21000             this.setValue(this.value);
21001         }else if(this.el.dom.value.length > 0){
21002             this.setValue(this.el.dom.value);
21003         }
21004     },
21005
21006     /**
21007      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21008      */
21009     isDirty : function() {
21010         if(this.disabled) {
21011             return false;
21012         }
21013         return String(this.getValue()) !== String(this.originalValue);
21014     },
21015
21016     // private
21017     afterRender : function(){
21018         Roo.form.Field.superclass.afterRender.call(this);
21019         this.initEvents();
21020     },
21021
21022     // private
21023     fireKey : function(e){
21024         //Roo.log('field ' + e.getKey());
21025         if(e.isNavKeyPress()){
21026             this.fireEvent("specialkey", this, e);
21027         }
21028     },
21029
21030     /**
21031      * Resets the current field value to the originally loaded value and clears any validation messages
21032      */
21033     reset : function(){
21034         this.setValue(this.originalValue);
21035         this.clearInvalid();
21036     },
21037
21038     // private
21039     initEvents : function(){
21040         // safari killled keypress - so keydown is now used..
21041         this.el.on("keydown" , this.fireKey,  this);
21042         this.el.on("focus", this.onFocus,  this);
21043         this.el.on("blur", this.onBlur,  this);
21044         this.el.relayEvent('keyup', this);
21045
21046         // reference to original value for reset
21047         this.originalValue = this.getValue();
21048     },
21049
21050     // private
21051     onFocus : function(){
21052         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21053             this.el.addClass(this.focusClass);
21054         }
21055         if(!this.hasFocus){
21056             this.hasFocus = true;
21057             this.startValue = this.getValue();
21058             this.fireEvent("focus", this);
21059         }
21060     },
21061
21062     beforeBlur : Roo.emptyFn,
21063
21064     // private
21065     onBlur : function(){
21066         this.beforeBlur();
21067         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21068             this.el.removeClass(this.focusClass);
21069         }
21070         this.hasFocus = false;
21071         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21072             this.validate();
21073         }
21074         var v = this.getValue();
21075         if(String(v) !== String(this.startValue)){
21076             this.fireEvent('change', this, v, this.startValue);
21077         }
21078         this.fireEvent("blur", this);
21079     },
21080
21081     /**
21082      * Returns whether or not the field value is currently valid
21083      * @param {Boolean} preventMark True to disable marking the field invalid
21084      * @return {Boolean} True if the value is valid, else false
21085      */
21086     isValid : function(preventMark){
21087         if(this.disabled){
21088             return true;
21089         }
21090         var restore = this.preventMark;
21091         this.preventMark = preventMark === true;
21092         var v = this.validateValue(this.processValue(this.getRawValue()));
21093         this.preventMark = restore;
21094         return v;
21095     },
21096
21097     /**
21098      * Validates the field value
21099      * @return {Boolean} True if the value is valid, else false
21100      */
21101     validate : function(){
21102         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21103             this.clearInvalid();
21104             return true;
21105         }
21106         return false;
21107     },
21108
21109     processValue : function(value){
21110         return value;
21111     },
21112
21113     // private
21114     // Subclasses should provide the validation implementation by overriding this
21115     validateValue : function(value){
21116         return true;
21117     },
21118
21119     /**
21120      * Mark this field as invalid
21121      * @param {String} msg The validation message
21122      */
21123     markInvalid : function(msg){
21124         if(!this.rendered || this.preventMark){ // not rendered
21125             return;
21126         }
21127         this.el.addClass(this.invalidClass);
21128         msg = msg || this.invalidText;
21129         switch(this.msgTarget){
21130             case 'qtip':
21131                 this.el.dom.qtip = msg;
21132                 this.el.dom.qclass = 'x-form-invalid-tip';
21133                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21134                     Roo.QuickTips.enable();
21135                 }
21136                 break;
21137             case 'title':
21138                 this.el.dom.title = msg;
21139                 break;
21140             case 'under':
21141                 if(!this.errorEl){
21142                     var elp = this.el.findParent('.x-form-element', 5, true);
21143                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21144                     this.errorEl.setWidth(elp.getWidth(true)-20);
21145                 }
21146                 this.errorEl.update(msg);
21147                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21148                 break;
21149             case 'side':
21150                 if(!this.errorIcon){
21151                     var elp = this.el.findParent('.x-form-element', 5, true);
21152                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21153                 }
21154                 this.alignErrorIcon();
21155                 this.errorIcon.dom.qtip = msg;
21156                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21157                 this.errorIcon.show();
21158                 this.on('resize', this.alignErrorIcon, this);
21159                 break;
21160             default:
21161                 var t = Roo.getDom(this.msgTarget);
21162                 t.innerHTML = msg;
21163                 t.style.display = this.msgDisplay;
21164                 break;
21165         }
21166         this.fireEvent('invalid', this, msg);
21167     },
21168
21169     // private
21170     alignErrorIcon : function(){
21171         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21172     },
21173
21174     /**
21175      * Clear any invalid styles/messages for this field
21176      */
21177     clearInvalid : function(){
21178         if(!this.rendered || this.preventMark){ // not rendered
21179             return;
21180         }
21181         this.el.removeClass(this.invalidClass);
21182         switch(this.msgTarget){
21183             case 'qtip':
21184                 this.el.dom.qtip = '';
21185                 break;
21186             case 'title':
21187                 this.el.dom.title = '';
21188                 break;
21189             case 'under':
21190                 if(this.errorEl){
21191                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21192                 }
21193                 break;
21194             case 'side':
21195                 if(this.errorIcon){
21196                     this.errorIcon.dom.qtip = '';
21197                     this.errorIcon.hide();
21198                     this.un('resize', this.alignErrorIcon, this);
21199                 }
21200                 break;
21201             default:
21202                 var t = Roo.getDom(this.msgTarget);
21203                 t.innerHTML = '';
21204                 t.style.display = 'none';
21205                 break;
21206         }
21207         this.fireEvent('valid', this);
21208     },
21209
21210     /**
21211      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21212      * @return {Mixed} value The field value
21213      */
21214     getRawValue : function(){
21215         var v = this.el.getValue();
21216         if(v === this.emptyText){
21217             v = '';
21218         }
21219         return v;
21220     },
21221
21222     /**
21223      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21224      * @return {Mixed} value The field value
21225      */
21226     getValue : function(){
21227         var v = this.el.getValue();
21228         if(v === this.emptyText || v === undefined){
21229             v = '';
21230         }
21231         return v;
21232     },
21233
21234     /**
21235      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21236      * @param {Mixed} value The value to set
21237      */
21238     setRawValue : function(v){
21239         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21240     },
21241
21242     /**
21243      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21244      * @param {Mixed} value The value to set
21245      */
21246     setValue : function(v){
21247         this.value = v;
21248         if(this.rendered){
21249             this.el.dom.value = (v === null || v === undefined ? '' : v);
21250             this.validate();
21251         }
21252     },
21253
21254     adjustSize : function(w, h){
21255         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21256         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21257         return s;
21258     },
21259
21260     adjustWidth : function(tag, w){
21261         tag = tag.toLowerCase();
21262         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21263             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21264                 if(tag == 'input'){
21265                     return w + 2;
21266                 }
21267                 if(tag = 'textarea'){
21268                     return w-2;
21269                 }
21270             }else if(Roo.isOpera){
21271                 if(tag == 'input'){
21272                     return w + 2;
21273                 }
21274                 if(tag = 'textarea'){
21275                     return w-2;
21276                 }
21277             }
21278         }
21279         return w;
21280     }
21281 });
21282
21283
21284 // anything other than normal should be considered experimental
21285 Roo.form.Field.msgFx = {
21286     normal : {
21287         show: function(msgEl, f){
21288             msgEl.setDisplayed('block');
21289         },
21290
21291         hide : function(msgEl, f){
21292             msgEl.setDisplayed(false).update('');
21293         }
21294     },
21295
21296     slide : {
21297         show: function(msgEl, f){
21298             msgEl.slideIn('t', {stopFx:true});
21299         },
21300
21301         hide : function(msgEl, f){
21302             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21303         }
21304     },
21305
21306     slideRight : {
21307         show: function(msgEl, f){
21308             msgEl.fixDisplay();
21309             msgEl.alignTo(f.el, 'tl-tr');
21310             msgEl.slideIn('l', {stopFx:true});
21311         },
21312
21313         hide : function(msgEl, f){
21314             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21315         }
21316     }
21317 };/*
21318  * Based on:
21319  * Ext JS Library 1.1.1
21320  * Copyright(c) 2006-2007, Ext JS, LLC.
21321  *
21322  * Originally Released Under LGPL - original licence link has changed is not relivant.
21323  *
21324  * Fork - LGPL
21325  * <script type="text/javascript">
21326  */
21327  
21328
21329 /**
21330  * @class Roo.form.TextField
21331  * @extends Roo.form.Field
21332  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21333  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21334  * @constructor
21335  * Creates a new TextField
21336  * @param {Object} config Configuration options
21337  */
21338 Roo.form.TextField = function(config){
21339     Roo.form.TextField.superclass.constructor.call(this, config);
21340     this.addEvents({
21341         /**
21342          * @event autosize
21343          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21344          * according to the default logic, but this event provides a hook for the developer to apply additional
21345          * logic at runtime to resize the field if needed.
21346              * @param {Roo.form.Field} this This text field
21347              * @param {Number} width The new field width
21348              */
21349         autosize : true
21350     });
21351 };
21352
21353 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21354     /**
21355      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21356      */
21357     grow : false,
21358     /**
21359      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21360      */
21361     growMin : 30,
21362     /**
21363      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21364      */
21365     growMax : 800,
21366     /**
21367      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21368      */
21369     vtype : null,
21370     /**
21371      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21372      */
21373     maskRe : null,
21374     /**
21375      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21376      */
21377     disableKeyFilter : false,
21378     /**
21379      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21380      */
21381     allowBlank : true,
21382     /**
21383      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21384      */
21385     minLength : 0,
21386     /**
21387      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21388      */
21389     maxLength : Number.MAX_VALUE,
21390     /**
21391      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21392      */
21393     minLengthText : "The minimum length for this field is {0}",
21394     /**
21395      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21396      */
21397     maxLengthText : "The maximum length for this field is {0}",
21398     /**
21399      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21400      */
21401     selectOnFocus : false,
21402     /**
21403      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21404      */
21405     blankText : "This field is required",
21406     /**
21407      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21408      * If available, this function will be called only after the basic validators all return true, and will be passed the
21409      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21410      */
21411     validator : null,
21412     /**
21413      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21414      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21415      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21416      */
21417     regex : null,
21418     /**
21419      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21420      */
21421     regexText : "",
21422     /**
21423      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21424      */
21425     emptyText : null,
21426     /**
21427      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21428      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21429      */
21430     emptyClass : 'x-form-empty-field',
21431
21432     // private
21433     initEvents : function(){
21434         Roo.form.TextField.superclass.initEvents.call(this);
21435         if(this.validationEvent == 'keyup'){
21436             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21437             this.el.on('keyup', this.filterValidation, this);
21438         }
21439         else if(this.validationEvent !== false){
21440             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21441         }
21442         if(this.selectOnFocus || this.emptyText){
21443             this.on("focus", this.preFocus, this);
21444             if(this.emptyText){
21445                 this.on('blur', this.postBlur, this);
21446                 this.applyEmptyText();
21447             }
21448         }
21449         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21450             this.el.on("keypress", this.filterKeys, this);
21451         }
21452         if(this.grow){
21453             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21454             this.el.on("click", this.autoSize,  this);
21455         }
21456     },
21457
21458     processValue : function(value){
21459         if(this.stripCharsRe){
21460             var newValue = value.replace(this.stripCharsRe, '');
21461             if(newValue !== value){
21462                 this.setRawValue(newValue);
21463                 return newValue;
21464             }
21465         }
21466         return value;
21467     },
21468
21469     filterValidation : function(e){
21470         if(!e.isNavKeyPress()){
21471             this.validationTask.delay(this.validationDelay);
21472         }
21473     },
21474
21475     // private
21476     onKeyUp : function(e){
21477         if(!e.isNavKeyPress()){
21478             this.autoSize();
21479         }
21480     },
21481
21482     /**
21483      * Resets the current field value to the originally-loaded value and clears any validation messages.
21484      * Also adds emptyText and emptyClass if the original value was blank.
21485      */
21486     reset : function(){
21487         Roo.form.TextField.superclass.reset.call(this);
21488         this.applyEmptyText();
21489     },
21490
21491     applyEmptyText : function(){
21492         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21493             this.setRawValue(this.emptyText);
21494             this.el.addClass(this.emptyClass);
21495         }
21496     },
21497
21498     // private
21499     preFocus : function(){
21500         if(this.emptyText){
21501             if(this.el.dom.value == this.emptyText){
21502                 this.setRawValue('');
21503             }
21504             this.el.removeClass(this.emptyClass);
21505         }
21506         if(this.selectOnFocus){
21507             this.el.dom.select();
21508         }
21509     },
21510
21511     // private
21512     postBlur : function(){
21513         this.applyEmptyText();
21514     },
21515
21516     // private
21517     filterKeys : function(e){
21518         var k = e.getKey();
21519         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21520             return;
21521         }
21522         var c = e.getCharCode(), cc = String.fromCharCode(c);
21523         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21524             return;
21525         }
21526         if(!this.maskRe.test(cc)){
21527             e.stopEvent();
21528         }
21529     },
21530
21531     setValue : function(v){
21532         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21533             this.el.removeClass(this.emptyClass);
21534         }
21535         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21536         this.applyEmptyText();
21537         this.autoSize();
21538     },
21539
21540     /**
21541      * Validates a value according to the field's validation rules and marks the field as invalid
21542      * if the validation fails
21543      * @param {Mixed} value The value to validate
21544      * @return {Boolean} True if the value is valid, else false
21545      */
21546     validateValue : function(value){
21547         if(value.length < 1 || value === this.emptyText){ // if it's blank
21548              if(this.allowBlank){
21549                 this.clearInvalid();
21550                 return true;
21551              }else{
21552                 this.markInvalid(this.blankText);
21553                 return false;
21554              }
21555         }
21556         if(value.length < this.minLength){
21557             this.markInvalid(String.format(this.minLengthText, this.minLength));
21558             return false;
21559         }
21560         if(value.length > this.maxLength){
21561             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21562             return false;
21563         }
21564         if(this.vtype){
21565             var vt = Roo.form.VTypes;
21566             if(!vt[this.vtype](value, this)){
21567                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21568                 return false;
21569             }
21570         }
21571         if(typeof this.validator == "function"){
21572             var msg = this.validator(value);
21573             if(msg !== true){
21574                 this.markInvalid(msg);
21575                 return false;
21576             }
21577         }
21578         if(this.regex && !this.regex.test(value)){
21579             this.markInvalid(this.regexText);
21580             return false;
21581         }
21582         return true;
21583     },
21584
21585     /**
21586      * Selects text in this field
21587      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21588      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21589      */
21590     selectText : function(start, end){
21591         var v = this.getRawValue();
21592         if(v.length > 0){
21593             start = start === undefined ? 0 : start;
21594             end = end === undefined ? v.length : end;
21595             var d = this.el.dom;
21596             if(d.setSelectionRange){
21597                 d.setSelectionRange(start, end);
21598             }else if(d.createTextRange){
21599                 var range = d.createTextRange();
21600                 range.moveStart("character", start);
21601                 range.moveEnd("character", v.length-end);
21602                 range.select();
21603             }
21604         }
21605     },
21606
21607     /**
21608      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21609      * This only takes effect if grow = true, and fires the autosize event.
21610      */
21611     autoSize : function(){
21612         if(!this.grow || !this.rendered){
21613             return;
21614         }
21615         if(!this.metrics){
21616             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21617         }
21618         var el = this.el;
21619         var v = el.dom.value;
21620         var d = document.createElement('div');
21621         d.appendChild(document.createTextNode(v));
21622         v = d.innerHTML;
21623         d = null;
21624         v += "&#160;";
21625         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21626         this.el.setWidth(w);
21627         this.fireEvent("autosize", this, w);
21628     }
21629 });/*
21630  * Based on:
21631  * Ext JS Library 1.1.1
21632  * Copyright(c) 2006-2007, Ext JS, LLC.
21633  *
21634  * Originally Released Under LGPL - original licence link has changed is not relivant.
21635  *
21636  * Fork - LGPL
21637  * <script type="text/javascript">
21638  */
21639  
21640 /**
21641  * @class Roo.form.Hidden
21642  * @extends Roo.form.TextField
21643  * Simple Hidden element used on forms 
21644  * 
21645  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21646  * 
21647  * @constructor
21648  * Creates a new Hidden form element.
21649  * @param {Object} config Configuration options
21650  */
21651
21652
21653
21654 // easy hidden field...
21655 Roo.form.Hidden = function(config){
21656     Roo.form.Hidden.superclass.constructor.call(this, config);
21657 };
21658   
21659 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21660     fieldLabel:      '',
21661     inputType:      'hidden',
21662     width:          50,
21663     allowBlank:     true,
21664     labelSeparator: '',
21665     hidden:         true,
21666     itemCls :       'x-form-item-display-none'
21667
21668
21669 });
21670
21671
21672 /*
21673  * Based on:
21674  * Ext JS Library 1.1.1
21675  * Copyright(c) 2006-2007, Ext JS, LLC.
21676  *
21677  * Originally Released Under LGPL - original licence link has changed is not relivant.
21678  *
21679  * Fork - LGPL
21680  * <script type="text/javascript">
21681  */
21682  
21683 /**
21684  * @class Roo.form.TriggerField
21685  * @extends Roo.form.TextField
21686  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21687  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21688  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21689  * for which you can provide a custom implementation.  For example:
21690  * <pre><code>
21691 var trigger = new Roo.form.TriggerField();
21692 trigger.onTriggerClick = myTriggerFn;
21693 trigger.applyTo('my-field');
21694 </code></pre>
21695  *
21696  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21697  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21698  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21699  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21700  * @constructor
21701  * Create a new TriggerField.
21702  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21703  * to the base TextField)
21704  */
21705 Roo.form.TriggerField = function(config){
21706     this.mimicing = false;
21707     Roo.form.TriggerField.superclass.constructor.call(this, config);
21708 };
21709
21710 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21711     /**
21712      * @cfg {String} triggerClass A CSS class to apply to the trigger
21713      */
21714     /**
21715      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21716      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21717      */
21718     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21719     /**
21720      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21721      */
21722     hideTrigger:false,
21723
21724     /** @cfg {Boolean} grow @hide */
21725     /** @cfg {Number} growMin @hide */
21726     /** @cfg {Number} growMax @hide */
21727
21728     /**
21729      * @hide 
21730      * @method
21731      */
21732     autoSize: Roo.emptyFn,
21733     // private
21734     monitorTab : true,
21735     // private
21736     deferHeight : true,
21737
21738     
21739     actionMode : 'wrap',
21740     // private
21741     onResize : function(w, h){
21742         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21743         if(typeof w == 'number'){
21744             var x = w - this.trigger.getWidth();
21745             this.el.setWidth(this.adjustWidth('input', x));
21746             this.trigger.setStyle('left', x+'px');
21747         }
21748     },
21749
21750     // private
21751     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21752
21753     // private
21754     getResizeEl : function(){
21755         return this.wrap;
21756     },
21757
21758     // private
21759     getPositionEl : function(){
21760         return this.wrap;
21761     },
21762
21763     // private
21764     alignErrorIcon : function(){
21765         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21766     },
21767
21768     // private
21769     onRender : function(ct, position){
21770         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21771         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21772         this.trigger = this.wrap.createChild(this.triggerConfig ||
21773                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21774         if(this.hideTrigger){
21775             this.trigger.setDisplayed(false);
21776         }
21777         this.initTrigger();
21778         if(!this.width){
21779             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21780         }
21781     },
21782
21783     // private
21784     initTrigger : function(){
21785         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21786         this.trigger.addClassOnOver('x-form-trigger-over');
21787         this.trigger.addClassOnClick('x-form-trigger-click');
21788     },
21789
21790     // private
21791     onDestroy : function(){
21792         if(this.trigger){
21793             this.trigger.removeAllListeners();
21794             this.trigger.remove();
21795         }
21796         if(this.wrap){
21797             this.wrap.remove();
21798         }
21799         Roo.form.TriggerField.superclass.onDestroy.call(this);
21800     },
21801
21802     // private
21803     onFocus : function(){
21804         Roo.form.TriggerField.superclass.onFocus.call(this);
21805         if(!this.mimicing){
21806             this.wrap.addClass('x-trigger-wrap-focus');
21807             this.mimicing = true;
21808             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21809             if(this.monitorTab){
21810                 this.el.on("keydown", this.checkTab, this);
21811             }
21812         }
21813     },
21814
21815     // private
21816     checkTab : function(e){
21817         if(e.getKey() == e.TAB){
21818             this.triggerBlur();
21819         }
21820     },
21821
21822     // private
21823     onBlur : function(){
21824         // do nothing
21825     },
21826
21827     // private
21828     mimicBlur : function(e, t){
21829         if(!this.wrap.contains(t) && this.validateBlur()){
21830             this.triggerBlur();
21831         }
21832     },
21833
21834     // private
21835     triggerBlur : function(){
21836         this.mimicing = false;
21837         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21838         if(this.monitorTab){
21839             this.el.un("keydown", this.checkTab, this);
21840         }
21841         this.wrap.removeClass('x-trigger-wrap-focus');
21842         Roo.form.TriggerField.superclass.onBlur.call(this);
21843     },
21844
21845     // private
21846     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21847     validateBlur : function(e, t){
21848         return true;
21849     },
21850
21851     // private
21852     onDisable : function(){
21853         Roo.form.TriggerField.superclass.onDisable.call(this);
21854         if(this.wrap){
21855             this.wrap.addClass('x-item-disabled');
21856         }
21857     },
21858
21859     // private
21860     onEnable : function(){
21861         Roo.form.TriggerField.superclass.onEnable.call(this);
21862         if(this.wrap){
21863             this.wrap.removeClass('x-item-disabled');
21864         }
21865     },
21866
21867     // private
21868     onShow : function(){
21869         var ae = this.getActionEl();
21870         
21871         if(ae){
21872             ae.dom.style.display = '';
21873             ae.dom.style.visibility = 'visible';
21874         }
21875     },
21876
21877     // private
21878     
21879     onHide : function(){
21880         var ae = this.getActionEl();
21881         ae.dom.style.display = 'none';
21882     },
21883
21884     /**
21885      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21886      * by an implementing function.
21887      * @method
21888      * @param {EventObject} e
21889      */
21890     onTriggerClick : Roo.emptyFn
21891 });
21892
21893 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21894 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21895 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21896 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21897     initComponent : function(){
21898         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21899
21900         this.triggerConfig = {
21901             tag:'span', cls:'x-form-twin-triggers', cn:[
21902             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21903             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21904         ]};
21905     },
21906
21907     getTrigger : function(index){
21908         return this.triggers[index];
21909     },
21910
21911     initTrigger : function(){
21912         var ts = this.trigger.select('.x-form-trigger', true);
21913         this.wrap.setStyle('overflow', 'hidden');
21914         var triggerField = this;
21915         ts.each(function(t, all, index){
21916             t.hide = function(){
21917                 var w = triggerField.wrap.getWidth();
21918                 this.dom.style.display = 'none';
21919                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21920             };
21921             t.show = function(){
21922                 var w = triggerField.wrap.getWidth();
21923                 this.dom.style.display = '';
21924                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21925             };
21926             var triggerIndex = 'Trigger'+(index+1);
21927
21928             if(this['hide'+triggerIndex]){
21929                 t.dom.style.display = 'none';
21930             }
21931             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21932             t.addClassOnOver('x-form-trigger-over');
21933             t.addClassOnClick('x-form-trigger-click');
21934         }, this);
21935         this.triggers = ts.elements;
21936     },
21937
21938     onTrigger1Click : Roo.emptyFn,
21939     onTrigger2Click : Roo.emptyFn
21940 });/*
21941  * Based on:
21942  * Ext JS Library 1.1.1
21943  * Copyright(c) 2006-2007, Ext JS, LLC.
21944  *
21945  * Originally Released Under LGPL - original licence link has changed is not relivant.
21946  *
21947  * Fork - LGPL
21948  * <script type="text/javascript">
21949  */
21950  
21951 /**
21952  * @class Roo.form.TextArea
21953  * @extends Roo.form.TextField
21954  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21955  * support for auto-sizing.
21956  * @constructor
21957  * Creates a new TextArea
21958  * @param {Object} config Configuration options
21959  */
21960 Roo.form.TextArea = function(config){
21961     Roo.form.TextArea.superclass.constructor.call(this, config);
21962     // these are provided exchanges for backwards compat
21963     // minHeight/maxHeight were replaced by growMin/growMax to be
21964     // compatible with TextField growing config values
21965     if(this.minHeight !== undefined){
21966         this.growMin = this.minHeight;
21967     }
21968     if(this.maxHeight !== undefined){
21969         this.growMax = this.maxHeight;
21970     }
21971 };
21972
21973 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21974     /**
21975      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21976      */
21977     growMin : 60,
21978     /**
21979      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21980      */
21981     growMax: 1000,
21982     /**
21983      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21984      * in the field (equivalent to setting overflow: hidden, defaults to false)
21985      */
21986     preventScrollbars: false,
21987     /**
21988      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21989      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21990      */
21991
21992     // private
21993     onRender : function(ct, position){
21994         if(!this.el){
21995             this.defaultAutoCreate = {
21996                 tag: "textarea",
21997                 style:"width:300px;height:60px;",
21998                 autocomplete: "off"
21999             };
22000         }
22001         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22002         if(this.grow){
22003             this.textSizeEl = Roo.DomHelper.append(document.body, {
22004                 tag: "pre", cls: "x-form-grow-sizer"
22005             });
22006             if(this.preventScrollbars){
22007                 this.el.setStyle("overflow", "hidden");
22008             }
22009             this.el.setHeight(this.growMin);
22010         }
22011     },
22012
22013     onDestroy : function(){
22014         if(this.textSizeEl){
22015             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22016         }
22017         Roo.form.TextArea.superclass.onDestroy.call(this);
22018     },
22019
22020     // private
22021     onKeyUp : function(e){
22022         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22023             this.autoSize();
22024         }
22025     },
22026
22027     /**
22028      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22029      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22030      */
22031     autoSize : function(){
22032         if(!this.grow || !this.textSizeEl){
22033             return;
22034         }
22035         var el = this.el;
22036         var v = el.dom.value;
22037         var ts = this.textSizeEl;
22038
22039         ts.innerHTML = '';
22040         ts.appendChild(document.createTextNode(v));
22041         v = ts.innerHTML;
22042
22043         Roo.fly(ts).setWidth(this.el.getWidth());
22044         if(v.length < 1){
22045             v = "&#160;&#160;";
22046         }else{
22047             if(Roo.isIE){
22048                 v = v.replace(/\n/g, '<p>&#160;</p>');
22049             }
22050             v += "&#160;\n&#160;";
22051         }
22052         ts.innerHTML = v;
22053         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22054         if(h != this.lastHeight){
22055             this.lastHeight = h;
22056             this.el.setHeight(h);
22057             this.fireEvent("autosize", this, h);
22058         }
22059     }
22060 });/*
22061  * Based on:
22062  * Ext JS Library 1.1.1
22063  * Copyright(c) 2006-2007, Ext JS, LLC.
22064  *
22065  * Originally Released Under LGPL - original licence link has changed is not relivant.
22066  *
22067  * Fork - LGPL
22068  * <script type="text/javascript">
22069  */
22070  
22071
22072 /**
22073  * @class Roo.form.NumberField
22074  * @extends Roo.form.TextField
22075  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22076  * @constructor
22077  * Creates a new NumberField
22078  * @param {Object} config Configuration options
22079  */
22080 Roo.form.NumberField = function(config){
22081     Roo.form.NumberField.superclass.constructor.call(this, config);
22082 };
22083
22084 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22085     /**
22086      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22087      */
22088     fieldClass: "x-form-field x-form-num-field",
22089     /**
22090      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22091      */
22092     allowDecimals : true,
22093     /**
22094      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22095      */
22096     decimalSeparator : ".",
22097     /**
22098      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22099      */
22100     decimalPrecision : 2,
22101     /**
22102      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22103      */
22104     allowNegative : true,
22105     /**
22106      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22107      */
22108     minValue : Number.NEGATIVE_INFINITY,
22109     /**
22110      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22111      */
22112     maxValue : Number.MAX_VALUE,
22113     /**
22114      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22115      */
22116     minText : "The minimum value for this field is {0}",
22117     /**
22118      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22119      */
22120     maxText : "The maximum value for this field is {0}",
22121     /**
22122      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22123      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22124      */
22125     nanText : "{0} is not a valid number",
22126
22127     // private
22128     initEvents : function(){
22129         Roo.form.NumberField.superclass.initEvents.call(this);
22130         var allowed = "0123456789";
22131         if(this.allowDecimals){
22132             allowed += this.decimalSeparator;
22133         }
22134         if(this.allowNegative){
22135             allowed += "-";
22136         }
22137         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22138         var keyPress = function(e){
22139             var k = e.getKey();
22140             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22141                 return;
22142             }
22143             var c = e.getCharCode();
22144             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22145                 e.stopEvent();
22146             }
22147         };
22148         this.el.on("keypress", keyPress, this);
22149     },
22150
22151     // private
22152     validateValue : function(value){
22153         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22154             return false;
22155         }
22156         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22157              return true;
22158         }
22159         var num = this.parseValue(value);
22160         if(isNaN(num)){
22161             this.markInvalid(String.format(this.nanText, value));
22162             return false;
22163         }
22164         if(num < this.minValue){
22165             this.markInvalid(String.format(this.minText, this.minValue));
22166             return false;
22167         }
22168         if(num > this.maxValue){
22169             this.markInvalid(String.format(this.maxText, this.maxValue));
22170             return false;
22171         }
22172         return true;
22173     },
22174
22175     getValue : function(){
22176         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22177     },
22178
22179     // private
22180     parseValue : function(value){
22181         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22182         return isNaN(value) ? '' : value;
22183     },
22184
22185     // private
22186     fixPrecision : function(value){
22187         var nan = isNaN(value);
22188         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22189             return nan ? '' : value;
22190         }
22191         return parseFloat(value).toFixed(this.decimalPrecision);
22192     },
22193
22194     setValue : function(v){
22195         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22196     },
22197
22198     // private
22199     decimalPrecisionFcn : function(v){
22200         return Math.floor(v);
22201     },
22202
22203     beforeBlur : function(){
22204         var v = this.parseValue(this.getRawValue());
22205         if(v){
22206             this.setValue(this.fixPrecision(v));
22207         }
22208     }
22209 });/*
22210  * Based on:
22211  * Ext JS Library 1.1.1
22212  * Copyright(c) 2006-2007, Ext JS, LLC.
22213  *
22214  * Originally Released Under LGPL - original licence link has changed is not relivant.
22215  *
22216  * Fork - LGPL
22217  * <script type="text/javascript">
22218  */
22219  
22220 /**
22221  * @class Roo.form.DateField
22222  * @extends Roo.form.TriggerField
22223  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22224 * @constructor
22225 * Create a new DateField
22226 * @param {Object} config
22227  */
22228 Roo.form.DateField = function(config){
22229     Roo.form.DateField.superclass.constructor.call(this, config);
22230     
22231       this.addEvents({
22232          
22233         /**
22234          * @event select
22235          * Fires when a date is selected
22236              * @param {Roo.form.DateField} combo This combo box
22237              * @param {Date} date The date selected
22238              */
22239         'select' : true
22240          
22241     });
22242     
22243     
22244     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22245     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22246     this.ddMatch = null;
22247     if(this.disabledDates){
22248         var dd = this.disabledDates;
22249         var re = "(?:";
22250         for(var i = 0; i < dd.length; i++){
22251             re += dd[i];
22252             if(i != dd.length-1) re += "|";
22253         }
22254         this.ddMatch = new RegExp(re + ")");
22255     }
22256 };
22257
22258 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22259     /**
22260      * @cfg {String} format
22261      * The default date format string which can be overriden for localization support.  The format must be
22262      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22263      */
22264     format : "m/d/y",
22265     /**
22266      * @cfg {String} altFormats
22267      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22268      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22269      */
22270     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22271     /**
22272      * @cfg {Array} disabledDays
22273      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22274      */
22275     disabledDays : null,
22276     /**
22277      * @cfg {String} disabledDaysText
22278      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22279      */
22280     disabledDaysText : "Disabled",
22281     /**
22282      * @cfg {Array} disabledDates
22283      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22284      * expression so they are very powerful. Some examples:
22285      * <ul>
22286      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22287      * <li>["03/08", "09/16"] would disable those days for every year</li>
22288      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22289      * <li>["03/../2006"] would disable every day in March 2006</li>
22290      * <li>["^03"] would disable every day in every March</li>
22291      * </ul>
22292      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22293      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22294      */
22295     disabledDates : null,
22296     /**
22297      * @cfg {String} disabledDatesText
22298      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22299      */
22300     disabledDatesText : "Disabled",
22301     /**
22302      * @cfg {Date/String} minValue
22303      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22304      * valid format (defaults to null).
22305      */
22306     minValue : null,
22307     /**
22308      * @cfg {Date/String} maxValue
22309      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22310      * valid format (defaults to null).
22311      */
22312     maxValue : null,
22313     /**
22314      * @cfg {String} minText
22315      * The error text to display when the date in the cell is before minValue (defaults to
22316      * 'The date in this field must be after {minValue}').
22317      */
22318     minText : "The date in this field must be equal to or after {0}",
22319     /**
22320      * @cfg {String} maxText
22321      * The error text to display when the date in the cell is after maxValue (defaults to
22322      * 'The date in this field must be before {maxValue}').
22323      */
22324     maxText : "The date in this field must be equal to or before {0}",
22325     /**
22326      * @cfg {String} invalidText
22327      * The error text to display when the date in the field is invalid (defaults to
22328      * '{value} is not a valid date - it must be in the format {format}').
22329      */
22330     invalidText : "{0} is not a valid date - it must be in the format {1}",
22331     /**
22332      * @cfg {String} triggerClass
22333      * An additional CSS class used to style the trigger button.  The trigger will always get the
22334      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22335      * which displays a calendar icon).
22336      */
22337     triggerClass : 'x-form-date-trigger',
22338     
22339
22340     /**
22341      * @cfg {bool} useIso
22342      * if enabled, then the date field will use a hidden field to store the 
22343      * real value as iso formated date. default (false)
22344      */ 
22345     useIso : false,
22346     /**
22347      * @cfg {String/Object} autoCreate
22348      * A DomHelper element spec, or true for a default element spec (defaults to
22349      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22350      */ 
22351     // private
22352     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22353     
22354     // private
22355     hiddenField: false,
22356     
22357     onRender : function(ct, position)
22358     {
22359         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22360         if (this.useIso) {
22361             this.el.dom.removeAttribute('name'); 
22362             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22363                     'before', true);
22364             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22365             // prevent input submission
22366             this.hiddenName = this.name;
22367         }
22368             
22369             
22370     },
22371     
22372     // private
22373     validateValue : function(value)
22374     {
22375         value = this.formatDate(value);
22376         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22377             return false;
22378         }
22379         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22380              return true;
22381         }
22382         var svalue = value;
22383         value = this.parseDate(value);
22384         if(!value){
22385             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22386             return false;
22387         }
22388         var time = value.getTime();
22389         if(this.minValue && time < this.minValue.getTime()){
22390             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22391             return false;
22392         }
22393         if(this.maxValue && time > this.maxValue.getTime()){
22394             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22395             return false;
22396         }
22397         if(this.disabledDays){
22398             var day = value.getDay();
22399             for(var i = 0; i < this.disabledDays.length; i++) {
22400                 if(day === this.disabledDays[i]){
22401                     this.markInvalid(this.disabledDaysText);
22402                     return false;
22403                 }
22404             }
22405         }
22406         var fvalue = this.formatDate(value);
22407         if(this.ddMatch && this.ddMatch.test(fvalue)){
22408             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22409             return false;
22410         }
22411         return true;
22412     },
22413
22414     // private
22415     // Provides logic to override the default TriggerField.validateBlur which just returns true
22416     validateBlur : function(){
22417         return !this.menu || !this.menu.isVisible();
22418     },
22419
22420     /**
22421      * Returns the current date value of the date field.
22422      * @return {Date} The date value
22423      */
22424     getValue : function(){
22425         
22426         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22427     },
22428
22429     /**
22430      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22431      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22432      * (the default format used is "m/d/y").
22433      * <br />Usage:
22434      * <pre><code>
22435 //All of these calls set the same date value (May 4, 2006)
22436
22437 //Pass a date object:
22438 var dt = new Date('5/4/06');
22439 dateField.setValue(dt);
22440
22441 //Pass a date string (default format):
22442 dateField.setValue('5/4/06');
22443
22444 //Pass a date string (custom format):
22445 dateField.format = 'Y-m-d';
22446 dateField.setValue('2006-5-4');
22447 </code></pre>
22448      * @param {String/Date} date The date or valid date string
22449      */
22450     setValue : function(date){
22451         if (this.hiddenField) {
22452             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22453         }
22454         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22455     },
22456
22457     // private
22458     parseDate : function(value){
22459         if(!value || value instanceof Date){
22460             return value;
22461         }
22462         var v = Date.parseDate(value, this.format);
22463         if(!v && this.altFormats){
22464             if(!this.altFormatsArray){
22465                 this.altFormatsArray = this.altFormats.split("|");
22466             }
22467             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22468                 v = Date.parseDate(value, this.altFormatsArray[i]);
22469             }
22470         }
22471         return v;
22472     },
22473
22474     // private
22475     formatDate : function(date, fmt){
22476         return (!date || !(date instanceof Date)) ?
22477                date : date.dateFormat(fmt || this.format);
22478     },
22479
22480     // private
22481     menuListeners : {
22482         select: function(m, d){
22483             this.setValue(d);
22484             this.fireEvent('select', this, d);
22485         },
22486         show : function(){ // retain focus styling
22487             this.onFocus();
22488         },
22489         hide : function(){
22490             this.focus.defer(10, this);
22491             var ml = this.menuListeners;
22492             this.menu.un("select", ml.select,  this);
22493             this.menu.un("show", ml.show,  this);
22494             this.menu.un("hide", ml.hide,  this);
22495         }
22496     },
22497
22498     // private
22499     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22500     onTriggerClick : function(){
22501         if(this.disabled){
22502             return;
22503         }
22504         if(this.menu == null){
22505             this.menu = new Roo.menu.DateMenu();
22506         }
22507         Roo.apply(this.menu.picker,  {
22508             showClear: this.allowBlank,
22509             minDate : this.minValue,
22510             maxDate : this.maxValue,
22511             disabledDatesRE : this.ddMatch,
22512             disabledDatesText : this.disabledDatesText,
22513             disabledDays : this.disabledDays,
22514             disabledDaysText : this.disabledDaysText,
22515             format : this.format,
22516             minText : String.format(this.minText, this.formatDate(this.minValue)),
22517             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22518         });
22519         this.menu.on(Roo.apply({}, this.menuListeners, {
22520             scope:this
22521         }));
22522         this.menu.picker.setValue(this.getValue() || new Date());
22523         this.menu.show(this.el, "tl-bl?");
22524     },
22525
22526     beforeBlur : function(){
22527         var v = this.parseDate(this.getRawValue());
22528         if(v){
22529             this.setValue(v);
22530         }
22531     }
22532
22533     /** @cfg {Boolean} grow @hide */
22534     /** @cfg {Number} growMin @hide */
22535     /** @cfg {Number} growMax @hide */
22536     /**
22537      * @hide
22538      * @method autoSize
22539      */
22540 });/*
22541  * Based on:
22542  * Ext JS Library 1.1.1
22543  * Copyright(c) 2006-2007, Ext JS, LLC.
22544  *
22545  * Originally Released Under LGPL - original licence link has changed is not relivant.
22546  *
22547  * Fork - LGPL
22548  * <script type="text/javascript">
22549  */
22550  
22551
22552 /**
22553  * @class Roo.form.ComboBox
22554  * @extends Roo.form.TriggerField
22555  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22556  * @constructor
22557  * Create a new ComboBox.
22558  * @param {Object} config Configuration options
22559  */
22560 Roo.form.ComboBox = function(config){
22561     Roo.form.ComboBox.superclass.constructor.call(this, config);
22562     this.addEvents({
22563         /**
22564          * @event expand
22565          * Fires when the dropdown list is expanded
22566              * @param {Roo.form.ComboBox} combo This combo box
22567              */
22568         'expand' : true,
22569         /**
22570          * @event collapse
22571          * Fires when the dropdown list is collapsed
22572              * @param {Roo.form.ComboBox} combo This combo box
22573              */
22574         'collapse' : true,
22575         /**
22576          * @event beforeselect
22577          * Fires before a list item is selected. Return false to cancel the selection.
22578              * @param {Roo.form.ComboBox} combo This combo box
22579              * @param {Roo.data.Record} record The data record returned from the underlying store
22580              * @param {Number} index The index of the selected item in the dropdown list
22581              */
22582         'beforeselect' : true,
22583         /**
22584          * @event select
22585          * Fires when a list item is selected
22586              * @param {Roo.form.ComboBox} combo This combo box
22587              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22588              * @param {Number} index The index of the selected item in the dropdown list
22589              */
22590         'select' : true,
22591         /**
22592          * @event beforequery
22593          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22594          * The event object passed has these properties:
22595              * @param {Roo.form.ComboBox} combo This combo box
22596              * @param {String} query The query
22597              * @param {Boolean} forceAll true to force "all" query
22598              * @param {Boolean} cancel true to cancel the query
22599              * @param {Object} e The query event object
22600              */
22601         'beforequery': true,
22602          /**
22603          * @event add
22604          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22605              * @param {Roo.form.ComboBox} combo This combo box
22606              */
22607         'add' : true,
22608         /**
22609          * @event edit
22610          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22611              * @param {Roo.form.ComboBox} combo This combo box
22612              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22613              */
22614         'edit' : true
22615         
22616         
22617     });
22618     if(this.transform){
22619         this.allowDomMove = false;
22620         var s = Roo.getDom(this.transform);
22621         if(!this.hiddenName){
22622             this.hiddenName = s.name;
22623         }
22624         if(!this.store){
22625             this.mode = 'local';
22626             var d = [], opts = s.options;
22627             for(var i = 0, len = opts.length;i < len; i++){
22628                 var o = opts[i];
22629                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22630                 if(o.selected) {
22631                     this.value = value;
22632                 }
22633                 d.push([value, o.text]);
22634             }
22635             this.store = new Roo.data.SimpleStore({
22636                 'id': 0,
22637                 fields: ['value', 'text'],
22638                 data : d
22639             });
22640             this.valueField = 'value';
22641             this.displayField = 'text';
22642         }
22643         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22644         if(!this.lazyRender){
22645             this.target = true;
22646             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22647             s.parentNode.removeChild(s); // remove it
22648             this.render(this.el.parentNode);
22649         }else{
22650             s.parentNode.removeChild(s); // remove it
22651         }
22652
22653     }
22654     if (this.store) {
22655         this.store = Roo.factory(this.store, Roo.data);
22656     }
22657     
22658     this.selectedIndex = -1;
22659     if(this.mode == 'local'){
22660         if(config.queryDelay === undefined){
22661             this.queryDelay = 10;
22662         }
22663         if(config.minChars === undefined){
22664             this.minChars = 0;
22665         }
22666     }
22667 };
22668
22669 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22670     /**
22671      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22672      */
22673     /**
22674      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22675      * rendering into an Roo.Editor, defaults to false)
22676      */
22677     /**
22678      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22679      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22680      */
22681     /**
22682      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22683      */
22684     /**
22685      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22686      * the dropdown list (defaults to undefined, with no header element)
22687      */
22688
22689      /**
22690      * @cfg {String/Roo.Template} tpl The template to use to render the output
22691      */
22692      
22693     // private
22694     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22695     /**
22696      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22697      */
22698     listWidth: undefined,
22699     /**
22700      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22701      * mode = 'remote' or 'text' if mode = 'local')
22702      */
22703     displayField: undefined,
22704     /**
22705      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22706      * mode = 'remote' or 'value' if mode = 'local'). 
22707      * Note: use of a valueField requires the user make a selection
22708      * in order for a value to be mapped.
22709      */
22710     valueField: undefined,
22711     /**
22712      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22713      * field's data value (defaults to the underlying DOM element's name)
22714      */
22715     hiddenName: undefined,
22716     /**
22717      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22718      */
22719     listClass: '',
22720     /**
22721      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22722      */
22723     selectedClass: 'x-combo-selected',
22724     /**
22725      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22726      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22727      * which displays a downward arrow icon).
22728      */
22729     triggerClass : 'x-form-arrow-trigger',
22730     /**
22731      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22732      */
22733     shadow:'sides',
22734     /**
22735      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22736      * anchor positions (defaults to 'tl-bl')
22737      */
22738     listAlign: 'tl-bl?',
22739     /**
22740      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22741      */
22742     maxHeight: 300,
22743     /**
22744      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22745      * query specified by the allQuery config option (defaults to 'query')
22746      */
22747     triggerAction: 'query',
22748     /**
22749      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22750      * (defaults to 4, does not apply if editable = false)
22751      */
22752     minChars : 4,
22753     /**
22754      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22755      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22756      */
22757     typeAhead: false,
22758     /**
22759      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22760      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22761      */
22762     queryDelay: 500,
22763     /**
22764      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22765      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22766      */
22767     pageSize: 0,
22768     /**
22769      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22770      * when editable = true (defaults to false)
22771      */
22772     selectOnFocus:false,
22773     /**
22774      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22775      */
22776     queryParam: 'query',
22777     /**
22778      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22779      * when mode = 'remote' (defaults to 'Loading...')
22780      */
22781     loadingText: 'Loading...',
22782     /**
22783      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22784      */
22785     resizable: false,
22786     /**
22787      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22788      */
22789     handleHeight : 8,
22790     /**
22791      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22792      * traditional select (defaults to true)
22793      */
22794     editable: true,
22795     /**
22796      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22797      */
22798     allQuery: '',
22799     /**
22800      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22801      */
22802     mode: 'remote',
22803     /**
22804      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22805      * listWidth has a higher value)
22806      */
22807     minListWidth : 70,
22808     /**
22809      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22810      * allow the user to set arbitrary text into the field (defaults to false)
22811      */
22812     forceSelection:false,
22813     /**
22814      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22815      * if typeAhead = true (defaults to 250)
22816      */
22817     typeAheadDelay : 250,
22818     /**
22819      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22820      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22821      */
22822     valueNotFoundText : undefined,
22823     /**
22824      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22825      */
22826     blockFocus : false,
22827     
22828     /**
22829      * @cfg {Boolean} disableClear Disable showing of clear button.
22830      */
22831     disableClear : false,
22832     /**
22833      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22834      */
22835     alwaysQuery : false,
22836     
22837     //private
22838     addicon : false,
22839     editicon: false,
22840     
22841     
22842     // private
22843     onRender : function(ct, position){
22844         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22845         if(this.hiddenName){
22846             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22847                     'before', true);
22848             this.hiddenField.value =
22849                 this.hiddenValue !== undefined ? this.hiddenValue :
22850                 this.value !== undefined ? this.value : '';
22851
22852             // prevent input submission
22853             this.el.dom.removeAttribute('name');
22854         }
22855         if(Roo.isGecko){
22856             this.el.dom.setAttribute('autocomplete', 'off');
22857         }
22858
22859         var cls = 'x-combo-list';
22860
22861         this.list = new Roo.Layer({
22862             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22863         });
22864
22865         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22866         this.list.setWidth(lw);
22867         this.list.swallowEvent('mousewheel');
22868         this.assetHeight = 0;
22869
22870         if(this.title){
22871             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22872             this.assetHeight += this.header.getHeight();
22873         }
22874
22875         this.innerList = this.list.createChild({cls:cls+'-inner'});
22876         this.innerList.on('mouseover', this.onViewOver, this);
22877         this.innerList.on('mousemove', this.onViewMove, this);
22878         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22879         
22880         if(this.allowBlank && !this.pageSize && !this.disableClear){
22881             this.footer = this.list.createChild({cls:cls+'-ft'});
22882             this.pageTb = new Roo.Toolbar(this.footer);
22883            
22884         }
22885         if(this.pageSize){
22886             this.footer = this.list.createChild({cls:cls+'-ft'});
22887             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22888                     {pageSize: this.pageSize});
22889             
22890         }
22891         
22892         if (this.pageTb && this.allowBlank && !this.disableClear) {
22893             var _this = this;
22894             this.pageTb.add(new Roo.Toolbar.Fill(), {
22895                 cls: 'x-btn-icon x-btn-clear',
22896                 text: '&#160;',
22897                 handler: function()
22898                 {
22899                     _this.collapse();
22900                     _this.clearValue();
22901                     _this.onSelect(false, -1);
22902                 }
22903             });
22904         }
22905         if (this.footer) {
22906             this.assetHeight += this.footer.getHeight();
22907         }
22908         
22909
22910         if(!this.tpl){
22911             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22912         }
22913
22914         this.view = new Roo.View(this.innerList, this.tpl, {
22915             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22916         });
22917
22918         this.view.on('click', this.onViewClick, this);
22919
22920         this.store.on('beforeload', this.onBeforeLoad, this);
22921         this.store.on('load', this.onLoad, this);
22922         this.store.on('loadexception', this.collapse, this);
22923
22924         if(this.resizable){
22925             this.resizer = new Roo.Resizable(this.list,  {
22926                pinned:true, handles:'se'
22927             });
22928             this.resizer.on('resize', function(r, w, h){
22929                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22930                 this.listWidth = w;
22931                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22932                 this.restrictHeight();
22933             }, this);
22934             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22935         }
22936         if(!this.editable){
22937             this.editable = true;
22938             this.setEditable(false);
22939         }  
22940         
22941         
22942         if (typeof(this.events.add.listeners) != 'undefined') {
22943             
22944             this.addicon = this.wrap.createChild(
22945                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22946        
22947             this.addicon.on('click', function(e) {
22948                 this.fireEvent('add', this);
22949             }, this);
22950         }
22951         if (typeof(this.events.edit.listeners) != 'undefined') {
22952             
22953             this.editicon = this.wrap.createChild(
22954                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22955             if (this.addicon) {
22956                 this.editicon.setStyle('margin-left', '40px');
22957             }
22958             this.editicon.on('click', function(e) {
22959                 
22960                 // we fire even  if inothing is selected..
22961                 this.fireEvent('edit', this, this.lastData );
22962                 
22963             }, this);
22964         }
22965         
22966         
22967         
22968     },
22969
22970     // private
22971     initEvents : function(){
22972         Roo.form.ComboBox.superclass.initEvents.call(this);
22973
22974         this.keyNav = new Roo.KeyNav(this.el, {
22975             "up" : function(e){
22976                 this.inKeyMode = true;
22977                 this.selectPrev();
22978             },
22979
22980             "down" : function(e){
22981                 if(!this.isExpanded()){
22982                     this.onTriggerClick();
22983                 }else{
22984                     this.inKeyMode = true;
22985                     this.selectNext();
22986                 }
22987             },
22988
22989             "enter" : function(e){
22990                 this.onViewClick();
22991                 //return true;
22992             },
22993
22994             "esc" : function(e){
22995                 this.collapse();
22996             },
22997
22998             "tab" : function(e){
22999                 this.onViewClick(false);
23000                 return true;
23001             },
23002
23003             scope : this,
23004
23005             doRelay : function(foo, bar, hname){
23006                 if(hname == 'down' || this.scope.isExpanded()){
23007                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23008                 }
23009                 return true;
23010             },
23011
23012             forceKeyDown: true
23013         });
23014         this.queryDelay = Math.max(this.queryDelay || 10,
23015                 this.mode == 'local' ? 10 : 250);
23016         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23017         if(this.typeAhead){
23018             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23019         }
23020         if(this.editable !== false){
23021             this.el.on("keyup", this.onKeyUp, this);
23022         }
23023         if(this.forceSelection){
23024             this.on('blur', this.doForce, this);
23025         }
23026     },
23027
23028     onDestroy : function(){
23029         if(this.view){
23030             this.view.setStore(null);
23031             this.view.el.removeAllListeners();
23032             this.view.el.remove();
23033             this.view.purgeListeners();
23034         }
23035         if(this.list){
23036             this.list.destroy();
23037         }
23038         if(this.store){
23039             this.store.un('beforeload', this.onBeforeLoad, this);
23040             this.store.un('load', this.onLoad, this);
23041             this.store.un('loadexception', this.collapse, this);
23042         }
23043         Roo.form.ComboBox.superclass.onDestroy.call(this);
23044     },
23045
23046     // private
23047     fireKey : function(e){
23048         if(e.isNavKeyPress() && !this.list.isVisible()){
23049             this.fireEvent("specialkey", this, e);
23050         }
23051     },
23052
23053     // private
23054     onResize: function(w, h){
23055         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23056         
23057         if(typeof w != 'number'){
23058             // we do not handle it!?!?
23059             return;
23060         }
23061         var tw = this.trigger.getWidth();
23062         tw += this.addicon ? this.addicon.getWidth() : 0;
23063         tw += this.editicon ? this.editicon.getWidth() : 0;
23064         var x = w - tw;
23065         this.el.setWidth( this.adjustWidth('input', x));
23066             
23067         this.trigger.setStyle('left', x+'px');
23068         
23069         if(this.list && this.listWidth === undefined){
23070             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23071             this.list.setWidth(lw);
23072             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23073         }
23074         
23075     
23076         
23077     },
23078
23079     /**
23080      * Allow or prevent the user from directly editing the field text.  If false is passed,
23081      * the user will only be able to select from the items defined in the dropdown list.  This method
23082      * is the runtime equivalent of setting the 'editable' config option at config time.
23083      * @param {Boolean} value True to allow the user to directly edit the field text
23084      */
23085     setEditable : function(value){
23086         if(value == this.editable){
23087             return;
23088         }
23089         this.editable = value;
23090         if(!value){
23091             this.el.dom.setAttribute('readOnly', true);
23092             this.el.on('mousedown', this.onTriggerClick,  this);
23093             this.el.addClass('x-combo-noedit');
23094         }else{
23095             this.el.dom.setAttribute('readOnly', false);
23096             this.el.un('mousedown', this.onTriggerClick,  this);
23097             this.el.removeClass('x-combo-noedit');
23098         }
23099     },
23100
23101     // private
23102     onBeforeLoad : function(){
23103         if(!this.hasFocus){
23104             return;
23105         }
23106         this.innerList.update(this.loadingText ?
23107                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23108         this.restrictHeight();
23109         this.selectedIndex = -1;
23110     },
23111
23112     // private
23113     onLoad : function(){
23114         if(!this.hasFocus){
23115             return;
23116         }
23117         if(this.store.getCount() > 0){
23118             this.expand();
23119             this.restrictHeight();
23120             if(this.lastQuery == this.allQuery){
23121                 if(this.editable){
23122                     this.el.dom.select();
23123                 }
23124                 if(!this.selectByValue(this.value, true)){
23125                     this.select(0, true);
23126                 }
23127             }else{
23128                 this.selectNext();
23129                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23130                     this.taTask.delay(this.typeAheadDelay);
23131                 }
23132             }
23133         }else{
23134             this.onEmptyResults();
23135         }
23136         //this.el.focus();
23137     },
23138
23139     // private
23140     onTypeAhead : function(){
23141         if(this.store.getCount() > 0){
23142             var r = this.store.getAt(0);
23143             var newValue = r.data[this.displayField];
23144             var len = newValue.length;
23145             var selStart = this.getRawValue().length;
23146             if(selStart != len){
23147                 this.setRawValue(newValue);
23148                 this.selectText(selStart, newValue.length);
23149             }
23150         }
23151     },
23152
23153     // private
23154     onSelect : function(record, index){
23155         if(this.fireEvent('beforeselect', this, record, index) !== false){
23156             this.setFromData(index > -1 ? record.data : false);
23157             this.collapse();
23158             this.fireEvent('select', this, record, index);
23159         }
23160     },
23161
23162     /**
23163      * Returns the currently selected field value or empty string if no value is set.
23164      * @return {String} value The selected value
23165      */
23166     getValue : function(){
23167         if(this.valueField){
23168             return typeof this.value != 'undefined' ? this.value : '';
23169         }else{
23170             return Roo.form.ComboBox.superclass.getValue.call(this);
23171         }
23172     },
23173
23174     /**
23175      * Clears any text/value currently set in the field
23176      */
23177     clearValue : function(){
23178         if(this.hiddenField){
23179             this.hiddenField.value = '';
23180         }
23181         this.value = '';
23182         this.setRawValue('');
23183         this.lastSelectionText = '';
23184         this.applyEmptyText();
23185     },
23186
23187     /**
23188      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23189      * will be displayed in the field.  If the value does not match the data value of an existing item,
23190      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23191      * Otherwise the field will be blank (although the value will still be set).
23192      * @param {String} value The value to match
23193      */
23194     setValue : function(v){
23195         var text = v;
23196         if(this.valueField){
23197             var r = this.findRecord(this.valueField, v);
23198             if(r){
23199                 text = r.data[this.displayField];
23200             }else if(this.valueNotFoundText !== undefined){
23201                 text = this.valueNotFoundText;
23202             }
23203         }
23204         this.lastSelectionText = text;
23205         if(this.hiddenField){
23206             this.hiddenField.value = v;
23207         }
23208         Roo.form.ComboBox.superclass.setValue.call(this, text);
23209         this.value = v;
23210     },
23211     /**
23212      * @property {Object} the last set data for the element
23213      */
23214     
23215     lastData : false,
23216     /**
23217      * Sets the value of the field based on a object which is related to the record format for the store.
23218      * @param {Object} value the value to set as. or false on reset?
23219      */
23220     setFromData : function(o){
23221         var dv = ''; // display value
23222         var vv = ''; // value value..
23223         this.lastData = o;
23224         if (this.displayField) {
23225             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23226         } else {
23227             // this is an error condition!!!
23228             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23229         }
23230         
23231         if(this.valueField){
23232             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23233         }
23234         if(this.hiddenField){
23235             this.hiddenField.value = vv;
23236             
23237             this.lastSelectionText = dv;
23238             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23239             this.value = vv;
23240             return;
23241         }
23242         // no hidden field.. - we store the value in 'value', but still display
23243         // display field!!!!
23244         this.lastSelectionText = dv;
23245         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23246         this.value = vv;
23247         
23248         
23249     },
23250     // private
23251     reset : function(){
23252         // overridden so that last data is reset..
23253         this.setValue(this.originalValue);
23254         this.clearInvalid();
23255         this.lastData = false;
23256     },
23257     // private
23258     findRecord : function(prop, value){
23259         var record;
23260         if(this.store.getCount() > 0){
23261             this.store.each(function(r){
23262                 if(r.data[prop] == value){
23263                     record = r;
23264                     return false;
23265                 }
23266             });
23267         }
23268         return record;
23269     },
23270
23271     // private
23272     onViewMove : function(e, t){
23273         this.inKeyMode = false;
23274     },
23275
23276     // private
23277     onViewOver : function(e, t){
23278         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23279             return;
23280         }
23281         var item = this.view.findItemFromChild(t);
23282         if(item){
23283             var index = this.view.indexOf(item);
23284             this.select(index, false);
23285         }
23286     },
23287
23288     // private
23289     onViewClick : function(doFocus){
23290         var index = this.view.getSelectedIndexes()[0];
23291         var r = this.store.getAt(index);
23292         if(r){
23293             this.onSelect(r, index);
23294         }
23295         if(doFocus !== false && !this.blockFocus){
23296             this.el.focus();
23297         }
23298     },
23299
23300     // private
23301     restrictHeight : function(){
23302         this.innerList.dom.style.height = '';
23303         var inner = this.innerList.dom;
23304         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23305         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23306         this.list.beginUpdate();
23307         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23308         this.list.alignTo(this.el, this.listAlign);
23309         this.list.endUpdate();
23310     },
23311
23312     // private
23313     onEmptyResults : function(){
23314         this.collapse();
23315     },
23316
23317     /**
23318      * Returns true if the dropdown list is expanded, else false.
23319      */
23320     isExpanded : function(){
23321         return this.list.isVisible();
23322     },
23323
23324     /**
23325      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23326      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23327      * @param {String} value The data value of the item to select
23328      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23329      * selected item if it is not currently in view (defaults to true)
23330      * @return {Boolean} True if the value matched an item in the list, else false
23331      */
23332     selectByValue : function(v, scrollIntoView){
23333         if(v !== undefined && v !== null){
23334             var r = this.findRecord(this.valueField || this.displayField, v);
23335             if(r){
23336                 this.select(this.store.indexOf(r), scrollIntoView);
23337                 return true;
23338             }
23339         }
23340         return false;
23341     },
23342
23343     /**
23344      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23345      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23346      * @param {Number} index The zero-based index of the list item to select
23347      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23348      * selected item if it is not currently in view (defaults to true)
23349      */
23350     select : function(index, scrollIntoView){
23351         this.selectedIndex = index;
23352         this.view.select(index);
23353         if(scrollIntoView !== false){
23354             var el = this.view.getNode(index);
23355             if(el){
23356                 this.innerList.scrollChildIntoView(el, false);
23357             }
23358         }
23359     },
23360
23361     // private
23362     selectNext : 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 < ct-1){
23368                 this.select(this.selectedIndex+1);
23369             }
23370         }
23371     },
23372
23373     // private
23374     selectPrev : function(){
23375         var ct = this.store.getCount();
23376         if(ct > 0){
23377             if(this.selectedIndex == -1){
23378                 this.select(0);
23379             }else if(this.selectedIndex != 0){
23380                 this.select(this.selectedIndex-1);
23381             }
23382         }
23383     },
23384
23385     // private
23386     onKeyUp : function(e){
23387         if(this.editable !== false && !e.isSpecialKey()){
23388             this.lastKey = e.getKey();
23389             this.dqTask.delay(this.queryDelay);
23390         }
23391     },
23392
23393     // private
23394     validateBlur : function(){
23395         return !this.list || !this.list.isVisible();   
23396     },
23397
23398     // private
23399     initQuery : function(){
23400         this.doQuery(this.getRawValue());
23401     },
23402
23403     // private
23404     doForce : function(){
23405         if(this.el.dom.value.length > 0){
23406             this.el.dom.value =
23407                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23408             this.applyEmptyText();
23409         }
23410     },
23411
23412     /**
23413      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23414      * query allowing the query action to be canceled if needed.
23415      * @param {String} query The SQL query to execute
23416      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23417      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23418      * saved in the current store (defaults to false)
23419      */
23420     doQuery : function(q, forceAll){
23421         if(q === undefined || q === null){
23422             q = '';
23423         }
23424         var qe = {
23425             query: q,
23426             forceAll: forceAll,
23427             combo: this,
23428             cancel:false
23429         };
23430         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23431             return false;
23432         }
23433         q = qe.query;
23434         forceAll = qe.forceAll;
23435         if(forceAll === true || (q.length >= this.minChars)){
23436             if(this.lastQuery != q || this.alwaysQuery){
23437                 this.lastQuery = q;
23438                 if(this.mode == 'local'){
23439                     this.selectedIndex = -1;
23440                     if(forceAll){
23441                         this.store.clearFilter();
23442                     }else{
23443                         this.store.filter(this.displayField, q);
23444                     }
23445                     this.onLoad();
23446                 }else{
23447                     this.store.baseParams[this.queryParam] = q;
23448                     this.store.load({
23449                         params: this.getParams(q)
23450                     });
23451                     this.expand();
23452                 }
23453             }else{
23454                 this.selectedIndex = -1;
23455                 this.onLoad();   
23456             }
23457         }
23458     },
23459
23460     // private
23461     getParams : function(q){
23462         var p = {};
23463         //p[this.queryParam] = q;
23464         if(this.pageSize){
23465             p.start = 0;
23466             p.limit = this.pageSize;
23467         }
23468         return p;
23469     },
23470
23471     /**
23472      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23473      */
23474     collapse : function(){
23475         if(!this.isExpanded()){
23476             return;
23477         }
23478         this.list.hide();
23479         Roo.get(document).un('mousedown', this.collapseIf, this);
23480         Roo.get(document).un('mousewheel', this.collapseIf, this);
23481         if (!this.editable) {
23482             Roo.get(document).un('keydown', this.listKeyPress, this);
23483         }
23484         this.fireEvent('collapse', this);
23485     },
23486
23487     // private
23488     collapseIf : function(e){
23489         if(!e.within(this.wrap) && !e.within(this.list)){
23490             this.collapse();
23491         }
23492     },
23493
23494     /**
23495      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23496      */
23497     expand : function(){
23498         if(this.isExpanded() || !this.hasFocus){
23499             return;
23500         }
23501         this.list.alignTo(this.el, this.listAlign);
23502         this.list.show();
23503         Roo.get(document).on('mousedown', this.collapseIf, this);
23504         Roo.get(document).on('mousewheel', this.collapseIf, this);
23505         if (!this.editable) {
23506             Roo.get(document).on('keydown', this.listKeyPress, this);
23507         }
23508         
23509         this.fireEvent('expand', this);
23510     },
23511
23512     // private
23513     // Implements the default empty TriggerField.onTriggerClick function
23514     onTriggerClick : function(){
23515         if(this.disabled){
23516             return;
23517         }
23518         if(this.isExpanded()){
23519             this.collapse();
23520             if (!this.blockFocus) {
23521                 this.el.focus();
23522             }
23523             
23524         }else {
23525             this.hasFocus = true;
23526             if(this.triggerAction == 'all') {
23527                 this.doQuery(this.allQuery, true);
23528             } else {
23529                 this.doQuery(this.getRawValue());
23530             }
23531             if (!this.blockFocus) {
23532                 this.el.focus();
23533             }
23534         }
23535     },
23536     listKeyPress : function(e)
23537     {
23538         //Roo.log('listkeypress');
23539         // scroll to first matching element based on key pres..
23540         if (e.isSpecialKey()) {
23541             return false;
23542         }
23543         var k = String.fromCharCode(e.getKey()).toUpperCase();
23544         //Roo.log(k);
23545         var match  = false;
23546         var csel = this.view.getSelectedNodes();
23547         var cselitem = false;
23548         if (csel.length) {
23549             var ix = this.view.indexOf(csel[0]);
23550             cselitem  = this.store.getAt(ix);
23551             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23552                 cselitem = false;
23553             }
23554             
23555         }
23556         
23557         this.store.each(function(v) { 
23558             if (cselitem) {
23559                 // start at existing selection.
23560                 if (cselitem.id == v.id) {
23561                     cselitem = false;
23562                 }
23563                 return;
23564             }
23565                 
23566             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23567                 match = this.store.indexOf(v);
23568                 return false;
23569             }
23570         }, this);
23571         
23572         if (match === false) {
23573             return true; // no more action?
23574         }
23575         // scroll to?
23576         this.view.select(match);
23577         var sn = Roo.get(this.view.getSelectedNodes()[0])
23578         sn.scrollIntoView(sn.dom.parentNode, false);
23579     }
23580
23581     /** 
23582     * @cfg {Boolean} grow 
23583     * @hide 
23584     */
23585     /** 
23586     * @cfg {Number} growMin 
23587     * @hide 
23588     */
23589     /** 
23590     * @cfg {Number} growMax 
23591     * @hide 
23592     */
23593     /**
23594      * @hide
23595      * @method autoSize
23596      */
23597 });/*
23598  * Based on:
23599  * Ext JS Library 1.1.1
23600  * Copyright(c) 2006-2007, Ext JS, LLC.
23601  *
23602  * Originally Released Under LGPL - original licence link has changed is not relivant.
23603  *
23604  * Fork - LGPL
23605  * <script type="text/javascript">
23606  */
23607 /**
23608  * @class Roo.form.Checkbox
23609  * @extends Roo.form.Field
23610  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23611  * @constructor
23612  * Creates a new Checkbox
23613  * @param {Object} config Configuration options
23614  */
23615 Roo.form.Checkbox = function(config){
23616     Roo.form.Checkbox.superclass.constructor.call(this, config);
23617     this.addEvents({
23618         /**
23619          * @event check
23620          * Fires when the checkbox is checked or unchecked.
23621              * @param {Roo.form.Checkbox} this This checkbox
23622              * @param {Boolean} checked The new checked value
23623              */
23624         check : true
23625     });
23626 };
23627
23628 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23629     /**
23630      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23631      */
23632     focusClass : undefined,
23633     /**
23634      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23635      */
23636     fieldClass: "x-form-field",
23637     /**
23638      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23639      */
23640     checked: false,
23641     /**
23642      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23643      * {tag: "input", type: "checkbox", autocomplete: "off"})
23644      */
23645     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23646     /**
23647      * @cfg {String} boxLabel The text that appears beside the checkbox
23648      */
23649     boxLabel : "",
23650     /**
23651      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23652      */  
23653     inputValue : '1',
23654     /**
23655      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23656      */
23657      valueOff: '0', // value when not checked..
23658
23659     actionMode : 'viewEl', 
23660     //
23661     // private
23662     itemCls : 'x-menu-check-item x-form-item',
23663     groupClass : 'x-menu-group-item',
23664     inputType : 'hidden',
23665     
23666     
23667     inSetChecked: false, // check that we are not calling self...
23668     
23669     inputElement: false, // real input element?
23670     basedOn: false, // ????
23671     
23672     isFormField: true, // not sure where this is needed!!!!
23673
23674     onResize : function(){
23675         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23676         if(!this.boxLabel){
23677             this.el.alignTo(this.wrap, 'c-c');
23678         }
23679     },
23680
23681     initEvents : function(){
23682         Roo.form.Checkbox.superclass.initEvents.call(this);
23683         this.el.on("click", this.onClick,  this);
23684         this.el.on("change", this.onClick,  this);
23685     },
23686
23687
23688     getResizeEl : function(){
23689         return this.wrap;
23690     },
23691
23692     getPositionEl : function(){
23693         return this.wrap;
23694     },
23695
23696     // private
23697     onRender : function(ct, position){
23698         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23699         /*
23700         if(this.inputValue !== undefined){
23701             this.el.dom.value = this.inputValue;
23702         }
23703         */
23704         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23705         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23706         var viewEl = this.wrap.createChild({ 
23707             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23708         this.viewEl = viewEl;   
23709         this.wrap.on('click', this.onClick,  this); 
23710         
23711         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23712         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23713         
23714         
23715         
23716         if(this.boxLabel){
23717             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23718         //    viewEl.on('click', this.onClick,  this); 
23719         }
23720         //if(this.checked){
23721             this.setChecked(this.checked);
23722         //}else{
23723             //this.checked = this.el.dom;
23724         //}
23725
23726     },
23727
23728     // private
23729     initValue : Roo.emptyFn,
23730
23731     /**
23732      * Returns the checked state of the checkbox.
23733      * @return {Boolean} True if checked, else false
23734      */
23735     getValue : function(){
23736         if(this.el){
23737             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23738         }
23739         return this.valueOff;
23740         
23741     },
23742
23743         // private
23744     onClick : function(){ 
23745         this.setChecked(!this.checked);
23746
23747         //if(this.el.dom.checked != this.checked){
23748         //    this.setValue(this.el.dom.checked);
23749        // }
23750     },
23751
23752     /**
23753      * Sets the checked state of the checkbox.
23754      * On is always based on a string comparison between inputValue and the param.
23755      * @param {Boolean/String} value - the value to set 
23756      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23757      */
23758     setValue : function(v,suppressEvent){
23759         
23760         
23761         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23762         //if(this.el && this.el.dom){
23763         //    this.el.dom.checked = this.checked;
23764         //    this.el.dom.defaultChecked = this.checked;
23765         //}
23766         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23767         //this.fireEvent("check", this, this.checked);
23768     },
23769     // private..
23770     setChecked : function(state,suppressEvent)
23771     {
23772         if (this.inSetChecked) {
23773             this.checked = state;
23774             return;
23775         }
23776         
23777     
23778         if(this.wrap){
23779             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23780         }
23781         this.checked = state;
23782         if(suppressEvent !== true){
23783             this.fireEvent('check', this, state);
23784         }
23785         this.inSetChecked = true;
23786         this.el.dom.value = state ? this.inputValue : this.valueOff;
23787         this.inSetChecked = false;
23788         
23789     },
23790     // handle setting of hidden value by some other method!!?!?
23791     setFromHidden: function()
23792     {
23793         if(!this.el){
23794             return;
23795         }
23796         //console.log("SET FROM HIDDEN");
23797         //alert('setFrom hidden');
23798         this.setValue(this.el.dom.value);
23799     },
23800     
23801     onDestroy : function()
23802     {
23803         if(this.viewEl){
23804             Roo.get(this.viewEl).remove();
23805         }
23806          
23807         Roo.form.Checkbox.superclass.onDestroy.call(this);
23808     }
23809
23810 });/*
23811  * Based on:
23812  * Ext JS Library 1.1.1
23813  * Copyright(c) 2006-2007, Ext JS, LLC.
23814  *
23815  * Originally Released Under LGPL - original licence link has changed is not relivant.
23816  *
23817  * Fork - LGPL
23818  * <script type="text/javascript">
23819  */
23820  
23821 /**
23822  * @class Roo.form.Radio
23823  * @extends Roo.form.Checkbox
23824  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23825  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23826  * @constructor
23827  * Creates a new Radio
23828  * @param {Object} config Configuration options
23829  */
23830 Roo.form.Radio = function(){
23831     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23832 };
23833 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23834     inputType: 'radio',
23835
23836     /**
23837      * If this radio is part of a group, it will return the selected value
23838      * @return {String}
23839      */
23840     getGroupValue : function(){
23841         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23842     }
23843 });//<script type="text/javascript">
23844
23845 /*
23846  * Ext JS Library 1.1.1
23847  * Copyright(c) 2006-2007, Ext JS, LLC.
23848  * licensing@extjs.com
23849  * 
23850  * http://www.extjs.com/license
23851  */
23852  
23853  /*
23854   * 
23855   * Known bugs:
23856   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23857   * - IE ? - no idea how much works there.
23858   * 
23859   * 
23860   * 
23861   */
23862  
23863
23864 /**
23865  * @class Ext.form.HtmlEditor
23866  * @extends Ext.form.Field
23867  * Provides a lightweight HTML Editor component.
23868  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23869  * 
23870  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23871  * supported by this editor.</b><br/><br/>
23872  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23873  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23874  */
23875 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23876       /**
23877      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23878      */
23879     toolbars : false,
23880     /**
23881      * @cfg {String} createLinkText The default text for the create link prompt
23882      */
23883     createLinkText : 'Please enter the URL for the link:',
23884     /**
23885      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23886      */
23887     defaultLinkValue : 'http:/'+'/',
23888    
23889     
23890     // id of frame..
23891     frameId: false,
23892     
23893     // private properties
23894     validationEvent : false,
23895     deferHeight: true,
23896     initialized : false,
23897     activated : false,
23898     sourceEditMode : false,
23899     onFocus : Roo.emptyFn,
23900     iframePad:3,
23901     hideMode:'offsets',
23902     defaultAutoCreate : {
23903         tag: "textarea",
23904         style:"width:500px;height:300px;",
23905         autocomplete: "off"
23906     },
23907
23908     // private
23909     initComponent : function(){
23910         this.addEvents({
23911             /**
23912              * @event initialize
23913              * Fires when the editor is fully initialized (including the iframe)
23914              * @param {HtmlEditor} this
23915              */
23916             initialize: true,
23917             /**
23918              * @event activate
23919              * Fires when the editor is first receives the focus. Any insertion must wait
23920              * until after this event.
23921              * @param {HtmlEditor} this
23922              */
23923             activate: true,
23924              /**
23925              * @event beforesync
23926              * Fires before the textarea is updated with content from the editor iframe. Return false
23927              * to cancel the sync.
23928              * @param {HtmlEditor} this
23929              * @param {String} html
23930              */
23931             beforesync: true,
23932              /**
23933              * @event beforepush
23934              * Fires before the iframe editor is updated with content from the textarea. Return false
23935              * to cancel the push.
23936              * @param {HtmlEditor} this
23937              * @param {String} html
23938              */
23939             beforepush: true,
23940              /**
23941              * @event sync
23942              * Fires when the textarea is updated with content from the editor iframe.
23943              * @param {HtmlEditor} this
23944              * @param {String} html
23945              */
23946             sync: true,
23947              /**
23948              * @event push
23949              * Fires when the iframe editor is updated with content from the textarea.
23950              * @param {HtmlEditor} this
23951              * @param {String} html
23952              */
23953             push: true,
23954              /**
23955              * @event editmodechange
23956              * Fires when the editor switches edit modes
23957              * @param {HtmlEditor} this
23958              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23959              */
23960             editmodechange: true,
23961             /**
23962              * @event editorevent
23963              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23964              * @param {HtmlEditor} this
23965              */
23966             editorevent: true
23967         })
23968     },
23969
23970     /**
23971      * Protected method that will not generally be called directly. It
23972      * is called when the editor creates its toolbar. Override this method if you need to
23973      * add custom toolbar buttons.
23974      * @param {HtmlEditor} editor
23975      */
23976     createToolbar : function(editor){
23977         if (!editor.toolbars || !editor.toolbars.length) {
23978             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23979         }
23980         
23981         for (var i =0 ; i < editor.toolbars.length;i++) {
23982             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23983             editor.toolbars[i].init(editor);
23984         }
23985          
23986         
23987     },
23988
23989     /**
23990      * Protected method that will not generally be called directly. It
23991      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23992      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23993      */
23994     getDocMarkup : function(){
23995         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23996     },
23997
23998     // private
23999     onRender : function(ct, position){
24000         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24001         this.el.dom.style.border = '0 none';
24002         this.el.dom.setAttribute('tabIndex', -1);
24003         this.el.addClass('x-hidden');
24004         if(Roo.isIE){ // fix IE 1px bogus margin
24005             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24006         }
24007         this.wrap = this.el.wrap({
24008             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24009         });
24010
24011         this.frameId = Roo.id();
24012         this.createToolbar(this);
24013         
24014         
24015         
24016         
24017       
24018         
24019         var iframe = this.wrap.createChild({
24020             tag: 'iframe',
24021             id: this.frameId,
24022             name: this.frameId,
24023             frameBorder : 'no',
24024             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24025         });
24026         
24027        // console.log(iframe);
24028         //this.wrap.dom.appendChild(iframe);
24029
24030         this.iframe = iframe.dom;
24031
24032          this.assignDocWin();
24033         
24034         this.doc.designMode = 'on';
24035        
24036         this.doc.open();
24037         this.doc.write(this.getDocMarkup());
24038         this.doc.close();
24039
24040         
24041         var task = { // must defer to wait for browser to be ready
24042             run : function(){
24043                 //console.log("run task?" + this.doc.readyState);
24044                 this.assignDocWin();
24045                 if(this.doc.body || this.doc.readyState == 'complete'){
24046                     try {
24047                         this.doc.designMode="on";
24048                     } catch (e) {
24049                         return;
24050                     }
24051                     Roo.TaskMgr.stop(task);
24052                     this.initEditor.defer(10, this);
24053                 }
24054             },
24055             interval : 10,
24056             duration:10000,
24057             scope: this
24058         };
24059         Roo.TaskMgr.start(task);
24060
24061         if(!this.width){
24062             this.setSize(this.el.getSize());
24063         }
24064     },
24065
24066     // private
24067     onResize : function(w, h){
24068         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24069         if(this.el && this.iframe){
24070             if(typeof w == 'number'){
24071                 var aw = w - this.wrap.getFrameWidth('lr');
24072                 this.el.setWidth(this.adjustWidth('textarea', aw));
24073                 this.iframe.style.width = aw + 'px';
24074             }
24075             if(typeof h == 'number'){
24076                 var tbh = 0;
24077                 for (var i =0; i < this.toolbars.length;i++) {
24078                     // fixme - ask toolbars for heights?
24079                     tbh += this.toolbars[i].tb.el.getHeight();
24080                 }
24081                 
24082                 
24083                 
24084                 
24085                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24086                 this.el.setHeight(this.adjustWidth('textarea', ah));
24087                 this.iframe.style.height = ah + 'px';
24088                 if(this.doc){
24089                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24090                 }
24091             }
24092         }
24093     },
24094
24095     /**
24096      * Toggles the editor between standard and source edit mode.
24097      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24098      */
24099     toggleSourceEdit : function(sourceEditMode){
24100         
24101         this.sourceEditMode = sourceEditMode === true;
24102         
24103         if(this.sourceEditMode){
24104           
24105             this.syncValue();
24106             this.iframe.className = 'x-hidden';
24107             this.el.removeClass('x-hidden');
24108             this.el.dom.removeAttribute('tabIndex');
24109             this.el.focus();
24110         }else{
24111              
24112             this.pushValue();
24113             this.iframe.className = '';
24114             this.el.addClass('x-hidden');
24115             this.el.dom.setAttribute('tabIndex', -1);
24116             this.deferFocus();
24117         }
24118         this.setSize(this.wrap.getSize());
24119         this.fireEvent('editmodechange', this, this.sourceEditMode);
24120     },
24121
24122     // private used internally
24123     createLink : function(){
24124         var url = prompt(this.createLinkText, this.defaultLinkValue);
24125         if(url && url != 'http:/'+'/'){
24126             this.relayCmd('createlink', url);
24127         }
24128     },
24129
24130     // private (for BoxComponent)
24131     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24132
24133     // private (for BoxComponent)
24134     getResizeEl : function(){
24135         return this.wrap;
24136     },
24137
24138     // private (for BoxComponent)
24139     getPositionEl : function(){
24140         return this.wrap;
24141     },
24142
24143     // private
24144     initEvents : function(){
24145         this.originalValue = this.getValue();
24146     },
24147
24148     /**
24149      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24150      * @method
24151      */
24152     markInvalid : Roo.emptyFn,
24153     /**
24154      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24155      * @method
24156      */
24157     clearInvalid : Roo.emptyFn,
24158
24159     setValue : function(v){
24160         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24161         this.pushValue();
24162     },
24163
24164     /**
24165      * Protected method that will not generally be called directly. If you need/want
24166      * custom HTML cleanup, this is the method you should override.
24167      * @param {String} html The HTML to be cleaned
24168      * return {String} The cleaned HTML
24169      */
24170     cleanHtml : function(html){
24171         html = String(html);
24172         if(html.length > 5){
24173             if(Roo.isSafari){ // strip safari nonsense
24174                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24175             }
24176         }
24177         if(html == '&nbsp;'){
24178             html = '';
24179         }
24180         return html;
24181     },
24182
24183     /**
24184      * Protected method that will not generally be called directly. Syncs the contents
24185      * of the editor iframe with the textarea.
24186      */
24187     syncValue : function(){
24188         if(this.initialized){
24189             var bd = (this.doc.body || this.doc.documentElement);
24190             var html = bd.innerHTML;
24191             if(Roo.isSafari){
24192                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24193                 var m = bs.match(/text-align:(.*?);/i);
24194                 if(m && m[1]){
24195                     html = '<div style="'+m[0]+'">' + html + '</div>';
24196                 }
24197             }
24198             html = this.cleanHtml(html);
24199             if(this.fireEvent('beforesync', this, html) !== false){
24200                 this.el.dom.value = html;
24201                 this.fireEvent('sync', this, html);
24202             }
24203         }
24204     },
24205
24206     /**
24207      * Protected method that will not generally be called directly. Pushes the value of the textarea
24208      * into the iframe editor.
24209      */
24210     pushValue : function(){
24211         if(this.initialized){
24212             var v = this.el.dom.value;
24213             if(v.length < 1){
24214                 v = '&#160;';
24215             }
24216             if(this.fireEvent('beforepush', this, v) !== false){
24217                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24218                 this.fireEvent('push', this, v);
24219             }
24220         }
24221     },
24222
24223     // private
24224     deferFocus : function(){
24225         this.focus.defer(10, this);
24226     },
24227
24228     // doc'ed in Field
24229     focus : function(){
24230         if(this.win && !this.sourceEditMode){
24231             this.win.focus();
24232         }else{
24233             this.el.focus();
24234         }
24235     },
24236     
24237     assignDocWin: function()
24238     {
24239         var iframe = this.iframe;
24240         
24241          if(Roo.isIE){
24242             this.doc = iframe.contentWindow.document;
24243             this.win = iframe.contentWindow;
24244         } else {
24245             if (!Roo.get(this.frameId)) {
24246                 return;
24247             }
24248             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24249             this.win = Roo.get(this.frameId).dom.contentWindow;
24250         }
24251     },
24252     
24253     // private
24254     initEditor : function(){
24255         //console.log("INIT EDITOR");
24256         this.assignDocWin();
24257         
24258         
24259         
24260         this.doc.designMode="on";
24261         this.doc.open();
24262         this.doc.write(this.getDocMarkup());
24263         this.doc.close();
24264         
24265         var dbody = (this.doc.body || this.doc.documentElement);
24266         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24267         // this copies styles from the containing element into thsi one..
24268         // not sure why we need all of this..
24269         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24270         ss['background-attachment'] = 'fixed'; // w3c
24271         dbody.bgProperties = 'fixed'; // ie
24272         Roo.DomHelper.applyStyles(dbody, ss);
24273         Roo.EventManager.on(this.doc, {
24274             'mousedown': this.onEditorEvent,
24275             'dblclick': this.onEditorEvent,
24276             'click': this.onEditorEvent,
24277             'keyup': this.onEditorEvent,
24278             buffer:100,
24279             scope: this
24280         });
24281         if(Roo.isGecko){
24282             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24283         }
24284         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24285             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24286         }
24287         this.initialized = true;
24288
24289         this.fireEvent('initialize', this);
24290         this.pushValue();
24291     },
24292
24293     // private
24294     onDestroy : function(){
24295         
24296         
24297         
24298         if(this.rendered){
24299             
24300             for (var i =0; i < this.toolbars.length;i++) {
24301                 // fixme - ask toolbars for heights?
24302                 this.toolbars[i].onDestroy();
24303             }
24304             
24305             this.wrap.dom.innerHTML = '';
24306             this.wrap.remove();
24307         }
24308     },
24309
24310     // private
24311     onFirstFocus : function(){
24312         
24313         this.assignDocWin();
24314         
24315         
24316         this.activated = true;
24317         for (var i =0; i < this.toolbars.length;i++) {
24318             this.toolbars[i].onFirstFocus();
24319         }
24320        
24321         if(Roo.isGecko){ // prevent silly gecko errors
24322             this.win.focus();
24323             var s = this.win.getSelection();
24324             if(!s.focusNode || s.focusNode.nodeType != 3){
24325                 var r = s.getRangeAt(0);
24326                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24327                 r.collapse(true);
24328                 this.deferFocus();
24329             }
24330             try{
24331                 this.execCmd('useCSS', true);
24332                 this.execCmd('styleWithCSS', false);
24333             }catch(e){}
24334         }
24335         this.fireEvent('activate', this);
24336     },
24337
24338     // private
24339     adjustFont: function(btn){
24340         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24341         //if(Roo.isSafari){ // safari
24342         //    adjust *= 2;
24343        // }
24344         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24345         if(Roo.isSafari){ // safari
24346             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24347             v =  (v < 10) ? 10 : v;
24348             v =  (v > 48) ? 48 : v;
24349             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24350             
24351         }
24352         
24353         
24354         v = Math.max(1, v+adjust);
24355         
24356         this.execCmd('FontSize', v  );
24357     },
24358
24359     onEditorEvent : function(e){
24360         this.fireEvent('editorevent', this, e);
24361       //  this.updateToolbar();
24362         this.syncValue();
24363     },
24364
24365     insertTag : function(tg)
24366     {
24367         // could be a bit smarter... -> wrap the current selected tRoo..
24368         
24369         this.execCmd("formatblock",   tg);
24370         
24371     },
24372     
24373     insertText : function(txt)
24374     {
24375         
24376         
24377         range = this.createRange();
24378         range.deleteContents();
24379                //alert(Sender.getAttribute('label'));
24380                
24381         range.insertNode(this.doc.createTextNode(txt));
24382     } ,
24383     
24384     // private
24385     relayBtnCmd : function(btn){
24386         this.relayCmd(btn.cmd);
24387     },
24388
24389     /**
24390      * Executes a Midas editor command on the editor document and performs necessary focus and
24391      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24392      * @param {String} cmd The Midas command
24393      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24394      */
24395     relayCmd : function(cmd, value){
24396         this.win.focus();
24397         this.execCmd(cmd, value);
24398         this.fireEvent('editorevent', this);
24399         //this.updateToolbar();
24400         this.deferFocus();
24401     },
24402
24403     /**
24404      * Executes a Midas editor command directly on the editor document.
24405      * For visual commands, you should use {@link #relayCmd} instead.
24406      * <b>This should only be called after the editor is initialized.</b>
24407      * @param {String} cmd The Midas command
24408      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24409      */
24410     execCmd : function(cmd, value){
24411         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24412         this.syncValue();
24413     },
24414
24415    
24416     /**
24417      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24418      * to insert tRoo.
24419      * @param {String} text
24420      */
24421     insertAtCursor : function(text){
24422         if(!this.activated){
24423             return;
24424         }
24425         if(Roo.isIE){
24426             this.win.focus();
24427             var r = this.doc.selection.createRange();
24428             if(r){
24429                 r.collapse(true);
24430                 r.pasteHTML(text);
24431                 this.syncValue();
24432                 this.deferFocus();
24433             }
24434         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24435             this.win.focus();
24436             this.execCmd('InsertHTML', text);
24437             this.deferFocus();
24438         }
24439     },
24440  // private
24441     mozKeyPress : function(e){
24442         if(e.ctrlKey){
24443             var c = e.getCharCode(), cmd;
24444           
24445             if(c > 0){
24446                 c = String.fromCharCode(c).toLowerCase();
24447                 switch(c){
24448                     case 'b':
24449                         cmd = 'bold';
24450                     break;
24451                     case 'i':
24452                         cmd = 'italic';
24453                     break;
24454                     case 'u':
24455                         cmd = 'underline';
24456                     case 'v':
24457                         this.cleanUpPaste.defer(100, this);
24458                         return;
24459                     break;
24460                 }
24461                 if(cmd){
24462                     this.win.focus();
24463                     this.execCmd(cmd);
24464                     this.deferFocus();
24465                     e.preventDefault();
24466                 }
24467                 
24468             }
24469         }
24470     },
24471
24472     // private
24473     fixKeys : function(){ // load time branching for fastest keydown performance
24474         if(Roo.isIE){
24475             return function(e){
24476                 var k = e.getKey(), r;
24477                 if(k == e.TAB){
24478                     e.stopEvent();
24479                     r = this.doc.selection.createRange();
24480                     if(r){
24481                         r.collapse(true);
24482                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24483                         this.deferFocus();
24484                     }
24485                     return;
24486                 }
24487                 
24488                 if(k == e.ENTER){
24489                     r = this.doc.selection.createRange();
24490                     if(r){
24491                         var target = r.parentElement();
24492                         if(!target || target.tagName.toLowerCase() != 'li'){
24493                             e.stopEvent();
24494                             r.pasteHTML('<br />');
24495                             r.collapse(false);
24496                             r.select();
24497                         }
24498                     }
24499                 }
24500                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24501                     this.cleanUpPaste.defer(100, this);
24502                     return;
24503                 }
24504                 
24505                 
24506             };
24507         }else if(Roo.isOpera){
24508             return function(e){
24509                 var k = e.getKey();
24510                 if(k == e.TAB){
24511                     e.stopEvent();
24512                     this.win.focus();
24513                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24514                     this.deferFocus();
24515                 }
24516                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24517                     this.cleanUpPaste.defer(100, this);
24518                     return;
24519                 }
24520                 
24521             };
24522         }else if(Roo.isSafari){
24523             return function(e){
24524                 var k = e.getKey();
24525                 
24526                 if(k == e.TAB){
24527                     e.stopEvent();
24528                     this.execCmd('InsertText','\t');
24529                     this.deferFocus();
24530                     return;
24531                 }
24532                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24533                     this.cleanUpPaste.defer(100, this);
24534                     return;
24535                 }
24536                 
24537              };
24538         }
24539     }(),
24540     
24541     getAllAncestors: function()
24542     {
24543         var p = this.getSelectedNode();
24544         var a = [];
24545         if (!p) {
24546             a.push(p); // push blank onto stack..
24547             p = this.getParentElement();
24548         }
24549         
24550         
24551         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24552             a.push(p);
24553             p = p.parentNode;
24554         }
24555         a.push(this.doc.body);
24556         return a;
24557     },
24558     lastSel : false,
24559     lastSelNode : false,
24560     
24561     
24562     getSelection : function() 
24563     {
24564         this.assignDocWin();
24565         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24566     },
24567     
24568     getSelectedNode: function() 
24569     {
24570         // this may only work on Gecko!!!
24571         
24572         // should we cache this!!!!
24573         
24574         
24575         
24576          
24577         var range = this.createRange(this.getSelection());
24578         
24579         if (Roo.isIE) {
24580             var parent = range.parentElement();
24581             while (true) {
24582                 var testRange = range.duplicate();
24583                 testRange.moveToElementText(parent);
24584                 if (testRange.inRange(range)) {
24585                     break;
24586                 }
24587                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24588                     break;
24589                 }
24590                 parent = parent.parentElement;
24591             }
24592             return parent;
24593         }
24594         
24595         
24596         var ar = range.endContainer.childNodes;
24597         if (!ar.length) {
24598             ar = range.commonAncestorContainer.childNodes;
24599             //alert(ar.length);
24600         }
24601         var nodes = [];
24602         var other_nodes = [];
24603         var has_other_nodes = false;
24604         for (var i=0;i<ar.length;i++) {
24605             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24606                 continue;
24607             }
24608             // fullly contained node.
24609             
24610             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24611                 nodes.push(ar[i]);
24612                 continue;
24613             }
24614             
24615             // probably selected..
24616             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24617                 other_nodes.push(ar[i]);
24618                 continue;
24619             }
24620             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24621                 continue;
24622             }
24623             
24624             
24625             has_other_nodes = true;
24626         }
24627         if (!nodes.length && other_nodes.length) {
24628             nodes= other_nodes;
24629         }
24630         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24631             return false;
24632         }
24633         
24634         return nodes[0];
24635     },
24636     createRange: function(sel)
24637     {
24638         // this has strange effects when using with 
24639         // top toolbar - not sure if it's a great idea.
24640         //this.editor.contentWindow.focus();
24641         if (typeof sel != "undefined") {
24642             try {
24643                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24644             } catch(e) {
24645                 return this.doc.createRange();
24646             }
24647         } else {
24648             return this.doc.createRange();
24649         }
24650     },
24651     getParentElement: function()
24652     {
24653         
24654         this.assignDocWin();
24655         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24656         
24657         var range = this.createRange(sel);
24658          
24659         try {
24660             var p = range.commonAncestorContainer;
24661             while (p.nodeType == 3) { // text node
24662                 p = p.parentNode;
24663             }
24664             return p;
24665         } catch (e) {
24666             return null;
24667         }
24668     
24669     },
24670     
24671     
24672     
24673     // BC Hacks - cause I cant work out what i was trying to do..
24674     rangeIntersectsNode : function(range, node)
24675     {
24676         var nodeRange = node.ownerDocument.createRange();
24677         try {
24678             nodeRange.selectNode(node);
24679         }
24680         catch (e) {
24681             nodeRange.selectNodeContents(node);
24682         }
24683
24684         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24685                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24686     },
24687     rangeCompareNode : function(range, node) {
24688         var nodeRange = node.ownerDocument.createRange();
24689         try {
24690             nodeRange.selectNode(node);
24691         } catch (e) {
24692             nodeRange.selectNodeContents(node);
24693         }
24694         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24695         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24696
24697         if (nodeIsBefore && !nodeIsAfter)
24698             return 0;
24699         if (!nodeIsBefore && nodeIsAfter)
24700             return 1;
24701         if (nodeIsBefore && nodeIsAfter)
24702             return 2;
24703
24704         return 3;
24705     },
24706
24707     // private? - in a new class?
24708     cleanUpPaste :  function()
24709     {
24710         // cleans up the whole document..
24711       //  console.log('cleanuppaste');
24712         this.cleanUpChildren(this.doc.body)
24713         
24714         
24715     },
24716     cleanUpChildren : function (n)
24717     {
24718         if (!n.childNodes.length) {
24719             return;
24720         }
24721         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24722            this.cleanUpChild(n.childNodes[i]);
24723         }
24724     },
24725     
24726     
24727         
24728     
24729     cleanUpChild : function (node)
24730     {
24731         //console.log(node);
24732         if (node.nodeName == "#text") {
24733             // clean up silly Windows -- stuff?
24734             return; 
24735         }
24736         if (node.nodeName == "#comment") {
24737             node.parentNode.removeChild(node);
24738             // clean up silly Windows -- stuff?
24739             return; 
24740         }
24741         
24742         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24743             // remove node.
24744             node.parentNode.removeChild(node);
24745             return;
24746             
24747         }
24748         if (!node.attributes || !node.attributes.length) {
24749             this.cleanUpChildren(node);
24750             return;
24751         }
24752         
24753         function cleanAttr(n,v)
24754         {
24755             
24756             if (v.match(/^\./) || v.match(/^\//)) {
24757                 return;
24758             }
24759             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24760                 return;
24761             }
24762             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24763             node.removeAttribute(n);
24764             
24765         }
24766         
24767         function cleanStyle(n,v)
24768         {
24769             if (v.match(/expression/)) { //XSS?? should we even bother..
24770                 node.removeAttribute(n);
24771                 return;
24772             }
24773             
24774             
24775             var parts = v.split(/;/);
24776             Roo.each(parts, function(p) {
24777                 p = p.replace(/\s+/g,'');
24778                 if (!p.length) {
24779                     return;
24780                 }
24781                 var l = p.split(':').shift().replace(/\s+/g,'');
24782                 
24783                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24784                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24785                     node.removeAttribute(n);
24786                     return false;
24787                 }
24788             });
24789             
24790             
24791         }
24792         
24793         
24794         for (var i = node.attributes.length-1; i > -1 ; i--) {
24795             var a = node.attributes[i];
24796             //console.log(a);
24797             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24798                 node.removeAttribute(a.name);
24799                 return;
24800             }
24801             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24802                 cleanAttr(a.name,a.value); // fixme..
24803                 return;
24804             }
24805             if (a.name == 'style') {
24806                 cleanStyle(a.name,a.value);
24807             }
24808             /// clean up MS crap..
24809             if (a.name == 'class') {
24810                 if (a.value.match(/^Mso/)) {
24811                     node.className = '';
24812                 }
24813             }
24814             
24815             // style cleanup!?
24816             // class cleanup?
24817             
24818         }
24819         
24820         
24821         this.cleanUpChildren(node);
24822         
24823         
24824     }
24825     
24826     
24827     // hide stuff that is not compatible
24828     /**
24829      * @event blur
24830      * @hide
24831      */
24832     /**
24833      * @event change
24834      * @hide
24835      */
24836     /**
24837      * @event focus
24838      * @hide
24839      */
24840     /**
24841      * @event specialkey
24842      * @hide
24843      */
24844     /**
24845      * @cfg {String} fieldClass @hide
24846      */
24847     /**
24848      * @cfg {String} focusClass @hide
24849      */
24850     /**
24851      * @cfg {String} autoCreate @hide
24852      */
24853     /**
24854      * @cfg {String} inputType @hide
24855      */
24856     /**
24857      * @cfg {String} invalidClass @hide
24858      */
24859     /**
24860      * @cfg {String} invalidText @hide
24861      */
24862     /**
24863      * @cfg {String} msgFx @hide
24864      */
24865     /**
24866      * @cfg {String} validateOnBlur @hide
24867      */
24868 });
24869
24870 Roo.form.HtmlEditor.white = [
24871         'area', 'br', 'img', 'input', 'hr', 'wbr',
24872         
24873        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24874        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24875        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24876        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24877        'table',   'ul',         'xmp', 
24878        
24879        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24880       'thead',   'tr', 
24881      
24882       'dir', 'menu', 'ol', 'ul', 'dl',
24883        
24884       'embed',  'object'
24885 ];
24886
24887
24888 Roo.form.HtmlEditor.black = [
24889     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24890         'applet', // 
24891         'base',   'basefont', 'bgsound', 'blink',  'body', 
24892         'frame',  'frameset', 'head',    'html',   'ilayer', 
24893         'iframe', 'layer',  'link',     'meta',    'object',   
24894         'script', 'style' ,'title',  'xml' // clean later..
24895 ];
24896 Roo.form.HtmlEditor.clean = [
24897     'script', 'style', 'title', 'xml'
24898 ];
24899
24900 // attributes..
24901
24902 Roo.form.HtmlEditor.ablack = [
24903     'on'
24904 ];
24905     
24906 Roo.form.HtmlEditor.aclean = [ 
24907     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24908 ];
24909
24910 // protocols..
24911 Roo.form.HtmlEditor.pwhite= [
24912         'http',  'https',  'mailto'
24913 ];
24914
24915 Roo.form.HtmlEditor.cwhite= [
24916         'text-align',
24917         'font-size'
24918 ];
24919
24920 // <script type="text/javascript">
24921 /*
24922  * Based on
24923  * Ext JS Library 1.1.1
24924  * Copyright(c) 2006-2007, Ext JS, LLC.
24925  *  
24926  
24927  */
24928
24929 /**
24930  * @class Roo.form.HtmlEditorToolbar1
24931  * Basic Toolbar
24932  * 
24933  * Usage:
24934  *
24935  new Roo.form.HtmlEditor({
24936     ....
24937     toolbars : [
24938         new Roo.form.HtmlEditorToolbar1({
24939             disable : { fonts: 1 , format: 1, ..., ... , ...],
24940             btns : [ .... ]
24941         })
24942     }
24943      
24944  * 
24945  * @cfg {Object} disable List of elements to disable..
24946  * @cfg {Array} btns List of additional buttons.
24947  * 
24948  * 
24949  * NEEDS Extra CSS? 
24950  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24951  */
24952  
24953 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24954 {
24955     
24956     Roo.apply(this, config);
24957     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24958     // dont call parent... till later.
24959 }
24960
24961 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24962     
24963     tb: false,
24964     
24965     rendered: false,
24966     
24967     editor : false,
24968     /**
24969      * @cfg {Object} disable  List of toolbar elements to disable
24970          
24971      */
24972     disable : false,
24973       /**
24974      * @cfg {Array} fontFamilies An array of available font families
24975      */
24976     fontFamilies : [
24977         'Arial',
24978         'Courier New',
24979         'Tahoma',
24980         'Times New Roman',
24981         'Verdana'
24982     ],
24983     
24984     specialChars : [
24985            "&#169;",
24986           "&#174;",     
24987           "&#8482;",    
24988           "&#163;" ,    
24989          // "&#8212;",    
24990           "&#8230;",    
24991           "&#247;" ,    
24992         //  "&#225;" ,     ?? a acute?
24993            "&#8364;"    , //Euro
24994        //   "&#8220;"    ,
24995         //  "&#8221;"    ,
24996         //  "&#8226;"    ,
24997           "&#176;"  //   , // degrees
24998
24999          // "&#233;"     , // e ecute
25000          // "&#250;"     , // u ecute?
25001     ],
25002     inputElements : [ 
25003             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25004             "input:submit", "input:button", "select", "textarea", "label" ],
25005     formats : [
25006         ["p"] ,  
25007         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25008         ["pre"],[ "code"], 
25009         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25010     ],
25011      /**
25012      * @cfg {String} defaultFont default font to use.
25013      */
25014     defaultFont: 'tahoma',
25015    
25016     fontSelect : false,
25017     
25018     
25019     formatCombo : false,
25020     
25021     init : function(editor)
25022     {
25023         this.editor = editor;
25024         
25025         
25026         var fid = editor.frameId;
25027         var etb = this;
25028         function btn(id, toggle, handler){
25029             var xid = fid + '-'+ id ;
25030             return {
25031                 id : xid,
25032                 cmd : id,
25033                 cls : 'x-btn-icon x-edit-'+id,
25034                 enableToggle:toggle !== false,
25035                 scope: editor, // was editor...
25036                 handler:handler||editor.relayBtnCmd,
25037                 clickEvent:'mousedown',
25038                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25039                 tabIndex:-1
25040             };
25041         }
25042         
25043         
25044         
25045         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25046         this.tb = tb;
25047          // stop form submits
25048         tb.el.on('click', function(e){
25049             e.preventDefault(); // what does this do?
25050         });
25051
25052         if(!this.disable.font && !Roo.isSafari){
25053             /* why no safari for fonts
25054             editor.fontSelect = tb.el.createChild({
25055                 tag:'select',
25056                 tabIndex: -1,
25057                 cls:'x-font-select',
25058                 html: editor.createFontOptions()
25059             });
25060             editor.fontSelect.on('change', function(){
25061                 var font = editor.fontSelect.dom.value;
25062                 editor.relayCmd('fontname', font);
25063                 editor.deferFocus();
25064             }, editor);
25065             tb.add(
25066                 editor.fontSelect.dom,
25067                 '-'
25068             );
25069             */
25070         };
25071         if(!this.disable.formats){
25072             this.formatCombo = new Roo.form.ComboBox({
25073                 store: new Roo.data.SimpleStore({
25074                     id : 'tag',
25075                     fields: ['tag'],
25076                     data : this.formats // from states.js
25077                 }),
25078                 blockFocus : true,
25079                 //autoCreate : {tag: "div",  size: "20"},
25080                 displayField:'tag',
25081                 typeAhead: false,
25082                 mode: 'local',
25083                 editable : false,
25084                 triggerAction: 'all',
25085                 emptyText:'Add tag',
25086                 selectOnFocus:true,
25087                 width:135,
25088                 listeners : {
25089                     'select': function(c, r, i) {
25090                         editor.insertTag(r.get('tag'));
25091                         editor.focus();
25092                     }
25093                 }
25094
25095             });
25096             tb.addField(this.formatCombo);
25097             
25098         }
25099         
25100         if(!this.disable.format){
25101             tb.add(
25102                 btn('bold'),
25103                 btn('italic'),
25104                 btn('underline')
25105             );
25106         };
25107         if(!this.disable.fontSize){
25108             tb.add(
25109                 '-',
25110                 
25111                 
25112                 btn('increasefontsize', false, editor.adjustFont),
25113                 btn('decreasefontsize', false, editor.adjustFont)
25114             );
25115         };
25116         
25117         
25118         if(this.disable.colors){
25119             tb.add(
25120                 '-', {
25121                     id:editor.frameId +'-forecolor',
25122                     cls:'x-btn-icon x-edit-forecolor',
25123                     clickEvent:'mousedown',
25124                     tooltip: this.buttonTips['forecolor'] || undefined,
25125                     tabIndex:-1,
25126                     menu : new Roo.menu.ColorMenu({
25127                         allowReselect: true,
25128                         focus: Roo.emptyFn,
25129                         value:'000000',
25130                         plain:true,
25131                         selectHandler: function(cp, color){
25132                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25133                             editor.deferFocus();
25134                         },
25135                         scope: editor,
25136                         clickEvent:'mousedown'
25137                     })
25138                 }, {
25139                     id:editor.frameId +'backcolor',
25140                     cls:'x-btn-icon x-edit-backcolor',
25141                     clickEvent:'mousedown',
25142                     tooltip: this.buttonTips['backcolor'] || undefined,
25143                     tabIndex:-1,
25144                     menu : new Roo.menu.ColorMenu({
25145                         focus: Roo.emptyFn,
25146                         value:'FFFFFF',
25147                         plain:true,
25148                         allowReselect: true,
25149                         selectHandler: function(cp, color){
25150                             if(Roo.isGecko){
25151                                 editor.execCmd('useCSS', false);
25152                                 editor.execCmd('hilitecolor', color);
25153                                 editor.execCmd('useCSS', true);
25154                                 editor.deferFocus();
25155                             }else{
25156                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25157                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25158                                 editor.deferFocus();
25159                             }
25160                         },
25161                         scope:editor,
25162                         clickEvent:'mousedown'
25163                     })
25164                 }
25165             );
25166         };
25167         // now add all the items...
25168         
25169
25170         if(!this.disable.alignments){
25171             tb.add(
25172                 '-',
25173                 btn('justifyleft'),
25174                 btn('justifycenter'),
25175                 btn('justifyright')
25176             );
25177         };
25178
25179         //if(!Roo.isSafari){
25180             if(!this.disable.links){
25181                 tb.add(
25182                     '-',
25183                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25184                 );
25185             };
25186
25187             if(!this.disable.lists){
25188                 tb.add(
25189                     '-',
25190                     btn('insertorderedlist'),
25191                     btn('insertunorderedlist')
25192                 );
25193             }
25194             if(!this.disable.sourceEdit){
25195                 tb.add(
25196                     '-',
25197                     btn('sourceedit', true, function(btn){
25198                         this.toggleSourceEdit(btn.pressed);
25199                     })
25200                 );
25201             }
25202         //}
25203         
25204         var smenu = { };
25205         // special menu.. - needs to be tidied up..
25206         if (!this.disable.special) {
25207             smenu = {
25208                 text: "&#169;",
25209                 cls: 'x-edit-none',
25210                 menu : {
25211                     items : []
25212                    }
25213             };
25214             for (var i =0; i < this.specialChars.length; i++) {
25215                 smenu.menu.items.push({
25216                     
25217                     html: this.specialChars[i],
25218                     handler: function(a,b) {
25219                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25220                         
25221                     },
25222                     tabIndex:-1
25223                 });
25224             }
25225             
25226             
25227             tb.add(smenu);
25228             
25229             
25230         }
25231         if (this.btns) {
25232             for(var i =0; i< this.btns.length;i++) {
25233                 var b = this.btns[i];
25234                 b.cls =  'x-edit-none';
25235                 b.scope = editor;
25236                 tb.add(b);
25237             }
25238         
25239         }
25240         
25241         
25242         
25243         // disable everything...
25244         
25245         this.tb.items.each(function(item){
25246            if(item.id != editor.frameId+ '-sourceedit'){
25247                 item.disable();
25248             }
25249         });
25250         this.rendered = true;
25251         
25252         // the all the btns;
25253         editor.on('editorevent', this.updateToolbar, this);
25254         // other toolbars need to implement this..
25255         //editor.on('editmodechange', this.updateToolbar, this);
25256     },
25257     
25258     
25259     
25260     /**
25261      * Protected method that will not generally be called directly. It triggers
25262      * a toolbar update by reading the markup state of the current selection in the editor.
25263      */
25264     updateToolbar: function(){
25265
25266         if(!this.editor.activated){
25267             this.editor.onFirstFocus();
25268             return;
25269         }
25270
25271         var btns = this.tb.items.map, 
25272             doc = this.editor.doc,
25273             frameId = this.editor.frameId;
25274
25275         if(!this.disable.font && !Roo.isSafari){
25276             /*
25277             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25278             if(name != this.fontSelect.dom.value){
25279                 this.fontSelect.dom.value = name;
25280             }
25281             */
25282         }
25283         if(!this.disable.format){
25284             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25285             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25286             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25287         }
25288         if(!this.disable.alignments){
25289             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25290             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25291             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25292         }
25293         if(!Roo.isSafari && !this.disable.lists){
25294             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25295             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25296         }
25297         
25298         var ans = this.editor.getAllAncestors();
25299         if (this.formatCombo) {
25300             
25301             
25302             var store = this.formatCombo.store;
25303             this.formatCombo.setValue("");
25304             for (var i =0; i < ans.length;i++) {
25305                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25306                     // select it..
25307                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25308                     break;
25309                 }
25310             }
25311         }
25312         
25313         
25314         
25315         // hides menus... - so this cant be on a menu...
25316         Roo.menu.MenuMgr.hideAll();
25317
25318         //this.editorsyncValue();
25319     },
25320    
25321     
25322     createFontOptions : function(){
25323         var buf = [], fs = this.fontFamilies, ff, lc;
25324         for(var i = 0, len = fs.length; i< len; i++){
25325             ff = fs[i];
25326             lc = ff.toLowerCase();
25327             buf.push(
25328                 '<option value="',lc,'" style="font-family:',ff,';"',
25329                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25330                     ff,
25331                 '</option>'
25332             );
25333         }
25334         return buf.join('');
25335     },
25336     
25337     toggleSourceEdit : function(sourceEditMode){
25338         if(sourceEditMode === undefined){
25339             sourceEditMode = !this.sourceEditMode;
25340         }
25341         this.sourceEditMode = sourceEditMode === true;
25342         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25343         // just toggle the button?
25344         if(btn.pressed !== this.editor.sourceEditMode){
25345             btn.toggle(this.editor.sourceEditMode);
25346             return;
25347         }
25348         
25349         if(this.sourceEditMode){
25350             this.tb.items.each(function(item){
25351                 if(item.cmd != 'sourceedit'){
25352                     item.disable();
25353                 }
25354             });
25355           
25356         }else{
25357             if(this.initialized){
25358                 this.tb.items.each(function(item){
25359                     item.enable();
25360                 });
25361             }
25362             
25363         }
25364         // tell the editor that it's been pressed..
25365         this.editor.toggleSourceEdit(sourceEditMode);
25366        
25367     },
25368      /**
25369      * Object collection of toolbar tooltips for the buttons in the editor. The key
25370      * is the command id associated with that button and the value is a valid QuickTips object.
25371      * For example:
25372 <pre><code>
25373 {
25374     bold : {
25375         title: 'Bold (Ctrl+B)',
25376         text: 'Make the selected text bold.',
25377         cls: 'x-html-editor-tip'
25378     },
25379     italic : {
25380         title: 'Italic (Ctrl+I)',
25381         text: 'Make the selected text italic.',
25382         cls: 'x-html-editor-tip'
25383     },
25384     ...
25385 </code></pre>
25386     * @type Object
25387      */
25388     buttonTips : {
25389         bold : {
25390             title: 'Bold (Ctrl+B)',
25391             text: 'Make the selected text bold.',
25392             cls: 'x-html-editor-tip'
25393         },
25394         italic : {
25395             title: 'Italic (Ctrl+I)',
25396             text: 'Make the selected text italic.',
25397             cls: 'x-html-editor-tip'
25398         },
25399         underline : {
25400             title: 'Underline (Ctrl+U)',
25401             text: 'Underline the selected text.',
25402             cls: 'x-html-editor-tip'
25403         },
25404         increasefontsize : {
25405             title: 'Grow Text',
25406             text: 'Increase the font size.',
25407             cls: 'x-html-editor-tip'
25408         },
25409         decreasefontsize : {
25410             title: 'Shrink Text',
25411             text: 'Decrease the font size.',
25412             cls: 'x-html-editor-tip'
25413         },
25414         backcolor : {
25415             title: 'Text Highlight Color',
25416             text: 'Change the background color of the selected text.',
25417             cls: 'x-html-editor-tip'
25418         },
25419         forecolor : {
25420             title: 'Font Color',
25421             text: 'Change the color of the selected text.',
25422             cls: 'x-html-editor-tip'
25423         },
25424         justifyleft : {
25425             title: 'Align Text Left',
25426             text: 'Align text to the left.',
25427             cls: 'x-html-editor-tip'
25428         },
25429         justifycenter : {
25430             title: 'Center Text',
25431             text: 'Center text in the editor.',
25432             cls: 'x-html-editor-tip'
25433         },
25434         justifyright : {
25435             title: 'Align Text Right',
25436             text: 'Align text to the right.',
25437             cls: 'x-html-editor-tip'
25438         },
25439         insertunorderedlist : {
25440             title: 'Bullet List',
25441             text: 'Start a bulleted list.',
25442             cls: 'x-html-editor-tip'
25443         },
25444         insertorderedlist : {
25445             title: 'Numbered List',
25446             text: 'Start a numbered list.',
25447             cls: 'x-html-editor-tip'
25448         },
25449         createlink : {
25450             title: 'Hyperlink',
25451             text: 'Make the selected text a hyperlink.',
25452             cls: 'x-html-editor-tip'
25453         },
25454         sourceedit : {
25455             title: 'Source Edit',
25456             text: 'Switch to source editing mode.',
25457             cls: 'x-html-editor-tip'
25458         }
25459     },
25460     // private
25461     onDestroy : function(){
25462         if(this.rendered){
25463             
25464             this.tb.items.each(function(item){
25465                 if(item.menu){
25466                     item.menu.removeAll();
25467                     if(item.menu.el){
25468                         item.menu.el.destroy();
25469                     }
25470                 }
25471                 item.destroy();
25472             });
25473              
25474         }
25475     },
25476     onFirstFocus: function() {
25477         this.tb.items.each(function(item){
25478            item.enable();
25479         });
25480     }
25481 });
25482
25483
25484
25485
25486 // <script type="text/javascript">
25487 /*
25488  * Based on
25489  * Ext JS Library 1.1.1
25490  * Copyright(c) 2006-2007, Ext JS, LLC.
25491  *  
25492  
25493  */
25494
25495  
25496 /**
25497  * @class Roo.form.HtmlEditor.ToolbarContext
25498  * Context Toolbar
25499  * 
25500  * Usage:
25501  *
25502  new Roo.form.HtmlEditor({
25503     ....
25504     toolbars : [
25505         new Roo.form.HtmlEditor.ToolbarStandard(),
25506         new Roo.form.HtmlEditor.ToolbarContext()
25507         })
25508     }
25509      
25510  * 
25511  * @config : {Object} disable List of elements to disable.. (not done yet.)
25512  * 
25513  * 
25514  */
25515
25516 Roo.form.HtmlEditor.ToolbarContext = function(config)
25517 {
25518     
25519     Roo.apply(this, config);
25520     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25521     // dont call parent... till later.
25522 }
25523 Roo.form.HtmlEditor.ToolbarContext.types = {
25524     'IMG' : {
25525         width : {
25526             title: "Width",
25527             width: 40
25528         },
25529         height:  {
25530             title: "Height",
25531             width: 40
25532         },
25533         align: {
25534             title: "Align",
25535             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25536             width : 80
25537             
25538         },
25539         border: {
25540             title: "Border",
25541             width: 40
25542         },
25543         alt: {
25544             title: "Alt",
25545             width: 120
25546         },
25547         src : {
25548             title: "Src",
25549             width: 220
25550         }
25551         
25552     },
25553     'A' : {
25554         name : {
25555             title: "Name",
25556             width: 50
25557         },
25558         href:  {
25559             title: "Href",
25560             width: 220
25561         } // border?
25562         
25563     },
25564     'TABLE' : {
25565         rows : {
25566             title: "Rows",
25567             width: 20
25568         },
25569         cols : {
25570             title: "Cols",
25571             width: 20
25572         },
25573         width : {
25574             title: "Width",
25575             width: 40
25576         },
25577         height : {
25578             title: "Height",
25579             width: 40
25580         },
25581         border : {
25582             title: "Border",
25583             width: 20
25584         }
25585     },
25586     'TD' : {
25587         width : {
25588             title: "Width",
25589             width: 40
25590         },
25591         height : {
25592             title: "Height",
25593             width: 40
25594         },   
25595         align: {
25596             title: "Align",
25597             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25598             width: 40
25599         },
25600         valign: {
25601             title: "Valign",
25602             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25603             width: 40
25604         },
25605         colspan: {
25606             title: "Colspan",
25607             width: 20
25608             
25609         }
25610     },
25611     'INPUT' : {
25612         name : {
25613             title: "name",
25614             width: 120
25615         },
25616         value : {
25617             title: "Value",
25618             width: 120
25619         },
25620         width : {
25621             title: "Width",
25622             width: 40
25623         }
25624     },
25625     'LABEL' : {
25626         'for' : {
25627             title: "For",
25628             width: 120
25629         }
25630     },
25631     'TEXTAREA' : {
25632           name : {
25633             title: "name",
25634             width: 120
25635         },
25636         rows : {
25637             title: "Rows",
25638             width: 20
25639         },
25640         cols : {
25641             title: "Cols",
25642             width: 20
25643         }
25644     },
25645     'SELECT' : {
25646         name : {
25647             title: "name",
25648             width: 120
25649         },
25650         selectoptions : {
25651             title: "Options",
25652             width: 200
25653         }
25654     },
25655     'BODY' : {
25656         title : {
25657             title: "title",
25658             width: 120,
25659             disabled : true
25660         }
25661     }
25662 };
25663
25664
25665
25666 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25667     
25668     tb: false,
25669     
25670     rendered: false,
25671     
25672     editor : false,
25673     /**
25674      * @cfg {Object} disable  List of toolbar elements to disable
25675          
25676      */
25677     disable : false,
25678     
25679     
25680     
25681     toolbars : false,
25682     
25683     init : function(editor)
25684     {
25685         this.editor = editor;
25686         
25687         
25688         var fid = editor.frameId;
25689         var etb = this;
25690         function btn(id, toggle, handler){
25691             var xid = fid + '-'+ id ;
25692             return {
25693                 id : xid,
25694                 cmd : id,
25695                 cls : 'x-btn-icon x-edit-'+id,
25696                 enableToggle:toggle !== false,
25697                 scope: editor, // was editor...
25698                 handler:handler||editor.relayBtnCmd,
25699                 clickEvent:'mousedown',
25700                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25701                 tabIndex:-1
25702             };
25703         }
25704         // create a new element.
25705         var wdiv = editor.wrap.createChild({
25706                 tag: 'div'
25707             }, editor.wrap.dom.firstChild.nextSibling, true);
25708         
25709         // can we do this more than once??
25710         
25711          // stop form submits
25712       
25713  
25714         // disable everything...
25715         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25716         this.toolbars = {};
25717            
25718         for (var i in  ty) {
25719           
25720             this.toolbars[i] = this.buildToolbar(ty[i],i);
25721         }
25722         this.tb = this.toolbars.BODY;
25723         this.tb.el.show();
25724         
25725          
25726         this.rendered = true;
25727         
25728         // the all the btns;
25729         editor.on('editorevent', this.updateToolbar, this);
25730         // other toolbars need to implement this..
25731         //editor.on('editmodechange', this.updateToolbar, this);
25732     },
25733     
25734     
25735     
25736     /**
25737      * Protected method that will not generally be called directly. It triggers
25738      * a toolbar update by reading the markup state of the current selection in the editor.
25739      */
25740     updateToolbar: function(){
25741
25742         if(!this.editor.activated){
25743             this.editor.onFirstFocus();
25744             return;
25745         }
25746
25747         
25748         var ans = this.editor.getAllAncestors();
25749         
25750         // pick
25751         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25752         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25753         sel = sel ? sel : this.editor.doc.body;
25754         sel = sel.tagName.length ? sel : this.editor.doc.body;
25755         var tn = sel.tagName.toUpperCase();
25756         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25757         tn = sel.tagName.toUpperCase();
25758         if (this.tb.name  == tn) {
25759             return; // no change
25760         }
25761         this.tb.el.hide();
25762         ///console.log("show: " + tn);
25763         this.tb =  this.toolbars[tn];
25764         this.tb.el.show();
25765         this.tb.fields.each(function(e) {
25766             e.setValue(sel.getAttribute(e.name));
25767         });
25768         this.tb.selectedNode = sel;
25769         
25770         
25771         Roo.menu.MenuMgr.hideAll();
25772
25773         //this.editorsyncValue();
25774     },
25775    
25776        
25777     // private
25778     onDestroy : function(){
25779         if(this.rendered){
25780             
25781             this.tb.items.each(function(item){
25782                 if(item.menu){
25783                     item.menu.removeAll();
25784                     if(item.menu.el){
25785                         item.menu.el.destroy();
25786                     }
25787                 }
25788                 item.destroy();
25789             });
25790              
25791         }
25792     },
25793     onFirstFocus: function() {
25794         // need to do this for all the toolbars..
25795         this.tb.items.each(function(item){
25796            item.enable();
25797         });
25798     },
25799     buildToolbar: function(tlist, nm)
25800     {
25801         var editor = this.editor;
25802          // create a new element.
25803         var wdiv = editor.wrap.createChild({
25804                 tag: 'div'
25805             }, editor.wrap.dom.firstChild.nextSibling, true);
25806         
25807        
25808         var tb = new Roo.Toolbar(wdiv);
25809         tb.add(nm+ ":&nbsp;");
25810         for (var i in tlist) {
25811             var item = tlist[i];
25812             tb.add(item.title + ":&nbsp;");
25813             if (item.opts) {
25814                 // fixme
25815                 
25816               
25817                 tb.addField( new Roo.form.ComboBox({
25818                     store: new Roo.data.SimpleStore({
25819                         id : 'val',
25820                         fields: ['val'],
25821                         data : item.opts // from states.js
25822                     }),
25823                     name : i,
25824                     displayField:'val',
25825                     typeAhead: false,
25826                     mode: 'local',
25827                     editable : false,
25828                     triggerAction: 'all',
25829                     emptyText:'Select',
25830                     selectOnFocus:true,
25831                     width: item.width ? item.width  : 130,
25832                     listeners : {
25833                         'select': function(c, r, i) {
25834                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25835                         }
25836                     }
25837
25838                 }));
25839                 continue;
25840                     
25841                 
25842                 
25843                 
25844                 
25845                 tb.addField( new Roo.form.TextField({
25846                     name: i,
25847                     width: 100,
25848                     //allowBlank:false,
25849                     value: ''
25850                 }));
25851                 continue;
25852             }
25853             tb.addField( new Roo.form.TextField({
25854                 name: i,
25855                 width: item.width,
25856                 //allowBlank:true,
25857                 value: '',
25858                 listeners: {
25859                     'change' : function(f, nv, ov) {
25860                         tb.selectedNode.setAttribute(f.name, nv);
25861                     }
25862                 }
25863             }));
25864              
25865         }
25866         tb.el.on('click', function(e){
25867             e.preventDefault(); // what does this do?
25868         });
25869         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25870         tb.el.hide();
25871         tb.name = nm;
25872         // dont need to disable them... as they will get hidden
25873         return tb;
25874          
25875         
25876     }
25877     
25878     
25879     
25880     
25881 });
25882
25883
25884
25885
25886
25887 /*
25888  * Based on:
25889  * Ext JS Library 1.1.1
25890  * Copyright(c) 2006-2007, Ext JS, LLC.
25891  *
25892  * Originally Released Under LGPL - original licence link has changed is not relivant.
25893  *
25894  * Fork - LGPL
25895  * <script type="text/javascript">
25896  */
25897  
25898 /**
25899  * @class Roo.form.BasicForm
25900  * @extends Roo.util.Observable
25901  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25902  * @constructor
25903  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25904  * @param {Object} config Configuration options
25905  */
25906 Roo.form.BasicForm = function(el, config){
25907     this.allItems = [];
25908     this.childForms = [];
25909     Roo.apply(this, config);
25910     /*
25911      * The Roo.form.Field items in this form.
25912      * @type MixedCollection
25913      */
25914      
25915      
25916     this.items = new Roo.util.MixedCollection(false, function(o){
25917         return o.id || (o.id = Roo.id());
25918     });
25919     this.addEvents({
25920         /**
25921          * @event beforeaction
25922          * Fires before any action is performed. Return false to cancel the action.
25923          * @param {Form} this
25924          * @param {Action} action The action to be performed
25925          */
25926         beforeaction: true,
25927         /**
25928          * @event actionfailed
25929          * Fires when an action fails.
25930          * @param {Form} this
25931          * @param {Action} action The action that failed
25932          */
25933         actionfailed : true,
25934         /**
25935          * @event actioncomplete
25936          * Fires when an action is completed.
25937          * @param {Form} this
25938          * @param {Action} action The action that completed
25939          */
25940         actioncomplete : true
25941     });
25942     if(el){
25943         this.initEl(el);
25944     }
25945     Roo.form.BasicForm.superclass.constructor.call(this);
25946 };
25947
25948 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25949     /**
25950      * @cfg {String} method
25951      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25952      */
25953     /**
25954      * @cfg {DataReader} reader
25955      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25956      * This is optional as there is built-in support for processing JSON.
25957      */
25958     /**
25959      * @cfg {DataReader} errorReader
25960      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25961      * This is completely optional as there is built-in support for processing JSON.
25962      */
25963     /**
25964      * @cfg {String} url
25965      * The URL to use for form actions if one isn't supplied in the action options.
25966      */
25967     /**
25968      * @cfg {Boolean} fileUpload
25969      * Set to true if this form is a file upload.
25970      */
25971     /**
25972      * @cfg {Object} baseParams
25973      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25974      */
25975     /**
25976      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25977      */
25978     timeout: 30,
25979
25980     // private
25981     activeAction : null,
25982
25983     /**
25984      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25985      * or setValues() data instead of when the form was first created.
25986      */
25987     trackResetOnLoad : false,
25988     
25989     
25990     /**
25991      * childForms - used for multi-tab forms
25992      * @type {Array}
25993      */
25994     childForms : false,
25995     
25996     /**
25997      * allItems - full list of fields.
25998      * @type {Array}
25999      */
26000     allItems : false,
26001     
26002     /**
26003      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26004      * element by passing it or its id or mask the form itself by passing in true.
26005      * @type Mixed
26006      */
26007     waitMsgTarget : undefined,
26008
26009     // private
26010     initEl : function(el){
26011         this.el = Roo.get(el);
26012         this.id = this.el.id || Roo.id();
26013         this.el.on('submit', this.onSubmit, this);
26014         this.el.addClass('x-form');
26015     },
26016
26017     // private
26018     onSubmit : function(e){
26019         e.stopEvent();
26020     },
26021
26022     /**
26023      * Returns true if client-side validation on the form is successful.
26024      * @return Boolean
26025      */
26026     isValid : function(){
26027         var valid = true;
26028         this.items.each(function(f){
26029            if(!f.validate()){
26030                valid = false;
26031            }
26032         });
26033         return valid;
26034     },
26035
26036     /**
26037      * Returns true if any fields in this form have changed since their original load.
26038      * @return Boolean
26039      */
26040     isDirty : function(){
26041         var dirty = false;
26042         this.items.each(function(f){
26043            if(f.isDirty()){
26044                dirty = true;
26045                return false;
26046            }
26047         });
26048         return dirty;
26049     },
26050
26051     /**
26052      * Performs a predefined action (submit or load) or custom actions you define on this form.
26053      * @param {String} actionName The name of the action type
26054      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26055      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26056      * accept other config options):
26057      * <pre>
26058 Property          Type             Description
26059 ----------------  ---------------  ----------------------------------------------------------------------------------
26060 url               String           The url for the action (defaults to the form's url)
26061 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26062 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26063 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26064                                    validate the form on the client (defaults to false)
26065      * </pre>
26066      * @return {BasicForm} this
26067      */
26068     doAction : function(action, options){
26069         if(typeof action == 'string'){
26070             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26071         }
26072         if(this.fireEvent('beforeaction', this, action) !== false){
26073             this.beforeAction(action);
26074             action.run.defer(100, action);
26075         }
26076         return this;
26077     },
26078
26079     /**
26080      * Shortcut to do a submit action.
26081      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26082      * @return {BasicForm} this
26083      */
26084     submit : function(options){
26085         this.doAction('submit', options);
26086         return this;
26087     },
26088
26089     /**
26090      * Shortcut to do a load action.
26091      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26092      * @return {BasicForm} this
26093      */
26094     load : function(options){
26095         this.doAction('load', options);
26096         return this;
26097     },
26098
26099     /**
26100      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26101      * @param {Record} record The record to edit
26102      * @return {BasicForm} this
26103      */
26104     updateRecord : function(record){
26105         record.beginEdit();
26106         var fs = record.fields;
26107         fs.each(function(f){
26108             var field = this.findField(f.name);
26109             if(field){
26110                 record.set(f.name, field.getValue());
26111             }
26112         }, this);
26113         record.endEdit();
26114         return this;
26115     },
26116
26117     /**
26118      * Loads an Roo.data.Record into this form.
26119      * @param {Record} record The record to load
26120      * @return {BasicForm} this
26121      */
26122     loadRecord : function(record){
26123         this.setValues(record.data);
26124         return this;
26125     },
26126
26127     // private
26128     beforeAction : function(action){
26129         var o = action.options;
26130         if(o.waitMsg){
26131             if(this.waitMsgTarget === true){
26132                 this.el.mask(o.waitMsg, 'x-mask-loading');
26133             }else if(this.waitMsgTarget){
26134                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26135                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26136             }else{
26137                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26138             }
26139         }
26140     },
26141
26142     // private
26143     afterAction : function(action, success){
26144         this.activeAction = null;
26145         var o = action.options;
26146         if(o.waitMsg){
26147             if(this.waitMsgTarget === true){
26148                 this.el.unmask();
26149             }else if(this.waitMsgTarget){
26150                 this.waitMsgTarget.unmask();
26151             }else{
26152                 Roo.MessageBox.updateProgress(1);
26153                 Roo.MessageBox.hide();
26154             }
26155         }
26156         if(success){
26157             if(o.reset){
26158                 this.reset();
26159             }
26160             Roo.callback(o.success, o.scope, [this, action]);
26161             this.fireEvent('actioncomplete', this, action);
26162         }else{
26163             Roo.callback(o.failure, o.scope, [this, action]);
26164             this.fireEvent('actionfailed', this, action);
26165         }
26166     },
26167
26168     /**
26169      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26170      * @param {String} id The value to search for
26171      * @return Field
26172      */
26173     findField : function(id){
26174         var field = this.items.get(id);
26175         if(!field){
26176             this.items.each(function(f){
26177                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26178                     field = f;
26179                     return false;
26180                 }
26181             });
26182         }
26183         return field || null;
26184     },
26185
26186     /**
26187      * Add a secondary form to this one, 
26188      * Used to provide tabbed forms. One form is primary, with hidden values 
26189      * which mirror the elements from the other forms.
26190      * 
26191      * @param {Roo.form.Form} form to add.
26192      * 
26193      */
26194     addForm : function(form)
26195     {
26196        
26197         if (this.childForms.indexOf(form) > -1) {
26198             // already added..
26199             return;
26200         }
26201         this.childForms.push(form);
26202         var n = '';
26203         Roo.each(form.allItems, function (fe) {
26204             
26205             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26206             if (this.findField(n)) { // already added..
26207                 return;
26208             }
26209             var add = new Roo.form.Hidden({
26210                 name : n
26211             });
26212             add.render(this.el);
26213             
26214             this.add( add );
26215         }, this);
26216         
26217     },
26218     /**
26219      * Mark fields in this form invalid in bulk.
26220      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26221      * @return {BasicForm} this
26222      */
26223     markInvalid : function(errors){
26224         if(errors instanceof Array){
26225             for(var i = 0, len = errors.length; i < len; i++){
26226                 var fieldError = errors[i];
26227                 var f = this.findField(fieldError.id);
26228                 if(f){
26229                     f.markInvalid(fieldError.msg);
26230                 }
26231             }
26232         }else{
26233             var field, id;
26234             for(id in errors){
26235                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26236                     field.markInvalid(errors[id]);
26237                 }
26238             }
26239         }
26240         Roo.each(this.childForms || [], function (f) {
26241             f.markInvalid(errors);
26242         });
26243         
26244         return this;
26245     },
26246
26247     /**
26248      * Set values for fields in this form in bulk.
26249      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26250      * @return {BasicForm} this
26251      */
26252     setValues : function(values){
26253         if(values instanceof Array){ // array of objects
26254             for(var i = 0, len = values.length; i < len; i++){
26255                 var v = values[i];
26256                 var f = this.findField(v.id);
26257                 if(f){
26258                     f.setValue(v.value);
26259                     if(this.trackResetOnLoad){
26260                         f.originalValue = f.getValue();
26261                     }
26262                 }
26263             }
26264         }else{ // object hash
26265             var field, id;
26266             for(id in values){
26267                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26268                     
26269                     if (field.setFromData && 
26270                         field.valueField && 
26271                         field.displayField &&
26272                         // combos' with local stores can 
26273                         // be queried via setValue()
26274                         // to set their value..
26275                         (field.store && !field.store.isLocal)
26276                         ) {
26277                         // it's a combo
26278                         var sd = { };
26279                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26280                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26281                         field.setFromData(sd);
26282                         
26283                     } else {
26284                         field.setValue(values[id]);
26285                     }
26286                     
26287                     
26288                     if(this.trackResetOnLoad){
26289                         field.originalValue = field.getValue();
26290                     }
26291                 }
26292             }
26293         }
26294          
26295         Roo.each(this.childForms || [], function (f) {
26296             f.setValues(values);
26297         });
26298                 
26299         return this;
26300     },
26301
26302     /**
26303      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26304      * they are returned as an array.
26305      * @param {Boolean} asString
26306      * @return {Object}
26307      */
26308     getValues : function(asString){
26309         if (this.childForms) {
26310             // copy values from the child forms
26311             Roo.each(this.childForms, function (f) {
26312                 this.setValues(f.getValues());
26313             }, this);
26314         }
26315         
26316         
26317         
26318         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26319         if(asString === true){
26320             return fs;
26321         }
26322         return Roo.urlDecode(fs);
26323     },
26324     
26325     /**
26326      * Returns the fields in this form as an object with key/value pairs. 
26327      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26328      * @return {Object}
26329      */
26330     getFieldValues : function()
26331     {
26332         if (this.childForms) {
26333             // copy values from the child forms
26334             Roo.each(this.childForms, function (f) {
26335                 this.setValues(f.getValues());
26336             }, this);
26337         }
26338         
26339         var ret = {};
26340         this.items.each(function(f){
26341             if (!f.getName()) {
26342                 return;
26343             }
26344             var v = f.getValue();
26345             if ((typeof(v) == 'object') && f.getRawValue) {
26346                 v = f.getRawValue() ; // dates..
26347             }
26348             ret[f.getName()] = v;
26349         });
26350         
26351         return ret;
26352     },
26353
26354     /**
26355      * Clears all invalid messages in this form.
26356      * @return {BasicForm} this
26357      */
26358     clearInvalid : function(){
26359         this.items.each(function(f){
26360            f.clearInvalid();
26361         });
26362         
26363         Roo.each(this.childForms || [], function (f) {
26364             f.clearInvalid();
26365         });
26366         
26367         
26368         return this;
26369     },
26370
26371     /**
26372      * Resets this form.
26373      * @return {BasicForm} this
26374      */
26375     reset : function(){
26376         this.items.each(function(f){
26377             f.reset();
26378         });
26379         
26380         Roo.each(this.childForms || [], function (f) {
26381             f.reset();
26382         });
26383        
26384         
26385         return this;
26386     },
26387
26388     /**
26389      * Add Roo.form components to this form.
26390      * @param {Field} field1
26391      * @param {Field} field2 (optional)
26392      * @param {Field} etc (optional)
26393      * @return {BasicForm} this
26394      */
26395     add : function(){
26396         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26397         return this;
26398     },
26399
26400
26401     /**
26402      * Removes a field from the items collection (does NOT remove its markup).
26403      * @param {Field} field
26404      * @return {BasicForm} this
26405      */
26406     remove : function(field){
26407         this.items.remove(field);
26408         return this;
26409     },
26410
26411     /**
26412      * Looks at the fields in this form, checks them for an id attribute,
26413      * and calls applyTo on the existing dom element with that id.
26414      * @return {BasicForm} this
26415      */
26416     render : function(){
26417         this.items.each(function(f){
26418             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26419                 f.applyTo(f.id);
26420             }
26421         });
26422         return this;
26423     },
26424
26425     /**
26426      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26427      * @param {Object} values
26428      * @return {BasicForm} this
26429      */
26430     applyToFields : function(o){
26431         this.items.each(function(f){
26432            Roo.apply(f, o);
26433         });
26434         return this;
26435     },
26436
26437     /**
26438      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26439      * @param {Object} values
26440      * @return {BasicForm} this
26441      */
26442     applyIfToFields : function(o){
26443         this.items.each(function(f){
26444            Roo.applyIf(f, o);
26445         });
26446         return this;
26447     }
26448 });
26449
26450 // back compat
26451 Roo.BasicForm = Roo.form.BasicForm;/*
26452  * Based on:
26453  * Ext JS Library 1.1.1
26454  * Copyright(c) 2006-2007, Ext JS, LLC.
26455  *
26456  * Originally Released Under LGPL - original licence link has changed is not relivant.
26457  *
26458  * Fork - LGPL
26459  * <script type="text/javascript">
26460  */
26461
26462 /**
26463  * @class Roo.form.Form
26464  * @extends Roo.form.BasicForm
26465  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26466  * @constructor
26467  * @param {Object} config Configuration options
26468  */
26469 Roo.form.Form = function(config){
26470     var xitems =  [];
26471     if (config.items) {
26472         xitems = config.items;
26473         delete config.items;
26474     }
26475    
26476     
26477     Roo.form.Form.superclass.constructor.call(this, null, config);
26478     this.url = this.url || this.action;
26479     if(!this.root){
26480         this.root = new Roo.form.Layout(Roo.applyIf({
26481             id: Roo.id()
26482         }, config));
26483     }
26484     this.active = this.root;
26485     /**
26486      * Array of all the buttons that have been added to this form via {@link addButton}
26487      * @type Array
26488      */
26489     this.buttons = [];
26490     this.allItems = [];
26491     this.addEvents({
26492         /**
26493          * @event clientvalidation
26494          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26495          * @param {Form} this
26496          * @param {Boolean} valid true if the form has passed client-side validation
26497          */
26498         clientvalidation: true,
26499         /**
26500          * @event rendered
26501          * Fires when the form is rendered
26502          * @param {Roo.form.Form} form
26503          */
26504         rendered : true
26505     });
26506     
26507     if (this.progressUrl) {
26508             // push a hidden field onto the list of fields..
26509             this.addxtype( {
26510                     xns: Roo.form, 
26511                     xtype : 'Hidden', 
26512                     name : 'UPLOAD_IDENTIFIER' 
26513             });
26514         }
26515         
26516     
26517     Roo.each(xitems, this.addxtype, this);
26518     
26519     
26520     
26521 };
26522
26523 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26524     /**
26525      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26526      */
26527     /**
26528      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26529      */
26530     /**
26531      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26532      */
26533     buttonAlign:'center',
26534
26535     /**
26536      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26537      */
26538     minButtonWidth:75,
26539
26540     /**
26541      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26542      * This property cascades to child containers if not set.
26543      */
26544     labelAlign:'left',
26545
26546     /**
26547      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26548      * fires a looping event with that state. This is required to bind buttons to the valid
26549      * state using the config value formBind:true on the button.
26550      */
26551     monitorValid : false,
26552
26553     /**
26554      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26555      */
26556     monitorPoll : 200,
26557     
26558     /**
26559      * @cfg {String} progressUrl - Url to return progress data 
26560      */
26561     
26562     progressUrl : false,
26563   
26564     /**
26565      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26566      * fields are added and the column is closed. If no fields are passed the column remains open
26567      * until end() is called.
26568      * @param {Object} config The config to pass to the column
26569      * @param {Field} field1 (optional)
26570      * @param {Field} field2 (optional)
26571      * @param {Field} etc (optional)
26572      * @return Column The column container object
26573      */
26574     column : function(c){
26575         var col = new Roo.form.Column(c);
26576         this.start(col);
26577         if(arguments.length > 1){ // duplicate code required because of Opera
26578             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26579             this.end();
26580         }
26581         return col;
26582     },
26583
26584     /**
26585      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26586      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26587      * until end() is called.
26588      * @param {Object} config The config to pass to the fieldset
26589      * @param {Field} field1 (optional)
26590      * @param {Field} field2 (optional)
26591      * @param {Field} etc (optional)
26592      * @return FieldSet The fieldset container object
26593      */
26594     fieldset : function(c){
26595         var fs = new Roo.form.FieldSet(c);
26596         this.start(fs);
26597         if(arguments.length > 1){ // duplicate code required because of Opera
26598             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26599             this.end();
26600         }
26601         return fs;
26602     },
26603
26604     /**
26605      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26606      * fields are added and the container is closed. If no fields are passed the container remains open
26607      * until end() is called.
26608      * @param {Object} config The config to pass to the Layout
26609      * @param {Field} field1 (optional)
26610      * @param {Field} field2 (optional)
26611      * @param {Field} etc (optional)
26612      * @return Layout The container object
26613      */
26614     container : function(c){
26615         var l = new Roo.form.Layout(c);
26616         this.start(l);
26617         if(arguments.length > 1){ // duplicate code required because of Opera
26618             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26619             this.end();
26620         }
26621         return l;
26622     },
26623
26624     /**
26625      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26626      * @param {Object} container A Roo.form.Layout or subclass of Layout
26627      * @return {Form} this
26628      */
26629     start : function(c){
26630         // cascade label info
26631         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26632         this.active.stack.push(c);
26633         c.ownerCt = this.active;
26634         this.active = c;
26635         return this;
26636     },
26637
26638     /**
26639      * Closes the current open container
26640      * @return {Form} this
26641      */
26642     end : function(){
26643         if(this.active == this.root){
26644             return this;
26645         }
26646         this.active = this.active.ownerCt;
26647         return this;
26648     },
26649
26650     /**
26651      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26652      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26653      * as the label of the field.
26654      * @param {Field} field1
26655      * @param {Field} field2 (optional)
26656      * @param {Field} etc. (optional)
26657      * @return {Form} this
26658      */
26659     add : function(){
26660         this.active.stack.push.apply(this.active.stack, arguments);
26661         this.allItems.push.apply(this.allItems,arguments);
26662         var r = [];
26663         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26664             if(a[i].isFormField){
26665                 r.push(a[i]);
26666             }
26667         }
26668         if(r.length > 0){
26669             Roo.form.Form.superclass.add.apply(this, r);
26670         }
26671         return this;
26672     },
26673     
26674
26675     
26676     
26677     
26678      /**
26679      * Find any element that has been added to a form, using it's ID or name
26680      * This can include framesets, columns etc. along with regular fields..
26681      * @param {String} id - id or name to find.
26682      
26683      * @return {Element} e - or false if nothing found.
26684      */
26685     findbyId : function(id)
26686     {
26687         var ret = false;
26688         if (!id) {
26689             return ret;
26690         }
26691         Ext.each(this.allItems, function(f){
26692             if (f.id == id || f.name == id ){
26693                 ret = f;
26694                 return false;
26695             }
26696         });
26697         return ret;
26698     },
26699
26700     
26701     
26702     /**
26703      * Render this form into the passed container. This should only be called once!
26704      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26705      * @return {Form} this
26706      */
26707     render : function(ct)
26708     {
26709         
26710         
26711         
26712         ct = Roo.get(ct);
26713         var o = this.autoCreate || {
26714             tag: 'form',
26715             method : this.method || 'POST',
26716             id : this.id || Roo.id()
26717         };
26718         this.initEl(ct.createChild(o));
26719
26720         this.root.render(this.el);
26721         
26722        
26723              
26724         this.items.each(function(f){
26725             f.render('x-form-el-'+f.id);
26726         });
26727
26728         if(this.buttons.length > 0){
26729             // tables are required to maintain order and for correct IE layout
26730             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26731                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26732                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26733             }}, null, true);
26734             var tr = tb.getElementsByTagName('tr')[0];
26735             for(var i = 0, len = this.buttons.length; i < len; i++) {
26736                 var b = this.buttons[i];
26737                 var td = document.createElement('td');
26738                 td.className = 'x-form-btn-td';
26739                 b.render(tr.appendChild(td));
26740             }
26741         }
26742         if(this.monitorValid){ // initialize after render
26743             this.startMonitoring();
26744         }
26745         this.fireEvent('rendered', this);
26746         return this;
26747     },
26748
26749     /**
26750      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26751      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26752      * object or a valid Roo.DomHelper element config
26753      * @param {Function} handler The function called when the button is clicked
26754      * @param {Object} scope (optional) The scope of the handler function
26755      * @return {Roo.Button}
26756      */
26757     addButton : function(config, handler, scope){
26758         var bc = {
26759             handler: handler,
26760             scope: scope,
26761             minWidth: this.minButtonWidth,
26762             hideParent:true
26763         };
26764         if(typeof config == "string"){
26765             bc.text = config;
26766         }else{
26767             Roo.apply(bc, config);
26768         }
26769         var btn = new Roo.Button(null, bc);
26770         this.buttons.push(btn);
26771         return btn;
26772     },
26773
26774      /**
26775      * Adds a series of form elements (using the xtype property as the factory method.
26776      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26777      * @param {Object} config 
26778      */
26779     
26780     addxtype : function()
26781     {
26782         var ar = Array.prototype.slice.call(arguments, 0);
26783         var ret = false;
26784         for(var i = 0; i < ar.length; i++) {
26785             if (!ar[i]) {
26786                 continue; // skip -- if this happends something invalid got sent, we 
26787                 // should ignore it, as basically that interface element will not show up
26788                 // and that should be pretty obvious!!
26789             }
26790             
26791             if (Roo.form[ar[i].xtype]) {
26792                 ar[i].form = this;
26793                 var fe = Roo.factory(ar[i], Roo.form);
26794                 if (!ret) {
26795                     ret = fe;
26796                 }
26797                 fe.form = this;
26798                 if (fe.store) {
26799                     fe.store.form = this;
26800                 }
26801                 if (fe.isLayout) {  
26802                          
26803                     this.start(fe);
26804                     this.allItems.push(fe);
26805                     if (fe.items && fe.addxtype) {
26806                         fe.addxtype.apply(fe, fe.items);
26807                         delete fe.items;
26808                     }
26809                      this.end();
26810                     continue;
26811                 }
26812                 
26813                 
26814                  
26815                 this.add(fe);
26816               //  console.log('adding ' + ar[i].xtype);
26817             }
26818             if (ar[i].xtype == 'Button') {  
26819                 //console.log('adding button');
26820                 //console.log(ar[i]);
26821                 this.addButton(ar[i]);
26822                 this.allItems.push(fe);
26823                 continue;
26824             }
26825             
26826             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26827                 alert('end is not supported on xtype any more, use items');
26828             //    this.end();
26829             //    //console.log('adding end');
26830             }
26831             
26832         }
26833         return ret;
26834     },
26835     
26836     /**
26837      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26838      * option "monitorValid"
26839      */
26840     startMonitoring : function(){
26841         if(!this.bound){
26842             this.bound = true;
26843             Roo.TaskMgr.start({
26844                 run : this.bindHandler,
26845                 interval : this.monitorPoll || 200,
26846                 scope: this
26847             });
26848         }
26849     },
26850
26851     /**
26852      * Stops monitoring of the valid state of this form
26853      */
26854     stopMonitoring : function(){
26855         this.bound = false;
26856     },
26857
26858     // private
26859     bindHandler : function(){
26860         if(!this.bound){
26861             return false; // stops binding
26862         }
26863         var valid = true;
26864         this.items.each(function(f){
26865             if(!f.isValid(true)){
26866                 valid = false;
26867                 return false;
26868             }
26869         });
26870         for(var i = 0, len = this.buttons.length; i < len; i++){
26871             var btn = this.buttons[i];
26872             if(btn.formBind === true && btn.disabled === valid){
26873                 btn.setDisabled(!valid);
26874             }
26875         }
26876         this.fireEvent('clientvalidation', this, valid);
26877     }
26878     
26879     
26880     
26881     
26882     
26883     
26884     
26885     
26886 });
26887
26888
26889 // back compat
26890 Roo.Form = Roo.form.Form;
26891 /*
26892  * Based on:
26893  * Ext JS Library 1.1.1
26894  * Copyright(c) 2006-2007, Ext JS, LLC.
26895  *
26896  * Originally Released Under LGPL - original licence link has changed is not relivant.
26897  *
26898  * Fork - LGPL
26899  * <script type="text/javascript">
26900  */
26901  
26902  /**
26903  * @class Roo.form.Action
26904  * Internal Class used to handle form actions
26905  * @constructor
26906  * @param {Roo.form.BasicForm} el The form element or its id
26907  * @param {Object} config Configuration options
26908  */
26909  
26910  
26911 // define the action interface
26912 Roo.form.Action = function(form, options){
26913     this.form = form;
26914     this.options = options || {};
26915 };
26916 /**
26917  * Client Validation Failed
26918  * @const 
26919  */
26920 Roo.form.Action.CLIENT_INVALID = 'client';
26921 /**
26922  * Server Validation Failed
26923  * @const 
26924  */
26925  Roo.form.Action.SERVER_INVALID = 'server';
26926  /**
26927  * Connect to Server Failed
26928  * @const 
26929  */
26930 Roo.form.Action.CONNECT_FAILURE = 'connect';
26931 /**
26932  * Reading Data from Server Failed
26933  * @const 
26934  */
26935 Roo.form.Action.LOAD_FAILURE = 'load';
26936
26937 Roo.form.Action.prototype = {
26938     type : 'default',
26939     failureType : undefined,
26940     response : undefined,
26941     result : undefined,
26942
26943     // interface method
26944     run : function(options){
26945
26946     },
26947
26948     // interface method
26949     success : function(response){
26950
26951     },
26952
26953     // interface method
26954     handleResponse : function(response){
26955
26956     },
26957
26958     // default connection failure
26959     failure : function(response){
26960         this.response = response;
26961         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26962         this.form.afterAction(this, false);
26963     },
26964
26965     processResponse : function(response){
26966         this.response = response;
26967         if(!response.responseText){
26968             return true;
26969         }
26970         this.result = this.handleResponse(response);
26971         return this.result;
26972     },
26973
26974     // utility functions used internally
26975     getUrl : function(appendParams){
26976         var url = this.options.url || this.form.url || this.form.el.dom.action;
26977         if(appendParams){
26978             var p = this.getParams();
26979             if(p){
26980                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26981             }
26982         }
26983         return url;
26984     },
26985
26986     getMethod : function(){
26987         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26988     },
26989
26990     getParams : function(){
26991         var bp = this.form.baseParams;
26992         var p = this.options.params;
26993         if(p){
26994             if(typeof p == "object"){
26995                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26996             }else if(typeof p == 'string' && bp){
26997                 p += '&' + Roo.urlEncode(bp);
26998             }
26999         }else if(bp){
27000             p = Roo.urlEncode(bp);
27001         }
27002         return p;
27003     },
27004
27005     createCallback : function(){
27006         return {
27007             success: this.success,
27008             failure: this.failure,
27009             scope: this,
27010             timeout: (this.form.timeout*1000),
27011             upload: this.form.fileUpload ? this.success : undefined
27012         };
27013     }
27014 };
27015
27016 Roo.form.Action.Submit = function(form, options){
27017     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27018 };
27019
27020 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27021     type : 'submit',
27022
27023     haveProgress : false,
27024     uploadComplete : false,
27025     
27026     // uploadProgress indicator.
27027     uploadProgress : function()
27028     {
27029         if (!this.form.progressUrl) {
27030             return;
27031         }
27032         
27033         if (!this.haveProgress) {
27034             Roo.MessageBox.progress("Uploading", "Uploading");
27035         }
27036         if (this.uploadComplete) {
27037            Roo.MessageBox.hide();
27038            return;
27039         }
27040         
27041         this.haveProgress = true;
27042    
27043         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27044         
27045         var c = new Roo.data.Connection();
27046         c.request({
27047             url : this.form.progressUrl,
27048             params: {
27049                 id : uid
27050             },
27051             method: 'GET',
27052             success : function(req){
27053                //console.log(data);
27054                 var rdata = false;
27055                 var edata;
27056                 try  {
27057                    rdata = Roo.decode(req.responseText)
27058                 } catch (e) {
27059                     Roo.log("Invalid data from server..");
27060                     Roo.log(edata);
27061                     return;
27062                 }
27063                 if (!rdata || !rdata.success) {
27064                     Roo.log(rdata);
27065                     return;
27066                 }
27067                 var data = rdata.data;
27068                 
27069                 if (this.uploadComplete) {
27070                    Roo.MessageBox.hide();
27071                    return;
27072                 }
27073                    
27074                 if (data){
27075                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27076                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27077                     );
27078                 }
27079                 this.uploadProgress.defer(2000,this);
27080             },
27081        
27082             failure: function(data) {
27083                 Roo.log('progress url failed ');
27084                 Roo.log(data);
27085             },
27086             scope : this
27087         });
27088            
27089     },
27090     
27091     
27092     run : function()
27093     {
27094         // run get Values on the form, so it syncs any secondary forms.
27095         this.form.getValues();
27096         
27097         var o = this.options;
27098         var method = this.getMethod();
27099         var isPost = method == 'POST';
27100         if(o.clientValidation === false || this.form.isValid()){
27101             
27102             if (this.form.progressUrl) {
27103                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27104                     (new Date() * 1) + '' + Math.random());
27105                     
27106             } 
27107             
27108             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27109                 form:this.form.el.dom,
27110                 url:this.getUrl(!isPost),
27111                 method: method,
27112                 params:isPost ? this.getParams() : null,
27113                 isUpload: this.form.fileUpload
27114             }));
27115             
27116             this.uploadProgress();
27117
27118         }else if (o.clientValidation !== false){ // client validation failed
27119             this.failureType = Roo.form.Action.CLIENT_INVALID;
27120             this.form.afterAction(this, false);
27121         }
27122     },
27123
27124     success : function(response)
27125     {
27126         this.uploadComplete= true;
27127         if (this.haveProgress) {
27128             Roo.MessageBox.hide();
27129         }
27130         
27131         var result = this.processResponse(response);
27132         if(result === true || result.success){
27133             this.form.afterAction(this, true);
27134             return;
27135         }
27136         if(result.errors){
27137             this.form.markInvalid(result.errors);
27138             this.failureType = Roo.form.Action.SERVER_INVALID;
27139         }
27140         this.form.afterAction(this, false);
27141     },
27142     failure : function(response)
27143     {
27144         this.uploadComplete= true;
27145         if (this.haveProgress) {
27146             Roo.MessageBox.hide();
27147         }
27148         
27149         this.response = response;
27150         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27151         this.form.afterAction(this, false);
27152     },
27153     
27154     handleResponse : function(response){
27155         if(this.form.errorReader){
27156             var rs = this.form.errorReader.read(response);
27157             var errors = [];
27158             if(rs.records){
27159                 for(var i = 0, len = rs.records.length; i < len; i++) {
27160                     var r = rs.records[i];
27161                     errors[i] = r.data;
27162                 }
27163             }
27164             if(errors.length < 1){
27165                 errors = null;
27166             }
27167             return {
27168                 success : rs.success,
27169                 errors : errors
27170             };
27171         }
27172         var ret = false;
27173         try {
27174             ret = Roo.decode(response.responseText);
27175         } catch (e) {
27176             ret = {
27177                 success: false,
27178                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27179                 errors : []
27180             };
27181         }
27182         return ret;
27183         
27184     }
27185 });
27186
27187
27188 Roo.form.Action.Load = function(form, options){
27189     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27190     this.reader = this.form.reader;
27191 };
27192
27193 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27194     type : 'load',
27195
27196     run : function(){
27197         Roo.Ajax.request(Roo.apply(
27198                 this.createCallback(), {
27199                     method:this.getMethod(),
27200                     url:this.getUrl(false),
27201                     params:this.getParams()
27202         }));
27203     },
27204
27205     success : function(response){
27206         var result = this.processResponse(response);
27207         if(result === true || !result.success || !result.data){
27208             this.failureType = Roo.form.Action.LOAD_FAILURE;
27209             this.form.afterAction(this, false);
27210             return;
27211         }
27212         this.form.clearInvalid();
27213         this.form.setValues(result.data);
27214         this.form.afterAction(this, true);
27215     },
27216
27217     handleResponse : function(response){
27218         if(this.form.reader){
27219             var rs = this.form.reader.read(response);
27220             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27221             return {
27222                 success : rs.success,
27223                 data : data
27224             };
27225         }
27226         return Roo.decode(response.responseText);
27227     }
27228 });
27229
27230 Roo.form.Action.ACTION_TYPES = {
27231     'load' : Roo.form.Action.Load,
27232     'submit' : Roo.form.Action.Submit
27233 };/*
27234  * Based on:
27235  * Ext JS Library 1.1.1
27236  * Copyright(c) 2006-2007, Ext JS, LLC.
27237  *
27238  * Originally Released Under LGPL - original licence link has changed is not relivant.
27239  *
27240  * Fork - LGPL
27241  * <script type="text/javascript">
27242  */
27243  
27244 /**
27245  * @class Roo.form.Layout
27246  * @extends Roo.Component
27247  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27248  * @constructor
27249  * @param {Object} config Configuration options
27250  */
27251 Roo.form.Layout = function(config){
27252     var xitems = [];
27253     if (config.items) {
27254         xitems = config.items;
27255         delete config.items;
27256     }
27257     Roo.form.Layout.superclass.constructor.call(this, config);
27258     this.stack = [];
27259     Roo.each(xitems, this.addxtype, this);
27260      
27261 };
27262
27263 Roo.extend(Roo.form.Layout, Roo.Component, {
27264     /**
27265      * @cfg {String/Object} autoCreate
27266      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27267      */
27268     /**
27269      * @cfg {String/Object/Function} style
27270      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27271      * a function which returns such a specification.
27272      */
27273     /**
27274      * @cfg {String} labelAlign
27275      * Valid values are "left," "top" and "right" (defaults to "left")
27276      */
27277     /**
27278      * @cfg {Number} labelWidth
27279      * Fixed width in pixels of all field labels (defaults to undefined)
27280      */
27281     /**
27282      * @cfg {Boolean} clear
27283      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27284      */
27285     clear : true,
27286     /**
27287      * @cfg {String} labelSeparator
27288      * The separator to use after field labels (defaults to ':')
27289      */
27290     labelSeparator : ':',
27291     /**
27292      * @cfg {Boolean} hideLabels
27293      * True to suppress the display of field labels in this layout (defaults to false)
27294      */
27295     hideLabels : false,
27296
27297     // private
27298     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27299     
27300     isLayout : true,
27301     
27302     // private
27303     onRender : function(ct, position){
27304         if(this.el){ // from markup
27305             this.el = Roo.get(this.el);
27306         }else {  // generate
27307             var cfg = this.getAutoCreate();
27308             this.el = ct.createChild(cfg, position);
27309         }
27310         if(this.style){
27311             this.el.applyStyles(this.style);
27312         }
27313         if(this.labelAlign){
27314             this.el.addClass('x-form-label-'+this.labelAlign);
27315         }
27316         if(this.hideLabels){
27317             this.labelStyle = "display:none";
27318             this.elementStyle = "padding-left:0;";
27319         }else{
27320             if(typeof this.labelWidth == 'number'){
27321                 this.labelStyle = "width:"+this.labelWidth+"px;";
27322                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27323             }
27324             if(this.labelAlign == 'top'){
27325                 this.labelStyle = "width:auto;";
27326                 this.elementStyle = "padding-left:0;";
27327             }
27328         }
27329         var stack = this.stack;
27330         var slen = stack.length;
27331         if(slen > 0){
27332             if(!this.fieldTpl){
27333                 var t = new Roo.Template(
27334                     '<div class="x-form-item {5}">',
27335                         '<label for="{0}" style="{2}">{1}{4}</label>',
27336                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27337                         '</div>',
27338                     '</div><div class="x-form-clear-left"></div>'
27339                 );
27340                 t.disableFormats = true;
27341                 t.compile();
27342                 Roo.form.Layout.prototype.fieldTpl = t;
27343             }
27344             for(var i = 0; i < slen; i++) {
27345                 if(stack[i].isFormField){
27346                     this.renderField(stack[i]);
27347                 }else{
27348                     this.renderComponent(stack[i]);
27349                 }
27350             }
27351         }
27352         if(this.clear){
27353             this.el.createChild({cls:'x-form-clear'});
27354         }
27355     },
27356
27357     // private
27358     renderField : function(f){
27359         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27360                f.id, //0
27361                f.fieldLabel, //1
27362                f.labelStyle||this.labelStyle||'', //2
27363                this.elementStyle||'', //3
27364                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27365                f.itemCls||this.itemCls||''  //5
27366        ], true).getPrevSibling());
27367     },
27368
27369     // private
27370     renderComponent : function(c){
27371         c.render(c.isLayout ? this.el : this.el.createChild());    
27372     },
27373     /**
27374      * Adds a object form elements (using the xtype property as the factory method.)
27375      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27376      * @param {Object} config 
27377      */
27378     addxtype : function(o)
27379     {
27380         // create the lement.
27381         o.form = this.form;
27382         var fe = Roo.factory(o, Roo.form);
27383         this.form.allItems.push(fe);
27384         this.stack.push(fe);
27385         
27386         if (fe.isFormField) {
27387             this.form.items.add(fe);
27388         }
27389          
27390         return fe;
27391     }
27392 });
27393
27394 /**
27395  * @class Roo.form.Column
27396  * @extends Roo.form.Layout
27397  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27398  * @constructor
27399  * @param {Object} config Configuration options
27400  */
27401 Roo.form.Column = function(config){
27402     Roo.form.Column.superclass.constructor.call(this, config);
27403 };
27404
27405 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27406     /**
27407      * @cfg {Number/String} width
27408      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27409      */
27410     /**
27411      * @cfg {String/Object} autoCreate
27412      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27413      */
27414
27415     // private
27416     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27417
27418     // private
27419     onRender : function(ct, position){
27420         Roo.form.Column.superclass.onRender.call(this, ct, position);
27421         if(this.width){
27422             this.el.setWidth(this.width);
27423         }
27424     }
27425 });
27426
27427
27428 /**
27429  * @class Roo.form.Row
27430  * @extends Roo.form.Layout
27431  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27432  * @constructor
27433  * @param {Object} config Configuration options
27434  */
27435
27436  
27437 Roo.form.Row = function(config){
27438     Roo.form.Row.superclass.constructor.call(this, config);
27439 };
27440  
27441 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27442       /**
27443      * @cfg {Number/String} width
27444      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27445      */
27446     /**
27447      * @cfg {Number/String} height
27448      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27449      */
27450     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27451     
27452     padWidth : 20,
27453     // private
27454     onRender : function(ct, position){
27455         //console.log('row render');
27456         if(!this.rowTpl){
27457             var t = new Roo.Template(
27458                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27459                     '<label for="{0}" style="{2}">{1}{4}</label>',
27460                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27461                     '</div>',
27462                 '</div>'
27463             );
27464             t.disableFormats = true;
27465             t.compile();
27466             Roo.form.Layout.prototype.rowTpl = t;
27467         }
27468         this.fieldTpl = this.rowTpl;
27469         
27470         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27471         var labelWidth = 100;
27472         
27473         if ((this.labelAlign != 'top')) {
27474             if (typeof this.labelWidth == 'number') {
27475                 labelWidth = this.labelWidth
27476             }
27477             this.padWidth =  20 + labelWidth;
27478             
27479         }
27480         
27481         Roo.form.Column.superclass.onRender.call(this, ct, position);
27482         if(this.width){
27483             this.el.setWidth(this.width);
27484         }
27485         if(this.height){
27486             this.el.setHeight(this.height);
27487         }
27488     },
27489     
27490     // private
27491     renderField : function(f){
27492         f.fieldEl = this.fieldTpl.append(this.el, [
27493                f.id, f.fieldLabel,
27494                f.labelStyle||this.labelStyle||'',
27495                this.elementStyle||'',
27496                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27497                f.itemCls||this.itemCls||'',
27498                f.width ? f.width + this.padWidth : 160 + this.padWidth
27499        ],true);
27500     }
27501 });
27502  
27503
27504 /**
27505  * @class Roo.form.FieldSet
27506  * @extends Roo.form.Layout
27507  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27508  * @constructor
27509  * @param {Object} config Configuration options
27510  */
27511 Roo.form.FieldSet = function(config){
27512     Roo.form.FieldSet.superclass.constructor.call(this, config);
27513 };
27514
27515 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27516     /**
27517      * @cfg {String} legend
27518      * The text to display as the legend for the FieldSet (defaults to '')
27519      */
27520     /**
27521      * @cfg {String/Object} autoCreate
27522      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27523      */
27524
27525     // private
27526     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27527
27528     // private
27529     onRender : function(ct, position){
27530         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27531         if(this.legend){
27532             this.setLegend(this.legend);
27533         }
27534     },
27535
27536     // private
27537     setLegend : function(text){
27538         if(this.rendered){
27539             this.el.child('legend').update(text);
27540         }
27541     }
27542 });/*
27543  * Based on:
27544  * Ext JS Library 1.1.1
27545  * Copyright(c) 2006-2007, Ext JS, LLC.
27546  *
27547  * Originally Released Under LGPL - original licence link has changed is not relivant.
27548  *
27549  * Fork - LGPL
27550  * <script type="text/javascript">
27551  */
27552 /**
27553  * @class Roo.form.VTypes
27554  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27555  * @singleton
27556  */
27557 Roo.form.VTypes = function(){
27558     // closure these in so they are only created once.
27559     var alpha = /^[a-zA-Z_]+$/;
27560     var alphanum = /^[a-zA-Z0-9_]+$/;
27561     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27562     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27563
27564     // All these messages and functions are configurable
27565     return {
27566         /**
27567          * The function used to validate email addresses
27568          * @param {String} value The email address
27569          */
27570         'email' : function(v){
27571             return email.test(v);
27572         },
27573         /**
27574          * The error text to display when the email validation function returns false
27575          * @type String
27576          */
27577         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27578         /**
27579          * The keystroke filter mask to be applied on email input
27580          * @type RegExp
27581          */
27582         'emailMask' : /[a-z0-9_\.\-@]/i,
27583
27584         /**
27585          * The function used to validate URLs
27586          * @param {String} value The URL
27587          */
27588         'url' : function(v){
27589             return url.test(v);
27590         },
27591         /**
27592          * The error text to display when the url validation function returns false
27593          * @type String
27594          */
27595         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27596         
27597         /**
27598          * The function used to validate alpha values
27599          * @param {String} value The value
27600          */
27601         'alpha' : function(v){
27602             return alpha.test(v);
27603         },
27604         /**
27605          * The error text to display when the alpha validation function returns false
27606          * @type String
27607          */
27608         'alphaText' : 'This field should only contain letters and _',
27609         /**
27610          * The keystroke filter mask to be applied on alpha input
27611          * @type RegExp
27612          */
27613         'alphaMask' : /[a-z_]/i,
27614
27615         /**
27616          * The function used to validate alphanumeric values
27617          * @param {String} value The value
27618          */
27619         'alphanum' : function(v){
27620             return alphanum.test(v);
27621         },
27622         /**
27623          * The error text to display when the alphanumeric validation function returns false
27624          * @type String
27625          */
27626         'alphanumText' : 'This field should only contain letters, numbers and _',
27627         /**
27628          * The keystroke filter mask to be applied on alphanumeric input
27629          * @type RegExp
27630          */
27631         'alphanumMask' : /[a-z0-9_]/i
27632     };
27633 }();//<script type="text/javascript">
27634
27635 /**
27636  * @class Roo.form.FCKeditor
27637  * @extends Roo.form.TextArea
27638  * Wrapper around the FCKEditor http://www.fckeditor.net
27639  * @constructor
27640  * Creates a new FCKeditor
27641  * @param {Object} config Configuration options
27642  */
27643 Roo.form.FCKeditor = function(config){
27644     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27645     this.addEvents({
27646          /**
27647          * @event editorinit
27648          * Fired when the editor is initialized - you can add extra handlers here..
27649          * @param {FCKeditor} this
27650          * @param {Object} the FCK object.
27651          */
27652         editorinit : true
27653     });
27654     
27655     
27656 };
27657 Roo.form.FCKeditor.editors = { };
27658 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27659 {
27660     //defaultAutoCreate : {
27661     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27662     //},
27663     // private
27664     /**
27665      * @cfg {Object} fck options - see fck manual for details.
27666      */
27667     fckconfig : false,
27668     
27669     /**
27670      * @cfg {Object} fck toolbar set (Basic or Default)
27671      */
27672     toolbarSet : 'Basic',
27673     /**
27674      * @cfg {Object} fck BasePath
27675      */ 
27676     basePath : '/fckeditor/',
27677     
27678     
27679     frame : false,
27680     
27681     value : '',
27682     
27683    
27684     onRender : function(ct, position)
27685     {
27686         if(!this.el){
27687             this.defaultAutoCreate = {
27688                 tag: "textarea",
27689                 style:"width:300px;height:60px;",
27690                 autocomplete: "off"
27691             };
27692         }
27693         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27694         /*
27695         if(this.grow){
27696             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27697             if(this.preventScrollbars){
27698                 this.el.setStyle("overflow", "hidden");
27699             }
27700             this.el.setHeight(this.growMin);
27701         }
27702         */
27703         //console.log('onrender' + this.getId() );
27704         Roo.form.FCKeditor.editors[this.getId()] = this;
27705          
27706
27707         this.replaceTextarea() ;
27708         
27709     },
27710     
27711     getEditor : function() {
27712         return this.fckEditor;
27713     },
27714     /**
27715      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27716      * @param {Mixed} value The value to set
27717      */
27718     
27719     
27720     setValue : function(value)
27721     {
27722         //console.log('setValue: ' + value);
27723         
27724         if(typeof(value) == 'undefined') { // not sure why this is happending...
27725             return;
27726         }
27727         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27728         
27729         //if(!this.el || !this.getEditor()) {
27730         //    this.value = value;
27731             //this.setValue.defer(100,this,[value]);    
27732         //    return;
27733         //} 
27734         
27735         if(!this.getEditor()) {
27736             return;
27737         }
27738         
27739         this.getEditor().SetData(value);
27740         
27741         //
27742
27743     },
27744
27745     /**
27746      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27747      * @return {Mixed} value The field value
27748      */
27749     getValue : function()
27750     {
27751         
27752         if (this.frame && this.frame.dom.style.display == 'none') {
27753             return Roo.form.FCKeditor.superclass.getValue.call(this);
27754         }
27755         
27756         if(!this.el || !this.getEditor()) {
27757            
27758            // this.getValue.defer(100,this); 
27759             return this.value;
27760         }
27761        
27762         
27763         var value=this.getEditor().GetData();
27764         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27765         return Roo.form.FCKeditor.superclass.getValue.call(this);
27766         
27767
27768     },
27769
27770     /**
27771      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27772      * @return {Mixed} value The field value
27773      */
27774     getRawValue : function()
27775     {
27776         if (this.frame && this.frame.dom.style.display == 'none') {
27777             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27778         }
27779         
27780         if(!this.el || !this.getEditor()) {
27781             //this.getRawValue.defer(100,this); 
27782             return this.value;
27783             return;
27784         }
27785         
27786         
27787         
27788         var value=this.getEditor().GetData();
27789         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27790         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27791          
27792     },
27793     
27794     setSize : function(w,h) {
27795         
27796         
27797         
27798         //if (this.frame && this.frame.dom.style.display == 'none') {
27799         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27800         //    return;
27801         //}
27802         //if(!this.el || !this.getEditor()) {
27803         //    this.setSize.defer(100,this, [w,h]); 
27804         //    return;
27805         //}
27806         
27807         
27808         
27809         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27810         
27811         this.frame.dom.setAttribute('width', w);
27812         this.frame.dom.setAttribute('height', h);
27813         this.frame.setSize(w,h);
27814         
27815     },
27816     
27817     toggleSourceEdit : function(value) {
27818         
27819       
27820          
27821         this.el.dom.style.display = value ? '' : 'none';
27822         this.frame.dom.style.display = value ?  'none' : '';
27823         
27824     },
27825     
27826     
27827     focus: function(tag)
27828     {
27829         if (this.frame.dom.style.display == 'none') {
27830             return Roo.form.FCKeditor.superclass.focus.call(this);
27831         }
27832         if(!this.el || !this.getEditor()) {
27833             this.focus.defer(100,this, [tag]); 
27834             return;
27835         }
27836         
27837         
27838         
27839         
27840         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27841         this.getEditor().Focus();
27842         if (tgs.length) {
27843             if (!this.getEditor().Selection.GetSelection()) {
27844                 this.focus.defer(100,this, [tag]); 
27845                 return;
27846             }
27847             
27848             
27849             var r = this.getEditor().EditorDocument.createRange();
27850             r.setStart(tgs[0],0);
27851             r.setEnd(tgs[0],0);
27852             this.getEditor().Selection.GetSelection().removeAllRanges();
27853             this.getEditor().Selection.GetSelection().addRange(r);
27854             this.getEditor().Focus();
27855         }
27856         
27857     },
27858     
27859     
27860     
27861     replaceTextarea : function()
27862     {
27863         if ( document.getElementById( this.getId() + '___Frame' ) )
27864             return ;
27865         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27866         //{
27867             // We must check the elements firstly using the Id and then the name.
27868         var oTextarea = document.getElementById( this.getId() );
27869         
27870         var colElementsByName = document.getElementsByName( this.getId() ) ;
27871          
27872         oTextarea.style.display = 'none' ;
27873
27874         if ( oTextarea.tabIndex ) {            
27875             this.TabIndex = oTextarea.tabIndex ;
27876         }
27877         
27878         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27879         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27880         this.frame = Roo.get(this.getId() + '___Frame')
27881     },
27882     
27883     _getConfigHtml : function()
27884     {
27885         var sConfig = '' ;
27886
27887         for ( var o in this.fckconfig ) {
27888             sConfig += sConfig.length > 0  ? '&amp;' : '';
27889             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27890         }
27891
27892         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27893     },
27894     
27895     
27896     _getIFrameHtml : function()
27897     {
27898         var sFile = 'fckeditor.html' ;
27899         /* no idea what this is about..
27900         try
27901         {
27902             if ( (/fcksource=true/i).test( window.top.location.search ) )
27903                 sFile = 'fckeditor.original.html' ;
27904         }
27905         catch (e) { 
27906         */
27907
27908         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27909         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27910         
27911         
27912         var html = '<iframe id="' + this.getId() +
27913             '___Frame" src="' + sLink +
27914             '" width="' + this.width +
27915             '" height="' + this.height + '"' +
27916             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27917             ' frameborder="0" scrolling="no"></iframe>' ;
27918
27919         return html ;
27920     },
27921     
27922     _insertHtmlBefore : function( html, element )
27923     {
27924         if ( element.insertAdjacentHTML )       {
27925             // IE
27926             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27927         } else { // Gecko
27928             var oRange = document.createRange() ;
27929             oRange.setStartBefore( element ) ;
27930             var oFragment = oRange.createContextualFragment( html );
27931             element.parentNode.insertBefore( oFragment, element ) ;
27932         }
27933     }
27934     
27935     
27936   
27937     
27938     
27939     
27940     
27941
27942 });
27943
27944 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27945
27946 function FCKeditor_OnComplete(editorInstance){
27947     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27948     f.fckEditor = editorInstance;
27949     //console.log("loaded");
27950     f.fireEvent('editorinit', f, editorInstance);
27951
27952   
27953
27954  
27955
27956
27957
27958
27959
27960
27961
27962
27963
27964
27965
27966
27967
27968
27969
27970 //<script type="text/javascript">
27971 /**
27972  * @class Roo.form.GridField
27973  * @extends Roo.form.Field
27974  * Embed a grid (or editable grid into a form)
27975  * STATUS ALPHA
27976  * 
27977  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27978  * it needs 
27979  * xgrid.store = Roo.data.Store
27980  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27981  * xgrid.store.reader = Roo.data.JsonReader 
27982  * 
27983  * 
27984  * @constructor
27985  * Creates a new GridField
27986  * @param {Object} config Configuration options
27987  */
27988 Roo.form.GridField = function(config){
27989     Roo.form.GridField.superclass.constructor.call(this, config);
27990      
27991 };
27992
27993 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27994     /**
27995      * @cfg {Number} width  - used to restrict width of grid..
27996      */
27997     width : 100,
27998     /**
27999      * @cfg {Number} height - used to restrict height of grid..
28000      */
28001     height : 50,
28002      /**
28003      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28004          * 
28005          *}
28006      */
28007     xgrid : false, 
28008     /**
28009      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28010      * {tag: "input", type: "checkbox", autocomplete: "off"})
28011      */
28012    // defaultAutoCreate : { tag: 'div' },
28013     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28014     /**
28015      * @cfg {String} addTitle Text to include for adding a title.
28016      */
28017     addTitle : false,
28018     //
28019     onResize : function(){
28020         Roo.form.Field.superclass.onResize.apply(this, arguments);
28021     },
28022
28023     initEvents : function(){
28024         // Roo.form.Checkbox.superclass.initEvents.call(this);
28025         // has no events...
28026        
28027     },
28028
28029
28030     getResizeEl : function(){
28031         return this.wrap;
28032     },
28033
28034     getPositionEl : function(){
28035         return this.wrap;
28036     },
28037
28038     // private
28039     onRender : function(ct, position){
28040         
28041         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28042         var style = this.style;
28043         delete this.style;
28044         
28045         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28046         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28047         this.viewEl = this.wrap.createChild({ tag: 'div' });
28048         if (style) {
28049             this.viewEl.applyStyles(style);
28050         }
28051         if (this.width) {
28052             this.viewEl.setWidth(this.width);
28053         }
28054         if (this.height) {
28055             this.viewEl.setHeight(this.height);
28056         }
28057         //if(this.inputValue !== undefined){
28058         //this.setValue(this.value);
28059         
28060         
28061         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28062         
28063         
28064         this.grid.render();
28065         this.grid.getDataSource().on('remove', this.refreshValue, this);
28066         this.grid.getDataSource().on('update', this.refreshValue, this);
28067         this.grid.on('afteredit', this.refreshValue, this);
28068  
28069     },
28070      
28071     
28072     /**
28073      * Sets the value of the item. 
28074      * @param {String} either an object  or a string..
28075      */
28076     setValue : function(v){
28077         //this.value = v;
28078         v = v || []; // empty set..
28079         // this does not seem smart - it really only affects memoryproxy grids..
28080         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28081             var ds = this.grid.getDataSource();
28082             // assumes a json reader..
28083             var data = {}
28084             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28085             ds.loadData( data);
28086         }
28087         Roo.form.GridField.superclass.setValue.call(this, v);
28088         this.refreshValue();
28089         // should load data in the grid really....
28090     },
28091     
28092     // private
28093     refreshValue: function() {
28094          var val = [];
28095         this.grid.getDataSource().each(function(r) {
28096             val.push(r.data);
28097         });
28098         this.el.dom.value = Roo.encode(val);
28099     }
28100     
28101      
28102     
28103     
28104 });/*
28105  * Based on:
28106  * Ext JS Library 1.1.1
28107  * Copyright(c) 2006-2007, Ext JS, LLC.
28108  *
28109  * Originally Released Under LGPL - original licence link has changed is not relivant.
28110  *
28111  * Fork - LGPL
28112  * <script type="text/javascript">
28113  */
28114 /**
28115  * @class Roo.form.DisplayField
28116  * @extends Roo.form.Field
28117  * A generic Field to display non-editable data.
28118  * @constructor
28119  * Creates a new Display Field item.
28120  * @param {Object} config Configuration options
28121  */
28122 Roo.form.DisplayField = function(config){
28123     Roo.form.DisplayField.superclass.constructor.call(this, config);
28124     
28125 };
28126
28127 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28128     inputType:      'hidden',
28129     allowBlank:     true,
28130     readOnly:         true,
28131     
28132  
28133     /**
28134      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28135      */
28136     focusClass : undefined,
28137     /**
28138      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28139      */
28140     fieldClass: 'x-form-field',
28141     
28142      /**
28143      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28144      */
28145     valueRenderer: undefined,
28146     
28147     width: 100,
28148     /**
28149      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28150      * {tag: "input", type: "checkbox", autocomplete: "off"})
28151      */
28152      
28153  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28154
28155     onResize : function(){
28156         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28157         
28158     },
28159
28160     initEvents : function(){
28161         // Roo.form.Checkbox.superclass.initEvents.call(this);
28162         // has no events...
28163        
28164     },
28165
28166
28167     getResizeEl : function(){
28168         return this.wrap;
28169     },
28170
28171     getPositionEl : function(){
28172         return this.wrap;
28173     },
28174
28175     // private
28176     onRender : function(ct, position){
28177         
28178         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28179         //if(this.inputValue !== undefined){
28180         this.wrap = this.el.wrap();
28181         
28182         this.viewEl = this.wrap.createChild({ tag: 'div'});
28183         
28184         if (this.bodyStyle) {
28185             this.viewEl.applyStyles(this.bodyStyle);
28186         }
28187         //this.viewEl.setStyle('padding', '2px');
28188         
28189         this.setValue(this.value);
28190         
28191     },
28192 /*
28193     // private
28194     initValue : Roo.emptyFn,
28195
28196   */
28197
28198         // private
28199     onClick : function(){
28200         
28201     },
28202
28203     /**
28204      * Sets the checked state of the checkbox.
28205      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28206      */
28207     setValue : function(v){
28208         this.value = v;
28209         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28210         // this might be called before we have a dom element..
28211         if (!this.viewEl) {
28212             return;
28213         }
28214         this.viewEl.dom.innerHTML = html;
28215         Roo.form.DisplayField.superclass.setValue.call(this, v);
28216
28217     }
28218 });//<script type="text/javasscript">
28219  
28220
28221 /**
28222  * @class Roo.DDView
28223  * A DnD enabled version of Roo.View.
28224  * @param {Element/String} container The Element in which to create the View.
28225  * @param {String} tpl The template string used to create the markup for each element of the View
28226  * @param {Object} config The configuration properties. These include all the config options of
28227  * {@link Roo.View} plus some specific to this class.<br>
28228  * <p>
28229  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28230  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28231  * <p>
28232  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28233 .x-view-drag-insert-above {
28234         border-top:1px dotted #3366cc;
28235 }
28236 .x-view-drag-insert-below {
28237         border-bottom:1px dotted #3366cc;
28238 }
28239 </code></pre>
28240  * 
28241  */
28242  
28243 Roo.DDView = function(container, tpl, config) {
28244     Roo.DDView.superclass.constructor.apply(this, arguments);
28245     this.getEl().setStyle("outline", "0px none");
28246     this.getEl().unselectable();
28247     if (this.dragGroup) {
28248                 this.setDraggable(this.dragGroup.split(","));
28249     }
28250     if (this.dropGroup) {
28251                 this.setDroppable(this.dropGroup.split(","));
28252     }
28253     if (this.deletable) {
28254         this.setDeletable();
28255     }
28256     this.isDirtyFlag = false;
28257         this.addEvents({
28258                 "drop" : true
28259         });
28260 };
28261
28262 Roo.extend(Roo.DDView, Roo.View, {
28263 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28264 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28265 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28266 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28267
28268         isFormField: true,
28269
28270         reset: Roo.emptyFn,
28271         
28272         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28273
28274         validate: function() {
28275                 return true;
28276         },
28277         
28278         destroy: function() {
28279                 this.purgeListeners();
28280                 this.getEl.removeAllListeners();
28281                 this.getEl().remove();
28282                 if (this.dragZone) {
28283                         if (this.dragZone.destroy) {
28284                                 this.dragZone.destroy();
28285                         }
28286                 }
28287                 if (this.dropZone) {
28288                         if (this.dropZone.destroy) {
28289                                 this.dropZone.destroy();
28290                         }
28291                 }
28292         },
28293
28294 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28295         getName: function() {
28296                 return this.name;
28297         },
28298
28299 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28300         setValue: function(v) {
28301                 if (!this.store) {
28302                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28303                 }
28304                 var data = {};
28305                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28306                 this.store.proxy = new Roo.data.MemoryProxy(data);
28307                 this.store.load();
28308         },
28309
28310 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28311         getValue: function() {
28312                 var result = '(';
28313                 this.store.each(function(rec) {
28314                         result += rec.id + ',';
28315                 });
28316                 return result.substr(0, result.length - 1) + ')';
28317         },
28318         
28319         getIds: function() {
28320                 var i = 0, result = new Array(this.store.getCount());
28321                 this.store.each(function(rec) {
28322                         result[i++] = rec.id;
28323                 });
28324                 return result;
28325         },
28326         
28327         isDirty: function() {
28328                 return this.isDirtyFlag;
28329         },
28330
28331 /**
28332  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28333  *      whole Element becomes the target, and this causes the drop gesture to append.
28334  */
28335     getTargetFromEvent : function(e) {
28336                 var target = e.getTarget();
28337                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28338                 target = target.parentNode;
28339                 }
28340                 if (!target) {
28341                         target = this.el.dom.lastChild || this.el.dom;
28342                 }
28343                 return target;
28344     },
28345
28346 /**
28347  *      Create the drag data which consists of an object which has the property "ddel" as
28348  *      the drag proxy element. 
28349  */
28350     getDragData : function(e) {
28351         var target = this.findItemFromChild(e.getTarget());
28352                 if(target) {
28353                         this.handleSelection(e);
28354                         var selNodes = this.getSelectedNodes();
28355             var dragData = {
28356                 source: this,
28357                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28358                 nodes: selNodes,
28359                 records: []
28360                         };
28361                         var selectedIndices = this.getSelectedIndexes();
28362                         for (var i = 0; i < selectedIndices.length; i++) {
28363                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28364                         }
28365                         if (selNodes.length == 1) {
28366                                 dragData.ddel = target.cloneNode(true); // the div element
28367                         } else {
28368                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28369                                 div.className = 'multi-proxy';
28370                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28371                                         div.appendChild(selNodes[i].cloneNode(true));
28372                                 }
28373                                 dragData.ddel = div;
28374                         }
28375             //console.log(dragData)
28376             //console.log(dragData.ddel.innerHTML)
28377                         return dragData;
28378                 }
28379         //console.log('nodragData')
28380                 return false;
28381     },
28382     
28383 /**     Specify to which ddGroup items in this DDView may be dragged. */
28384     setDraggable: function(ddGroup) {
28385         if (ddGroup instanceof Array) {
28386                 Roo.each(ddGroup, this.setDraggable, this);
28387                 return;
28388         }
28389         if (this.dragZone) {
28390                 this.dragZone.addToGroup(ddGroup);
28391         } else {
28392                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28393                                 containerScroll: true,
28394                                 ddGroup: ddGroup 
28395
28396                         });
28397 //                      Draggability implies selection. DragZone's mousedown selects the element.
28398                         if (!this.multiSelect) { this.singleSelect = true; }
28399
28400 //                      Wire the DragZone's handlers up to methods in *this*
28401                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28402                 }
28403     },
28404
28405 /**     Specify from which ddGroup this DDView accepts drops. */
28406     setDroppable: function(ddGroup) {
28407         if (ddGroup instanceof Array) {
28408                 Roo.each(ddGroup, this.setDroppable, this);
28409                 return;
28410         }
28411         if (this.dropZone) {
28412                 this.dropZone.addToGroup(ddGroup);
28413         } else {
28414                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28415                                 containerScroll: true,
28416                                 ddGroup: ddGroup
28417                         });
28418
28419 //                      Wire the DropZone's handlers up to methods in *this*
28420                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28421                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28422                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28423                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28424                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28425                 }
28426     },
28427
28428 /**     Decide whether to drop above or below a View node. */
28429     getDropPoint : function(e, n, dd){
28430         if (n == this.el.dom) { return "above"; }
28431                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28432                 var c = t + (b - t) / 2;
28433                 var y = Roo.lib.Event.getPageY(e);
28434                 if(y <= c) {
28435                         return "above";
28436                 }else{
28437                         return "below";
28438                 }
28439     },
28440
28441     onNodeEnter : function(n, dd, e, data){
28442                 return false;
28443     },
28444     
28445     onNodeOver : function(n, dd, e, data){
28446                 var pt = this.getDropPoint(e, n, dd);
28447                 // set the insert point style on the target node
28448                 var dragElClass = this.dropNotAllowed;
28449                 if (pt) {
28450                         var targetElClass;
28451                         if (pt == "above"){
28452                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28453                                 targetElClass = "x-view-drag-insert-above";
28454                         } else {
28455                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28456                                 targetElClass = "x-view-drag-insert-below";
28457                         }
28458                         if (this.lastInsertClass != targetElClass){
28459                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28460                                 this.lastInsertClass = targetElClass;
28461                         }
28462                 }
28463                 return dragElClass;
28464         },
28465
28466     onNodeOut : function(n, dd, e, data){
28467                 this.removeDropIndicators(n);
28468     },
28469
28470     onNodeDrop : function(n, dd, e, data){
28471         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28472                 return false;
28473         }
28474         var pt = this.getDropPoint(e, n, dd);
28475                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28476                 if (pt == "below") { insertAt++; }
28477                 for (var i = 0; i < data.records.length; i++) {
28478                         var r = data.records[i];
28479                         var dup = this.store.getById(r.id);
28480                         if (dup && (dd != this.dragZone)) {
28481                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28482                         } else {
28483                                 if (data.copy) {
28484                                         this.store.insert(insertAt++, r.copy());
28485                                 } else {
28486                                         data.source.isDirtyFlag = true;
28487                                         r.store.remove(r);
28488                                         this.store.insert(insertAt++, r);
28489                                 }
28490                                 this.isDirtyFlag = true;
28491                         }
28492                 }
28493                 this.dragZone.cachedTarget = null;
28494                 return true;
28495     },
28496
28497     removeDropIndicators : function(n){
28498                 if(n){
28499                         Roo.fly(n).removeClass([
28500                                 "x-view-drag-insert-above",
28501                                 "x-view-drag-insert-below"]);
28502                         this.lastInsertClass = "_noclass";
28503                 }
28504     },
28505
28506 /**
28507  *      Utility method. Add a delete option to the DDView's context menu.
28508  *      @param {String} imageUrl The URL of the "delete" icon image.
28509  */
28510         setDeletable: function(imageUrl) {
28511                 if (!this.singleSelect && !this.multiSelect) {
28512                         this.singleSelect = true;
28513                 }
28514                 var c = this.getContextMenu();
28515                 this.contextMenu.on("itemclick", function(item) {
28516                         switch (item.id) {
28517                                 case "delete":
28518                                         this.remove(this.getSelectedIndexes());
28519                                         break;
28520                         }
28521                 }, this);
28522                 this.contextMenu.add({
28523                         icon: imageUrl,
28524                         id: "delete",
28525                         text: 'Delete'
28526                 });
28527         },
28528         
28529 /**     Return the context menu for this DDView. */
28530         getContextMenu: function() {
28531                 if (!this.contextMenu) {
28532 //                      Create the View's context menu
28533                         this.contextMenu = new Roo.menu.Menu({
28534                                 id: this.id + "-contextmenu"
28535                         });
28536                         this.el.on("contextmenu", this.showContextMenu, this);
28537                 }
28538                 return this.contextMenu;
28539         },
28540         
28541         disableContextMenu: function() {
28542                 if (this.contextMenu) {
28543                         this.el.un("contextmenu", this.showContextMenu, this);
28544                 }
28545         },
28546
28547         showContextMenu: function(e, item) {
28548         item = this.findItemFromChild(e.getTarget());
28549                 if (item) {
28550                         e.stopEvent();
28551                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28552                         this.contextMenu.showAt(e.getXY());
28553             }
28554     },
28555
28556 /**
28557  *      Remove {@link Roo.data.Record}s at the specified indices.
28558  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28559  */
28560     remove: function(selectedIndices) {
28561                 selectedIndices = [].concat(selectedIndices);
28562                 for (var i = 0; i < selectedIndices.length; i++) {
28563                         var rec = this.store.getAt(selectedIndices[i]);
28564                         this.store.remove(rec);
28565                 }
28566     },
28567
28568 /**
28569  *      Double click fires the event, but also, if this is draggable, and there is only one other
28570  *      related DropZone, it transfers the selected node.
28571  */
28572     onDblClick : function(e){
28573         var item = this.findItemFromChild(e.getTarget());
28574         if(item){
28575             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28576                 return false;
28577             }
28578             if (this.dragGroup) {
28579                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28580                     while (targets.indexOf(this.dropZone) > -1) {
28581                             targets.remove(this.dropZone);
28582                                 }
28583                     if (targets.length == 1) {
28584                                         this.dragZone.cachedTarget = null;
28585                         var el = Roo.get(targets[0].getEl());
28586                         var box = el.getBox(true);
28587                         targets[0].onNodeDrop(el.dom, {
28588                                 target: el.dom,
28589                                 xy: [box.x, box.y + box.height - 1]
28590                         }, null, this.getDragData(e));
28591                     }
28592                 }
28593         }
28594     },
28595     
28596     handleSelection: function(e) {
28597                 this.dragZone.cachedTarget = null;
28598         var item = this.findItemFromChild(e.getTarget());
28599         if (!item) {
28600                 this.clearSelections(true);
28601                 return;
28602         }
28603                 if (item && (this.multiSelect || this.singleSelect)){
28604                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28605                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28606                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28607                                 this.unselect(item);
28608                         } else {
28609                                 this.select(item, this.multiSelect && e.ctrlKey);
28610                                 this.lastSelection = item;
28611                         }
28612                 }
28613     },
28614
28615     onItemClick : function(item, index, e){
28616                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28617                         return false;
28618                 }
28619                 return true;
28620     },
28621
28622     unselect : function(nodeInfo, suppressEvent){
28623                 var node = this.getNode(nodeInfo);
28624                 if(node && this.isSelected(node)){
28625                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28626                                 Roo.fly(node).removeClass(this.selectedClass);
28627                                 this.selections.remove(node);
28628                                 if(!suppressEvent){
28629                                         this.fireEvent("selectionchange", this, this.selections);
28630                                 }
28631                         }
28632                 }
28633     }
28634 });
28635 /*
28636  * Based on:
28637  * Ext JS Library 1.1.1
28638  * Copyright(c) 2006-2007, Ext JS, LLC.
28639  *
28640  * Originally Released Under LGPL - original licence link has changed is not relivant.
28641  *
28642  * Fork - LGPL
28643  * <script type="text/javascript">
28644  */
28645  
28646 /**
28647  * @class Roo.LayoutManager
28648  * @extends Roo.util.Observable
28649  * Base class for layout managers.
28650  */
28651 Roo.LayoutManager = function(container, config){
28652     Roo.LayoutManager.superclass.constructor.call(this);
28653     this.el = Roo.get(container);
28654     // ie scrollbar fix
28655     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28656         document.body.scroll = "no";
28657     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28658         this.el.position('relative');
28659     }
28660     this.id = this.el.id;
28661     this.el.addClass("x-layout-container");
28662     /** false to disable window resize monitoring @type Boolean */
28663     this.monitorWindowResize = true;
28664     this.regions = {};
28665     this.addEvents({
28666         /**
28667          * @event layout
28668          * Fires when a layout is performed. 
28669          * @param {Roo.LayoutManager} this
28670          */
28671         "layout" : true,
28672         /**
28673          * @event regionresized
28674          * Fires when the user resizes a region. 
28675          * @param {Roo.LayoutRegion} region The resized region
28676          * @param {Number} newSize The new size (width for east/west, height for north/south)
28677          */
28678         "regionresized" : true,
28679         /**
28680          * @event regioncollapsed
28681          * Fires when a region is collapsed. 
28682          * @param {Roo.LayoutRegion} region The collapsed region
28683          */
28684         "regioncollapsed" : true,
28685         /**
28686          * @event regionexpanded
28687          * Fires when a region is expanded.  
28688          * @param {Roo.LayoutRegion} region The expanded region
28689          */
28690         "regionexpanded" : true
28691     });
28692     this.updating = false;
28693     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28694 };
28695
28696 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28697     /**
28698      * Returns true if this layout is currently being updated
28699      * @return {Boolean}
28700      */
28701     isUpdating : function(){
28702         return this.updating; 
28703     },
28704     
28705     /**
28706      * Suspend the LayoutManager from doing auto-layouts while
28707      * making multiple add or remove calls
28708      */
28709     beginUpdate : function(){
28710         this.updating = true;    
28711     },
28712     
28713     /**
28714      * Restore auto-layouts and optionally disable the manager from performing a layout
28715      * @param {Boolean} noLayout true to disable a layout update 
28716      */
28717     endUpdate : function(noLayout){
28718         this.updating = false;
28719         if(!noLayout){
28720             this.layout();
28721         }    
28722     },
28723     
28724     layout: function(){
28725         
28726     },
28727     
28728     onRegionResized : function(region, newSize){
28729         this.fireEvent("regionresized", region, newSize);
28730         this.layout();
28731     },
28732     
28733     onRegionCollapsed : function(region){
28734         this.fireEvent("regioncollapsed", region);
28735     },
28736     
28737     onRegionExpanded : function(region){
28738         this.fireEvent("regionexpanded", region);
28739     },
28740         
28741     /**
28742      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28743      * performs box-model adjustments.
28744      * @return {Object} The size as an object {width: (the width), height: (the height)}
28745      */
28746     getViewSize : function(){
28747         var size;
28748         if(this.el.dom != document.body){
28749             size = this.el.getSize();
28750         }else{
28751             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28752         }
28753         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28754         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28755         return size;
28756     },
28757     
28758     /**
28759      * Returns the Element this layout is bound to.
28760      * @return {Roo.Element}
28761      */
28762     getEl : function(){
28763         return this.el;
28764     },
28765     
28766     /**
28767      * Returns the specified region.
28768      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28769      * @return {Roo.LayoutRegion}
28770      */
28771     getRegion : function(target){
28772         return this.regions[target.toLowerCase()];
28773     },
28774     
28775     onWindowResize : function(){
28776         if(this.monitorWindowResize){
28777             this.layout();
28778         }
28779     }
28780 });/*
28781  * Based on:
28782  * Ext JS Library 1.1.1
28783  * Copyright(c) 2006-2007, Ext JS, LLC.
28784  *
28785  * Originally Released Under LGPL - original licence link has changed is not relivant.
28786  *
28787  * Fork - LGPL
28788  * <script type="text/javascript">
28789  */
28790 /**
28791  * @class Roo.BorderLayout
28792  * @extends Roo.LayoutManager
28793  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28794  * please see: <br><br>
28795  * <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>
28796  * <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>
28797  * Example:
28798  <pre><code>
28799  var layout = new Roo.BorderLayout(document.body, {
28800     north: {
28801         initialSize: 25,
28802         titlebar: false
28803     },
28804     west: {
28805         split:true,
28806         initialSize: 200,
28807         minSize: 175,
28808         maxSize: 400,
28809         titlebar: true,
28810         collapsible: true
28811     },
28812     east: {
28813         split:true,
28814         initialSize: 202,
28815         minSize: 175,
28816         maxSize: 400,
28817         titlebar: true,
28818         collapsible: true
28819     },
28820     south: {
28821         split:true,
28822         initialSize: 100,
28823         minSize: 100,
28824         maxSize: 200,
28825         titlebar: true,
28826         collapsible: true
28827     },
28828     center: {
28829         titlebar: true,
28830         autoScroll:true,
28831         resizeTabs: true,
28832         minTabWidth: 50,
28833         preferredTabWidth: 150
28834     }
28835 });
28836
28837 // shorthand
28838 var CP = Roo.ContentPanel;
28839
28840 layout.beginUpdate();
28841 layout.add("north", new CP("north", "North"));
28842 layout.add("south", new CP("south", {title: "South", closable: true}));
28843 layout.add("west", new CP("west", {title: "West"}));
28844 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28845 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28846 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28847 layout.getRegion("center").showPanel("center1");
28848 layout.endUpdate();
28849 </code></pre>
28850
28851 <b>The container the layout is rendered into can be either the body element or any other element.
28852 If it is not the body element, the container needs to either be an absolute positioned element,
28853 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28854 the container size if it is not the body element.</b>
28855
28856 * @constructor
28857 * Create a new BorderLayout
28858 * @param {String/HTMLElement/Element} container The container this layout is bound to
28859 * @param {Object} config Configuration options
28860  */
28861 Roo.BorderLayout = function(container, config){
28862     config = config || {};
28863     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28864     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28865     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28866         var target = this.factory.validRegions[i];
28867         if(config[target]){
28868             this.addRegion(target, config[target]);
28869         }
28870     }
28871 };
28872
28873 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28874     /**
28875      * Creates and adds a new region if it doesn't already exist.
28876      * @param {String} target The target region key (north, south, east, west or center).
28877      * @param {Object} config The regions config object
28878      * @return {BorderLayoutRegion} The new region
28879      */
28880     addRegion : function(target, config){
28881         if(!this.regions[target]){
28882             var r = this.factory.create(target, this, config);
28883             this.bindRegion(target, r);
28884         }
28885         return this.regions[target];
28886     },
28887
28888     // private (kinda)
28889     bindRegion : function(name, r){
28890         this.regions[name] = r;
28891         r.on("visibilitychange", this.layout, this);
28892         r.on("paneladded", this.layout, this);
28893         r.on("panelremoved", this.layout, this);
28894         r.on("invalidated", this.layout, this);
28895         r.on("resized", this.onRegionResized, this);
28896         r.on("collapsed", this.onRegionCollapsed, this);
28897         r.on("expanded", this.onRegionExpanded, this);
28898     },
28899
28900     /**
28901      * Performs a layout update.
28902      */
28903     layout : function(){
28904         if(this.updating) return;
28905         var size = this.getViewSize();
28906         var w = size.width;
28907         var h = size.height;
28908         var centerW = w;
28909         var centerH = h;
28910         var centerY = 0;
28911         var centerX = 0;
28912         //var x = 0, y = 0;
28913
28914         var rs = this.regions;
28915         var north = rs["north"];
28916         var south = rs["south"]; 
28917         var west = rs["west"];
28918         var east = rs["east"];
28919         var center = rs["center"];
28920         //if(this.hideOnLayout){ // not supported anymore
28921             //c.el.setStyle("display", "none");
28922         //}
28923         if(north && north.isVisible()){
28924             var b = north.getBox();
28925             var m = north.getMargins();
28926             b.width = w - (m.left+m.right);
28927             b.x = m.left;
28928             b.y = m.top;
28929             centerY = b.height + b.y + m.bottom;
28930             centerH -= centerY;
28931             north.updateBox(this.safeBox(b));
28932         }
28933         if(south && south.isVisible()){
28934             var b = south.getBox();
28935             var m = south.getMargins();
28936             b.width = w - (m.left+m.right);
28937             b.x = m.left;
28938             var totalHeight = (b.height + m.top + m.bottom);
28939             b.y = h - totalHeight + m.top;
28940             centerH -= totalHeight;
28941             south.updateBox(this.safeBox(b));
28942         }
28943         if(west && west.isVisible()){
28944             var b = west.getBox();
28945             var m = west.getMargins();
28946             b.height = centerH - (m.top+m.bottom);
28947             b.x = m.left;
28948             b.y = centerY + m.top;
28949             var totalWidth = (b.width + m.left + m.right);
28950             centerX += totalWidth;
28951             centerW -= totalWidth;
28952             west.updateBox(this.safeBox(b));
28953         }
28954         if(east && east.isVisible()){
28955             var b = east.getBox();
28956             var m = east.getMargins();
28957             b.height = centerH - (m.top+m.bottom);
28958             var totalWidth = (b.width + m.left + m.right);
28959             b.x = w - totalWidth + m.left;
28960             b.y = centerY + m.top;
28961             centerW -= totalWidth;
28962             east.updateBox(this.safeBox(b));
28963         }
28964         if(center){
28965             var m = center.getMargins();
28966             var centerBox = {
28967                 x: centerX + m.left,
28968                 y: centerY + m.top,
28969                 width: centerW - (m.left+m.right),
28970                 height: centerH - (m.top+m.bottom)
28971             };
28972             //if(this.hideOnLayout){
28973                 //center.el.setStyle("display", "block");
28974             //}
28975             center.updateBox(this.safeBox(centerBox));
28976         }
28977         this.el.repaint();
28978         this.fireEvent("layout", this);
28979     },
28980
28981     // private
28982     safeBox : function(box){
28983         box.width = Math.max(0, box.width);
28984         box.height = Math.max(0, box.height);
28985         return box;
28986     },
28987
28988     /**
28989      * Adds a ContentPanel (or subclass) to this layout.
28990      * @param {String} target The target region key (north, south, east, west or center).
28991      * @param {Roo.ContentPanel} panel The panel to add
28992      * @return {Roo.ContentPanel} The added panel
28993      */
28994     add : function(target, panel){
28995          
28996         target = target.toLowerCase();
28997         return this.regions[target].add(panel);
28998     },
28999
29000     /**
29001      * Remove a ContentPanel (or subclass) to this layout.
29002      * @param {String} target The target region key (north, south, east, west or center).
29003      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29004      * @return {Roo.ContentPanel} The removed panel
29005      */
29006     remove : function(target, panel){
29007         target = target.toLowerCase();
29008         return this.regions[target].remove(panel);
29009     },
29010
29011     /**
29012      * Searches all regions for a panel with the specified id
29013      * @param {String} panelId
29014      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29015      */
29016     findPanel : function(panelId){
29017         var rs = this.regions;
29018         for(var target in rs){
29019             if(typeof rs[target] != "function"){
29020                 var p = rs[target].getPanel(panelId);
29021                 if(p){
29022                     return p;
29023                 }
29024             }
29025         }
29026         return null;
29027     },
29028
29029     /**
29030      * Searches all regions for a panel with the specified id and activates (shows) it.
29031      * @param {String/ContentPanel} panelId The panels id or the panel itself
29032      * @return {Roo.ContentPanel} The shown panel or null
29033      */
29034     showPanel : function(panelId) {
29035       var rs = this.regions;
29036       for(var target in rs){
29037          var r = rs[target];
29038          if(typeof r != "function"){
29039             if(r.hasPanel(panelId)){
29040                return r.showPanel(panelId);
29041             }
29042          }
29043       }
29044       return null;
29045    },
29046
29047    /**
29048      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29049      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29050      */
29051     restoreState : function(provider){
29052         if(!provider){
29053             provider = Roo.state.Manager;
29054         }
29055         var sm = new Roo.LayoutStateManager();
29056         sm.init(this, provider);
29057     },
29058
29059     /**
29060      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29061      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29062      * a valid ContentPanel config object.  Example:
29063      * <pre><code>
29064 // Create the main layout
29065 var layout = new Roo.BorderLayout('main-ct', {
29066     west: {
29067         split:true,
29068         minSize: 175,
29069         titlebar: true
29070     },
29071     center: {
29072         title:'Components'
29073     }
29074 }, 'main-ct');
29075
29076 // Create and add multiple ContentPanels at once via configs
29077 layout.batchAdd({
29078    west: {
29079        id: 'source-files',
29080        autoCreate:true,
29081        title:'Ext Source Files',
29082        autoScroll:true,
29083        fitToFrame:true
29084    },
29085    center : {
29086        el: cview,
29087        autoScroll:true,
29088        fitToFrame:true,
29089        toolbar: tb,
29090        resizeEl:'cbody'
29091    }
29092 });
29093 </code></pre>
29094      * @param {Object} regions An object containing ContentPanel configs by region name
29095      */
29096     batchAdd : function(regions){
29097         this.beginUpdate();
29098         for(var rname in regions){
29099             var lr = this.regions[rname];
29100             if(lr){
29101                 this.addTypedPanels(lr, regions[rname]);
29102             }
29103         }
29104         this.endUpdate();
29105     },
29106
29107     // private
29108     addTypedPanels : function(lr, ps){
29109         if(typeof ps == 'string'){
29110             lr.add(new Roo.ContentPanel(ps));
29111         }
29112         else if(ps instanceof Array){
29113             for(var i =0, len = ps.length; i < len; i++){
29114                 this.addTypedPanels(lr, ps[i]);
29115             }
29116         }
29117         else if(!ps.events){ // raw config?
29118             var el = ps.el;
29119             delete ps.el; // prevent conflict
29120             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29121         }
29122         else {  // panel object assumed!
29123             lr.add(ps);
29124         }
29125     },
29126     /**
29127      * Adds a xtype elements to the layout.
29128      * <pre><code>
29129
29130 layout.addxtype({
29131        xtype : 'ContentPanel',
29132        region: 'west',
29133        items: [ .... ]
29134    }
29135 );
29136
29137 layout.addxtype({
29138         xtype : 'NestedLayoutPanel',
29139         region: 'west',
29140         layout: {
29141            center: { },
29142            west: { }   
29143         },
29144         items : [ ... list of content panels or nested layout panels.. ]
29145    }
29146 );
29147 </code></pre>
29148      * @param {Object} cfg Xtype definition of item to add.
29149      */
29150     addxtype : function(cfg)
29151     {
29152         // basically accepts a pannel...
29153         // can accept a layout region..!?!?
29154        // console.log('BorderLayout add ' + cfg.xtype)
29155         
29156         if (!cfg.xtype.match(/Panel$/)) {
29157             return false;
29158         }
29159         var ret = false;
29160         var region = cfg.region;
29161         delete cfg.region;
29162         
29163           
29164         var xitems = [];
29165         if (cfg.items) {
29166             xitems = cfg.items;
29167             delete cfg.items;
29168         }
29169         
29170         
29171         switch(cfg.xtype) 
29172         {
29173             case 'ContentPanel':  // ContentPanel (el, cfg)
29174             case 'ScrollPanel':  // ContentPanel (el, cfg)
29175                 if(cfg.autoCreate) {
29176                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29177                 } else {
29178                     var el = this.el.createChild();
29179                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29180                 }
29181                 
29182                 this.add(region, ret);
29183                 break;
29184             
29185             
29186             case 'TreePanel': // our new panel!
29187                 cfg.el = this.el.createChild();
29188                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29189                 this.add(region, ret);
29190                 break;
29191             
29192             case 'NestedLayoutPanel': 
29193                 // create a new Layout (which is  a Border Layout...
29194                 var el = this.el.createChild();
29195                 var clayout = cfg.layout;
29196                 delete cfg.layout;
29197                 clayout.items   = clayout.items  || [];
29198                 // replace this exitems with the clayout ones..
29199                 xitems = clayout.items;
29200                  
29201                 
29202                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29203                     cfg.background = false;
29204                 }
29205                 var layout = new Roo.BorderLayout(el, clayout);
29206                 
29207                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29208                 //console.log('adding nested layout panel '  + cfg.toSource());
29209                 this.add(region, ret);
29210                 
29211                 break;
29212                 
29213             case 'GridPanel': 
29214             
29215                 // needs grid and region
29216                 
29217                 //var el = this.getRegion(region).el.createChild();
29218                 var el = this.el.createChild();
29219                 // create the grid first...
29220                 
29221                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29222                 delete cfg.grid;
29223                 if (region == 'center' && this.active ) {
29224                     cfg.background = false;
29225                 }
29226                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29227                 
29228                 this.add(region, ret);
29229                 if (cfg.background) {
29230                     ret.on('activate', function(gp) {
29231                         if (!gp.grid.rendered) {
29232                             gp.grid.render();
29233                         }
29234                     });
29235                 } else {
29236                     grid.render();
29237                 }
29238                 break;
29239            
29240                
29241                 
29242                 
29243             default: 
29244                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29245                 return;
29246              // GridPanel (grid, cfg)
29247             
29248         }
29249         this.beginUpdate();
29250         // add children..
29251         Roo.each(xitems, function(i)  {
29252             ret.addxtype(i);
29253         });
29254         this.endUpdate();
29255         return ret;
29256         
29257     }
29258 });
29259
29260 /**
29261  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29262  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29263  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29264  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29265  * <pre><code>
29266 // shorthand
29267 var CP = Roo.ContentPanel;
29268
29269 var layout = Roo.BorderLayout.create({
29270     north: {
29271         initialSize: 25,
29272         titlebar: false,
29273         panels: [new CP("north", "North")]
29274     },
29275     west: {
29276         split:true,
29277         initialSize: 200,
29278         minSize: 175,
29279         maxSize: 400,
29280         titlebar: true,
29281         collapsible: true,
29282         panels: [new CP("west", {title: "West"})]
29283     },
29284     east: {
29285         split:true,
29286         initialSize: 202,
29287         minSize: 175,
29288         maxSize: 400,
29289         titlebar: true,
29290         collapsible: true,
29291         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29292     },
29293     south: {
29294         split:true,
29295         initialSize: 100,
29296         minSize: 100,
29297         maxSize: 200,
29298         titlebar: true,
29299         collapsible: true,
29300         panels: [new CP("south", {title: "South", closable: true})]
29301     },
29302     center: {
29303         titlebar: true,
29304         autoScroll:true,
29305         resizeTabs: true,
29306         minTabWidth: 50,
29307         preferredTabWidth: 150,
29308         panels: [
29309             new CP("center1", {title: "Close Me", closable: true}),
29310             new CP("center2", {title: "Center Panel", closable: false})
29311         ]
29312     }
29313 }, document.body);
29314
29315 layout.getRegion("center").showPanel("center1");
29316 </code></pre>
29317  * @param config
29318  * @param targetEl
29319  */
29320 Roo.BorderLayout.create = function(config, targetEl){
29321     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29322     layout.beginUpdate();
29323     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29324     for(var j = 0, jlen = regions.length; j < jlen; j++){
29325         var lr = regions[j];
29326         if(layout.regions[lr] && config[lr].panels){
29327             var r = layout.regions[lr];
29328             var ps = config[lr].panels;
29329             layout.addTypedPanels(r, ps);
29330         }
29331     }
29332     layout.endUpdate();
29333     return layout;
29334 };
29335
29336 // private
29337 Roo.BorderLayout.RegionFactory = {
29338     // private
29339     validRegions : ["north","south","east","west","center"],
29340
29341     // private
29342     create : function(target, mgr, config){
29343         target = target.toLowerCase();
29344         if(config.lightweight || config.basic){
29345             return new Roo.BasicLayoutRegion(mgr, config, target);
29346         }
29347         switch(target){
29348             case "north":
29349                 return new Roo.NorthLayoutRegion(mgr, config);
29350             case "south":
29351                 return new Roo.SouthLayoutRegion(mgr, config);
29352             case "east":
29353                 return new Roo.EastLayoutRegion(mgr, config);
29354             case "west":
29355                 return new Roo.WestLayoutRegion(mgr, config);
29356             case "center":
29357                 return new Roo.CenterLayoutRegion(mgr, config);
29358         }
29359         throw 'Layout region "'+target+'" not supported.';
29360     }
29361 };/*
29362  * Based on:
29363  * Ext JS Library 1.1.1
29364  * Copyright(c) 2006-2007, Ext JS, LLC.
29365  *
29366  * Originally Released Under LGPL - original licence link has changed is not relivant.
29367  *
29368  * Fork - LGPL
29369  * <script type="text/javascript">
29370  */
29371  
29372 /**
29373  * @class Roo.BasicLayoutRegion
29374  * @extends Roo.util.Observable
29375  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29376  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29377  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29378  */
29379 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29380     this.mgr = mgr;
29381     this.position  = pos;
29382     this.events = {
29383         /**
29384          * @scope Roo.BasicLayoutRegion
29385          */
29386         
29387         /**
29388          * @event beforeremove
29389          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29390          * @param {Roo.LayoutRegion} this
29391          * @param {Roo.ContentPanel} panel The panel
29392          * @param {Object} e The cancel event object
29393          */
29394         "beforeremove" : true,
29395         /**
29396          * @event invalidated
29397          * Fires when the layout for this region is changed.
29398          * @param {Roo.LayoutRegion} this
29399          */
29400         "invalidated" : true,
29401         /**
29402          * @event visibilitychange
29403          * Fires when this region is shown or hidden 
29404          * @param {Roo.LayoutRegion} this
29405          * @param {Boolean} visibility true or false
29406          */
29407         "visibilitychange" : true,
29408         /**
29409          * @event paneladded
29410          * Fires when a panel is added. 
29411          * @param {Roo.LayoutRegion} this
29412          * @param {Roo.ContentPanel} panel The panel
29413          */
29414         "paneladded" : true,
29415         /**
29416          * @event panelremoved
29417          * Fires when a panel is removed. 
29418          * @param {Roo.LayoutRegion} this
29419          * @param {Roo.ContentPanel} panel The panel
29420          */
29421         "panelremoved" : true,
29422         /**
29423          * @event collapsed
29424          * Fires when this region is collapsed.
29425          * @param {Roo.LayoutRegion} this
29426          */
29427         "collapsed" : true,
29428         /**
29429          * @event expanded
29430          * Fires when this region is expanded.
29431          * @param {Roo.LayoutRegion} this
29432          */
29433         "expanded" : true,
29434         /**
29435          * @event slideshow
29436          * Fires when this region is slid into view.
29437          * @param {Roo.LayoutRegion} this
29438          */
29439         "slideshow" : true,
29440         /**
29441          * @event slidehide
29442          * Fires when this region slides out of view. 
29443          * @param {Roo.LayoutRegion} this
29444          */
29445         "slidehide" : true,
29446         /**
29447          * @event panelactivated
29448          * Fires when a panel is activated. 
29449          * @param {Roo.LayoutRegion} this
29450          * @param {Roo.ContentPanel} panel The activated panel
29451          */
29452         "panelactivated" : true,
29453         /**
29454          * @event resized
29455          * Fires when the user resizes this region. 
29456          * @param {Roo.LayoutRegion} this
29457          * @param {Number} newSize The new size (width for east/west, height for north/south)
29458          */
29459         "resized" : true
29460     };
29461     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29462     this.panels = new Roo.util.MixedCollection();
29463     this.panels.getKey = this.getPanelId.createDelegate(this);
29464     this.box = null;
29465     this.activePanel = null;
29466     // ensure listeners are added...
29467     
29468     if (config.listeners || config.events) {
29469         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29470             listeners : config.listeners || {},
29471             events : config.events || {}
29472         });
29473     }
29474     
29475     if(skipConfig !== true){
29476         this.applyConfig(config);
29477     }
29478 };
29479
29480 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29481     getPanelId : function(p){
29482         return p.getId();
29483     },
29484     
29485     applyConfig : function(config){
29486         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29487         this.config = config;
29488         
29489     },
29490     
29491     /**
29492      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29493      * the width, for horizontal (north, south) the height.
29494      * @param {Number} newSize The new width or height
29495      */
29496     resizeTo : function(newSize){
29497         var el = this.el ? this.el :
29498                  (this.activePanel ? this.activePanel.getEl() : null);
29499         if(el){
29500             switch(this.position){
29501                 case "east":
29502                 case "west":
29503                     el.setWidth(newSize);
29504                     this.fireEvent("resized", this, newSize);
29505                 break;
29506                 case "north":
29507                 case "south":
29508                     el.setHeight(newSize);
29509                     this.fireEvent("resized", this, newSize);
29510                 break;                
29511             }
29512         }
29513     },
29514     
29515     getBox : function(){
29516         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29517     },
29518     
29519     getMargins : function(){
29520         return this.margins;
29521     },
29522     
29523     updateBox : function(box){
29524         this.box = box;
29525         var el = this.activePanel.getEl();
29526         el.dom.style.left = box.x + "px";
29527         el.dom.style.top = box.y + "px";
29528         this.activePanel.setSize(box.width, box.height);
29529     },
29530     
29531     /**
29532      * Returns the container element for this region.
29533      * @return {Roo.Element}
29534      */
29535     getEl : function(){
29536         return this.activePanel;
29537     },
29538     
29539     /**
29540      * Returns true if this region is currently visible.
29541      * @return {Boolean}
29542      */
29543     isVisible : function(){
29544         return this.activePanel ? true : false;
29545     },
29546     
29547     setActivePanel : function(panel){
29548         panel = this.getPanel(panel);
29549         if(this.activePanel && this.activePanel != panel){
29550             this.activePanel.setActiveState(false);
29551             this.activePanel.getEl().setLeftTop(-10000,-10000);
29552         }
29553         this.activePanel = panel;
29554         panel.setActiveState(true);
29555         if(this.box){
29556             panel.setSize(this.box.width, this.box.height);
29557         }
29558         this.fireEvent("panelactivated", this, panel);
29559         this.fireEvent("invalidated");
29560     },
29561     
29562     /**
29563      * Show the specified panel.
29564      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29565      * @return {Roo.ContentPanel} The shown panel or null
29566      */
29567     showPanel : function(panel){
29568         if(panel = this.getPanel(panel)){
29569             this.setActivePanel(panel);
29570         }
29571         return panel;
29572     },
29573     
29574     /**
29575      * Get the active panel for this region.
29576      * @return {Roo.ContentPanel} The active panel or null
29577      */
29578     getActivePanel : function(){
29579         return this.activePanel;
29580     },
29581     
29582     /**
29583      * Add the passed ContentPanel(s)
29584      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29585      * @return {Roo.ContentPanel} The panel added (if only one was added)
29586      */
29587     add : function(panel){
29588         if(arguments.length > 1){
29589             for(var i = 0, len = arguments.length; i < len; i++) {
29590                 this.add(arguments[i]);
29591             }
29592             return null;
29593         }
29594         if(this.hasPanel(panel)){
29595             this.showPanel(panel);
29596             return panel;
29597         }
29598         var el = panel.getEl();
29599         if(el.dom.parentNode != this.mgr.el.dom){
29600             this.mgr.el.dom.appendChild(el.dom);
29601         }
29602         if(panel.setRegion){
29603             panel.setRegion(this);
29604         }
29605         this.panels.add(panel);
29606         el.setStyle("position", "absolute");
29607         if(!panel.background){
29608             this.setActivePanel(panel);
29609             if(this.config.initialSize && this.panels.getCount()==1){
29610                 this.resizeTo(this.config.initialSize);
29611             }
29612         }
29613         this.fireEvent("paneladded", this, panel);
29614         return panel;
29615     },
29616     
29617     /**
29618      * Returns true if the panel is in this region.
29619      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29620      * @return {Boolean}
29621      */
29622     hasPanel : function(panel){
29623         if(typeof panel == "object"){ // must be panel obj
29624             panel = panel.getId();
29625         }
29626         return this.getPanel(panel) ? true : false;
29627     },
29628     
29629     /**
29630      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29631      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29632      * @param {Boolean} preservePanel Overrides the config preservePanel option
29633      * @return {Roo.ContentPanel} The panel that was removed
29634      */
29635     remove : function(panel, preservePanel){
29636         panel = this.getPanel(panel);
29637         if(!panel){
29638             return null;
29639         }
29640         var e = {};
29641         this.fireEvent("beforeremove", this, panel, e);
29642         if(e.cancel === true){
29643             return null;
29644         }
29645         var panelId = panel.getId();
29646         this.panels.removeKey(panelId);
29647         return panel;
29648     },
29649     
29650     /**
29651      * Returns the panel specified or null if it's not in this region.
29652      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29653      * @return {Roo.ContentPanel}
29654      */
29655     getPanel : function(id){
29656         if(typeof id == "object"){ // must be panel obj
29657             return id;
29658         }
29659         return this.panels.get(id);
29660     },
29661     
29662     /**
29663      * Returns this regions position (north/south/east/west/center).
29664      * @return {String} 
29665      */
29666     getPosition: function(){
29667         return this.position;    
29668     }
29669 });/*
29670  * Based on:
29671  * Ext JS Library 1.1.1
29672  * Copyright(c) 2006-2007, Ext JS, LLC.
29673  *
29674  * Originally Released Under LGPL - original licence link has changed is not relivant.
29675  *
29676  * Fork - LGPL
29677  * <script type="text/javascript">
29678  */
29679  
29680 /**
29681  * @class Roo.LayoutRegion
29682  * @extends Roo.BasicLayoutRegion
29683  * This class represents a region in a layout manager.
29684  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
29685  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
29686  * @cfg {Boolean} floatable False to disable floating (defaults to true)
29687  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29688  * @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})
29689  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
29690  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
29691  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
29692  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
29693  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
29694  * @cfg {String} title The title for the region (overrides panel titles)
29695  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
29696  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29697  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
29698  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29699  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
29700  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29701  * the space available, similar to FireFox 1.5 tabs (defaults to false)
29702  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
29703  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
29704  * @cfg {Boolean} showPin True to show a pin button
29705 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
29706 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
29707 * @cfg {Boolean} disableTabTips True to disable tab tooltips
29708 * @cfg {Number} width  For East/West panels
29709 * @cfg {Number} height For North/South panels
29710 * @cfg {Boolean} split To show the splitter
29711  */
29712 Roo.LayoutRegion = function(mgr, config, pos){
29713     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29714     var dh = Roo.DomHelper;
29715     /** This region's container element 
29716     * @type Roo.Element */
29717     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29718     /** This region's title element 
29719     * @type Roo.Element */
29720
29721     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29722         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29723         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29724     ]}, true);
29725     this.titleEl.enableDisplayMode();
29726     /** This region's title text element 
29727     * @type HTMLElement */
29728     this.titleTextEl = this.titleEl.dom.firstChild;
29729     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29730     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29731     this.closeBtn.enableDisplayMode();
29732     this.closeBtn.on("click", this.closeClicked, this);
29733     this.closeBtn.hide();
29734
29735     this.createBody(config);
29736     this.visible = true;
29737     this.collapsed = false;
29738
29739     if(config.hideWhenEmpty){
29740         this.hide();
29741         this.on("paneladded", this.validateVisibility, this);
29742         this.on("panelremoved", this.validateVisibility, this);
29743     }
29744     this.applyConfig(config);
29745 };
29746
29747 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29748
29749     createBody : function(){
29750         /** This region's body element 
29751         * @type Roo.Element */
29752         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29753     },
29754
29755     applyConfig : function(c){
29756         if(c.collapsible && this.position != "center" && !this.collapsedEl){
29757             var dh = Roo.DomHelper;
29758             if(c.titlebar !== false){
29759                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
29760                 this.collapseBtn.on("click", this.collapse, this);
29761                 this.collapseBtn.enableDisplayMode();
29762
29763                 if(c.showPin === true || this.showPin){
29764                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
29765                     this.stickBtn.enableDisplayMode();
29766                     this.stickBtn.on("click", this.expand, this);
29767                     this.stickBtn.hide();
29768                 }
29769             }
29770             /** This region's collapsed element
29771             * @type Roo.Element */
29772             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
29773                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
29774             ]}, true);
29775             if(c.floatable !== false){
29776                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
29777                this.collapsedEl.on("click", this.collapseClick, this);
29778             }
29779
29780             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
29781                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
29782                    id: "message", unselectable: "on", style:{"float":"left"}});
29783                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
29784              }
29785             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
29786             this.expandBtn.on("click", this.expand, this);
29787         }
29788         if(this.collapseBtn){
29789             this.collapseBtn.setVisible(c.collapsible == true);
29790         }
29791         this.cmargins = c.cmargins || this.cmargins ||
29792                          (this.position == "west" || this.position == "east" ?
29793                              {top: 0, left: 2, right:2, bottom: 0} :
29794                              {top: 2, left: 0, right:0, bottom: 2});
29795         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29796         this.bottomTabs = c.tabPosition != "top";
29797         this.autoScroll = c.autoScroll || false;
29798         if(this.autoScroll){
29799             this.bodyEl.setStyle("overflow", "auto");
29800         }else{
29801             this.bodyEl.setStyle("overflow", "hidden");
29802         }
29803         //if(c.titlebar !== false){
29804             if((!c.titlebar && !c.title) || c.titlebar === false){
29805                 this.titleEl.hide();
29806             }else{
29807                 this.titleEl.show();
29808                 if(c.title){
29809                     this.titleTextEl.innerHTML = c.title;
29810                 }
29811             }
29812         //}
29813         this.duration = c.duration || .30;
29814         this.slideDuration = c.slideDuration || .45;
29815         this.config = c;
29816         if(c.collapsed){
29817             this.collapse(true);
29818         }
29819         if(c.hidden){
29820             this.hide();
29821         }
29822     },
29823     /**
29824      * Returns true if this region is currently visible.
29825      * @return {Boolean}
29826      */
29827     isVisible : function(){
29828         return this.visible;
29829     },
29830
29831     /**
29832      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
29833      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
29834      */
29835     setCollapsedTitle : function(title){
29836         title = title || "&#160;";
29837         if(this.collapsedTitleTextEl){
29838             this.collapsedTitleTextEl.innerHTML = title;
29839         }
29840     },
29841
29842     getBox : function(){
29843         var b;
29844         if(!this.collapsed){
29845             b = this.el.getBox(false, true);
29846         }else{
29847             b = this.collapsedEl.getBox(false, true);
29848         }
29849         return b;
29850     },
29851
29852     getMargins : function(){
29853         return this.collapsed ? this.cmargins : this.margins;
29854     },
29855
29856     highlight : function(){
29857         this.el.addClass("x-layout-panel-dragover");
29858     },
29859
29860     unhighlight : function(){
29861         this.el.removeClass("x-layout-panel-dragover");
29862     },
29863
29864     updateBox : function(box){
29865         this.box = box;
29866         if(!this.collapsed){
29867             this.el.dom.style.left = box.x + "px";
29868             this.el.dom.style.top = box.y + "px";
29869             this.updateBody(box.width, box.height);
29870         }else{
29871             this.collapsedEl.dom.style.left = box.x + "px";
29872             this.collapsedEl.dom.style.top = box.y + "px";
29873             this.collapsedEl.setSize(box.width, box.height);
29874         }
29875         if(this.tabs){
29876             this.tabs.autoSizeTabs();
29877         }
29878     },
29879
29880     updateBody : function(w, h){
29881         if(w !== null){
29882             this.el.setWidth(w);
29883             w -= this.el.getBorderWidth("rl");
29884             if(this.config.adjustments){
29885                 w += this.config.adjustments[0];
29886             }
29887         }
29888         if(h !== null){
29889             this.el.setHeight(h);
29890             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
29891             h -= this.el.getBorderWidth("tb");
29892             if(this.config.adjustments){
29893                 h += this.config.adjustments[1];
29894             }
29895             this.bodyEl.setHeight(h);
29896             if(this.tabs){
29897                 h = this.tabs.syncHeight(h);
29898             }
29899         }
29900         if(this.panelSize){
29901             w = w !== null ? w : this.panelSize.width;
29902             h = h !== null ? h : this.panelSize.height;
29903         }
29904         if(this.activePanel){
29905             var el = this.activePanel.getEl();
29906             w = w !== null ? w : el.getWidth();
29907             h = h !== null ? h : el.getHeight();
29908             this.panelSize = {width: w, height: h};
29909             this.activePanel.setSize(w, h);
29910         }
29911         if(Roo.isIE && this.tabs){
29912             this.tabs.el.repaint();
29913         }
29914     },
29915
29916     /**
29917      * Returns the container element for this region.
29918      * @return {Roo.Element}
29919      */
29920     getEl : function(){
29921         return this.el;
29922     },
29923
29924     /**
29925      * Hides this region.
29926      */
29927     hide : function(){
29928         if(!this.collapsed){
29929             this.el.dom.style.left = "-2000px";
29930             this.el.hide();
29931         }else{
29932             this.collapsedEl.dom.style.left = "-2000px";
29933             this.collapsedEl.hide();
29934         }
29935         this.visible = false;
29936         this.fireEvent("visibilitychange", this, false);
29937     },
29938
29939     /**
29940      * Shows this region if it was previously hidden.
29941      */
29942     show : function(){
29943         if(!this.collapsed){
29944             this.el.show();
29945         }else{
29946             this.collapsedEl.show();
29947         }
29948         this.visible = true;
29949         this.fireEvent("visibilitychange", this, true);
29950     },
29951
29952     closeClicked : function(){
29953         if(this.activePanel){
29954             this.remove(this.activePanel);
29955         }
29956     },
29957
29958     collapseClick : function(e){
29959         if(this.isSlid){
29960            e.stopPropagation();
29961            this.slideIn();
29962         }else{
29963            e.stopPropagation();
29964            this.slideOut();
29965         }
29966     },
29967
29968     /**
29969      * Collapses this region.
29970      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
29971      */
29972     collapse : function(skipAnim){
29973         if(this.collapsed) return;
29974         this.collapsed = true;
29975         if(this.split){
29976             this.split.el.hide();
29977         }
29978         if(this.config.animate && skipAnim !== true){
29979             this.fireEvent("invalidated", this);
29980             this.animateCollapse();
29981         }else{
29982             this.el.setLocation(-20000,-20000);
29983             this.el.hide();
29984             this.collapsedEl.show();
29985             this.fireEvent("collapsed", this);
29986             this.fireEvent("invalidated", this);
29987         }
29988     },
29989
29990     animateCollapse : function(){
29991         // overridden
29992     },
29993
29994     /**
29995      * Expands this region if it was previously collapsed.
29996      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
29997      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
29998      */
29999     expand : function(e, skipAnim){
30000         if(e) e.stopPropagation();
30001         if(!this.collapsed || this.el.hasActiveFx()) return;
30002         if(this.isSlid){
30003             this.afterSlideIn();
30004             skipAnim = true;
30005         }
30006         this.collapsed = false;
30007         if(this.config.animate && skipAnim !== true){
30008             this.animateExpand();
30009         }else{
30010             this.el.show();
30011             if(this.split){
30012                 this.split.el.show();
30013             }
30014             this.collapsedEl.setLocation(-2000,-2000);
30015             this.collapsedEl.hide();
30016             this.fireEvent("invalidated", this);
30017             this.fireEvent("expanded", this);
30018         }
30019     },
30020
30021     animateExpand : function(){
30022         // overridden
30023     },
30024
30025     initTabs : function(){
30026         this.bodyEl.setStyle("overflow", "hidden");
30027         var ts = new Roo.TabPanel(this.bodyEl.dom, {
30028             tabPosition: this.bottomTabs ? 'bottom' : 'top',
30029             disableTooltips: this.config.disableTabTips
30030         });
30031         if(this.config.hideTabs){
30032             ts.stripWrap.setDisplayed(false);
30033         }
30034         this.tabs = ts;
30035         ts.resizeTabs = this.config.resizeTabs === true;
30036         ts.minTabWidth = this.config.minTabWidth || 40;
30037         ts.maxTabWidth = this.config.maxTabWidth || 250;
30038         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30039         ts.monitorResize = false;
30040         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30041         ts.bodyEl.addClass('x-layout-tabs-body');
30042         this.panels.each(this.initPanelAsTab, this);
30043     },
30044
30045     initPanelAsTab : function(panel){
30046         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30047                     this.config.closeOnTab && panel.isClosable());
30048         if(panel.tabTip !== undefined){
30049             ti.setTooltip(panel.tabTip);
30050         }
30051         ti.on("activate", function(){
30052               this.setActivePanel(panel);
30053         }, this);
30054         if(this.config.closeOnTab){
30055             ti.on("beforeclose", function(t, e){
30056                 e.cancel = true;
30057                 this.remove(panel);
30058             }, this);
30059         }
30060         return ti;
30061     },
30062
30063     updatePanelTitle : function(panel, title){
30064         if(this.activePanel == panel){
30065             this.updateTitle(title);
30066         }
30067         if(this.tabs){
30068             var ti = this.tabs.getTab(panel.getEl().id);
30069             ti.setText(title);
30070             if(panel.tabTip !== undefined){
30071                 ti.setTooltip(panel.tabTip);
30072             }
30073         }
30074     },
30075
30076     updateTitle : function(title){
30077         if(this.titleTextEl && !this.config.title){
30078             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30079         }
30080     },
30081
30082     setActivePanel : function(panel){
30083         panel = this.getPanel(panel);
30084         if(this.activePanel && this.activePanel != panel){
30085             this.activePanel.setActiveState(false);
30086         }
30087         this.activePanel = panel;
30088         panel.setActiveState(true);
30089         if(this.panelSize){
30090             panel.setSize(this.panelSize.width, this.panelSize.height);
30091         }
30092         if(this.closeBtn){
30093             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30094         }
30095         this.updateTitle(panel.getTitle());
30096         if(this.tabs){
30097             this.fireEvent("invalidated", this);
30098         }
30099         this.fireEvent("panelactivated", this, panel);
30100     },
30101
30102     /**
30103      * Shows the specified panel.
30104      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30105      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30106      */
30107     showPanel : function(panel){
30108         if(panel = this.getPanel(panel)){
30109             if(this.tabs){
30110                 var tab = this.tabs.getTab(panel.getEl().id);
30111                 if(tab.isHidden()){
30112                     this.tabs.unhideTab(tab.id);
30113                 }
30114                 tab.activate();
30115             }else{
30116                 this.setActivePanel(panel);
30117             }
30118         }
30119         return panel;
30120     },
30121
30122     /**
30123      * Get the active panel for this region.
30124      * @return {Roo.ContentPanel} The active panel or null
30125      */
30126     getActivePanel : function(){
30127         return this.activePanel;
30128     },
30129
30130     validateVisibility : function(){
30131         if(this.panels.getCount() < 1){
30132             this.updateTitle("&#160;");
30133             this.closeBtn.hide();
30134             this.hide();
30135         }else{
30136             if(!this.isVisible()){
30137                 this.show();
30138             }
30139         }
30140     },
30141
30142     /**
30143      * Adds the passed ContentPanel(s) to this region.
30144      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30145      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30146      */
30147     add : function(panel){
30148         if(arguments.length > 1){
30149             for(var i = 0, len = arguments.length; i < len; i++) {
30150                 this.add(arguments[i]);
30151             }
30152             return null;
30153         }
30154         if(this.hasPanel(panel)){
30155             this.showPanel(panel);
30156             return panel;
30157         }
30158         panel.setRegion(this);
30159         this.panels.add(panel);
30160         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30161             this.bodyEl.dom.appendChild(panel.getEl().dom);
30162             if(panel.background !== true){
30163                 this.setActivePanel(panel);
30164             }
30165             this.fireEvent("paneladded", this, panel);
30166             return panel;
30167         }
30168         if(!this.tabs){
30169             this.initTabs();
30170         }else{
30171             this.initPanelAsTab(panel);
30172         }
30173         if(panel.background !== true){
30174             this.tabs.activate(panel.getEl().id);
30175         }
30176         this.fireEvent("paneladded", this, panel);
30177         return panel;
30178     },
30179
30180     /**
30181      * Hides the tab for the specified panel.
30182      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30183      */
30184     hidePanel : function(panel){
30185         if(this.tabs && (panel = this.getPanel(panel))){
30186             this.tabs.hideTab(panel.getEl().id);
30187         }
30188     },
30189
30190     /**
30191      * Unhides the tab for a previously hidden panel.
30192      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30193      */
30194     unhidePanel : function(panel){
30195         if(this.tabs && (panel = this.getPanel(panel))){
30196             this.tabs.unhideTab(panel.getEl().id);
30197         }
30198     },
30199
30200     clearPanels : function(){
30201         while(this.panels.getCount() > 0){
30202              this.remove(this.panels.first());
30203         }
30204     },
30205
30206     /**
30207      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30208      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30209      * @param {Boolean} preservePanel Overrides the config preservePanel option
30210      * @return {Roo.ContentPanel} The panel that was removed
30211      */
30212     remove : function(panel, preservePanel){
30213         panel = this.getPanel(panel);
30214         if(!panel){
30215             return null;
30216         }
30217         var e = {};
30218         this.fireEvent("beforeremove", this, panel, e);
30219         if(e.cancel === true){
30220             return null;
30221         }
30222         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30223         var panelId = panel.getId();
30224         this.panels.removeKey(panelId);
30225         if(preservePanel){
30226             document.body.appendChild(panel.getEl().dom);
30227         }
30228         if(this.tabs){
30229             this.tabs.removeTab(panel.getEl().id);
30230         }else if (!preservePanel){
30231             this.bodyEl.dom.removeChild(panel.getEl().dom);
30232         }
30233         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30234             var p = this.panels.first();
30235             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30236             tempEl.appendChild(p.getEl().dom);
30237             this.bodyEl.update("");
30238             this.bodyEl.dom.appendChild(p.getEl().dom);
30239             tempEl = null;
30240             this.updateTitle(p.getTitle());
30241             this.tabs = null;
30242             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30243             this.setActivePanel(p);
30244         }
30245         panel.setRegion(null);
30246         if(this.activePanel == panel){
30247             this.activePanel = null;
30248         }
30249         if(this.config.autoDestroy !== false && preservePanel !== true){
30250             try{panel.destroy();}catch(e){}
30251         }
30252         this.fireEvent("panelremoved", this, panel);
30253         return panel;
30254     },
30255
30256     /**
30257      * Returns the TabPanel component used by this region
30258      * @return {Roo.TabPanel}
30259      */
30260     getTabs : function(){
30261         return this.tabs;
30262     },
30263
30264     createTool : function(parentEl, className){
30265         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30266             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30267         btn.addClassOnOver("x-layout-tools-button-over");
30268         return btn;
30269     }
30270 });/*
30271  * Based on:
30272  * Ext JS Library 1.1.1
30273  * Copyright(c) 2006-2007, Ext JS, LLC.
30274  *
30275  * Originally Released Under LGPL - original licence link has changed is not relivant.
30276  *
30277  * Fork - LGPL
30278  * <script type="text/javascript">
30279  */
30280  
30281
30282
30283 /**
30284  * @class Roo.SplitLayoutRegion
30285  * @extends Roo.LayoutRegion
30286  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30287  */
30288 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30289     this.cursor = cursor;
30290     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30291 };
30292
30293 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30294     splitTip : "Drag to resize.",
30295     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30296     useSplitTips : false,
30297
30298     applyConfig : function(config){
30299         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30300         if(config.split){
30301             if(!this.split){
30302                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30303                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30304                 /** The SplitBar for this region 
30305                 * @type Roo.SplitBar */
30306                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30307                 this.split.on("moved", this.onSplitMove, this);
30308                 this.split.useShim = config.useShim === true;
30309                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30310                 if(this.useSplitTips){
30311                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30312                 }
30313                 if(config.collapsible){
30314                     this.split.el.on("dblclick", this.collapse,  this);
30315                 }
30316             }
30317             if(typeof config.minSize != "undefined"){
30318                 this.split.minSize = config.minSize;
30319             }
30320             if(typeof config.maxSize != "undefined"){
30321                 this.split.maxSize = config.maxSize;
30322             }
30323             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30324                 this.hideSplitter();
30325             }
30326         }
30327     },
30328
30329     getHMaxSize : function(){
30330          var cmax = this.config.maxSize || 10000;
30331          var center = this.mgr.getRegion("center");
30332          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30333     },
30334
30335     getVMaxSize : function(){
30336          var cmax = this.config.maxSize || 10000;
30337          var center = this.mgr.getRegion("center");
30338          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30339     },
30340
30341     onSplitMove : function(split, newSize){
30342         this.fireEvent("resized", this, newSize);
30343     },
30344     
30345     /** 
30346      * Returns the {@link Roo.SplitBar} for this region.
30347      * @return {Roo.SplitBar}
30348      */
30349     getSplitBar : function(){
30350         return this.split;
30351     },
30352     
30353     hide : function(){
30354         this.hideSplitter();
30355         Roo.SplitLayoutRegion.superclass.hide.call(this);
30356     },
30357
30358     hideSplitter : function(){
30359         if(this.split){
30360             this.split.el.setLocation(-2000,-2000);
30361             this.split.el.hide();
30362         }
30363     },
30364
30365     show : function(){
30366         if(this.split){
30367             this.split.el.show();
30368         }
30369         Roo.SplitLayoutRegion.superclass.show.call(this);
30370     },
30371     
30372     beforeSlide: function(){
30373         if(Roo.isGecko){// firefox overflow auto bug workaround
30374             this.bodyEl.clip();
30375             if(this.tabs) this.tabs.bodyEl.clip();
30376             if(this.activePanel){
30377                 this.activePanel.getEl().clip();
30378                 
30379                 if(this.activePanel.beforeSlide){
30380                     this.activePanel.beforeSlide();
30381                 }
30382             }
30383         }
30384     },
30385     
30386     afterSlide : function(){
30387         if(Roo.isGecko){// firefox overflow auto bug workaround
30388             this.bodyEl.unclip();
30389             if(this.tabs) this.tabs.bodyEl.unclip();
30390             if(this.activePanel){
30391                 this.activePanel.getEl().unclip();
30392                 if(this.activePanel.afterSlide){
30393                     this.activePanel.afterSlide();
30394                 }
30395             }
30396         }
30397     },
30398
30399     initAutoHide : function(){
30400         if(this.autoHide !== false){
30401             if(!this.autoHideHd){
30402                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30403                 this.autoHideHd = {
30404                     "mouseout": function(e){
30405                         if(!e.within(this.el, true)){
30406                             st.delay(500);
30407                         }
30408                     },
30409                     "mouseover" : function(e){
30410                         st.cancel();
30411                     },
30412                     scope : this
30413                 };
30414             }
30415             this.el.on(this.autoHideHd);
30416         }
30417     },
30418
30419     clearAutoHide : function(){
30420         if(this.autoHide !== false){
30421             this.el.un("mouseout", this.autoHideHd.mouseout);
30422             this.el.un("mouseover", this.autoHideHd.mouseover);
30423         }
30424     },
30425
30426     clearMonitor : function(){
30427         Roo.get(document).un("click", this.slideInIf, this);
30428     },
30429
30430     // these names are backwards but not changed for compat
30431     slideOut : function(){
30432         if(this.isSlid || this.el.hasActiveFx()){
30433             return;
30434         }
30435         this.isSlid = true;
30436         if(this.collapseBtn){
30437             this.collapseBtn.hide();
30438         }
30439         this.closeBtnState = this.closeBtn.getStyle('display');
30440         this.closeBtn.hide();
30441         if(this.stickBtn){
30442             this.stickBtn.show();
30443         }
30444         this.el.show();
30445         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30446         this.beforeSlide();
30447         this.el.setStyle("z-index", 10001);
30448         this.el.slideIn(this.getSlideAnchor(), {
30449             callback: function(){
30450                 this.afterSlide();
30451                 this.initAutoHide();
30452                 Roo.get(document).on("click", this.slideInIf, this);
30453                 this.fireEvent("slideshow", this);
30454             },
30455             scope: this,
30456             block: true
30457         });
30458     },
30459
30460     afterSlideIn : function(){
30461         this.clearAutoHide();
30462         this.isSlid = false;
30463         this.clearMonitor();
30464         this.el.setStyle("z-index", "");
30465         if(this.collapseBtn){
30466             this.collapseBtn.show();
30467         }
30468         this.closeBtn.setStyle('display', this.closeBtnState);
30469         if(this.stickBtn){
30470             this.stickBtn.hide();
30471         }
30472         this.fireEvent("slidehide", this);
30473     },
30474
30475     slideIn : function(cb){
30476         if(!this.isSlid || this.el.hasActiveFx()){
30477             Roo.callback(cb);
30478             return;
30479         }
30480         this.isSlid = false;
30481         this.beforeSlide();
30482         this.el.slideOut(this.getSlideAnchor(), {
30483             callback: function(){
30484                 this.el.setLeftTop(-10000, -10000);
30485                 this.afterSlide();
30486                 this.afterSlideIn();
30487                 Roo.callback(cb);
30488             },
30489             scope: this,
30490             block: true
30491         });
30492     },
30493     
30494     slideInIf : function(e){
30495         if(!e.within(this.el)){
30496             this.slideIn();
30497         }
30498     },
30499
30500     animateCollapse : function(){
30501         this.beforeSlide();
30502         this.el.setStyle("z-index", 20000);
30503         var anchor = this.getSlideAnchor();
30504         this.el.slideOut(anchor, {
30505             callback : function(){
30506                 this.el.setStyle("z-index", "");
30507                 this.collapsedEl.slideIn(anchor, {duration:.3});
30508                 this.afterSlide();
30509                 this.el.setLocation(-10000,-10000);
30510                 this.el.hide();
30511                 this.fireEvent("collapsed", this);
30512             },
30513             scope: this,
30514             block: true
30515         });
30516     },
30517
30518     animateExpand : function(){
30519         this.beforeSlide();
30520         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30521         this.el.setStyle("z-index", 20000);
30522         this.collapsedEl.hide({
30523             duration:.1
30524         });
30525         this.el.slideIn(this.getSlideAnchor(), {
30526             callback : function(){
30527                 this.el.setStyle("z-index", "");
30528                 this.afterSlide();
30529                 if(this.split){
30530                     this.split.el.show();
30531                 }
30532                 this.fireEvent("invalidated", this);
30533                 this.fireEvent("expanded", this);
30534             },
30535             scope: this,
30536             block: true
30537         });
30538     },
30539
30540     anchors : {
30541         "west" : "left",
30542         "east" : "right",
30543         "north" : "top",
30544         "south" : "bottom"
30545     },
30546
30547     sanchors : {
30548         "west" : "l",
30549         "east" : "r",
30550         "north" : "t",
30551         "south" : "b"
30552     },
30553
30554     canchors : {
30555         "west" : "tl-tr",
30556         "east" : "tr-tl",
30557         "north" : "tl-bl",
30558         "south" : "bl-tl"
30559     },
30560
30561     getAnchor : function(){
30562         return this.anchors[this.position];
30563     },
30564
30565     getCollapseAnchor : function(){
30566         return this.canchors[this.position];
30567     },
30568
30569     getSlideAnchor : function(){
30570         return this.sanchors[this.position];
30571     },
30572
30573     getAlignAdj : function(){
30574         var cm = this.cmargins;
30575         switch(this.position){
30576             case "west":
30577                 return [0, 0];
30578             break;
30579             case "east":
30580                 return [0, 0];
30581             break;
30582             case "north":
30583                 return [0, 0];
30584             break;
30585             case "south":
30586                 return [0, 0];
30587             break;
30588         }
30589     },
30590
30591     getExpandAdj : function(){
30592         var c = this.collapsedEl, cm = this.cmargins;
30593         switch(this.position){
30594             case "west":
30595                 return [-(cm.right+c.getWidth()+cm.left), 0];
30596             break;
30597             case "east":
30598                 return [cm.right+c.getWidth()+cm.left, 0];
30599             break;
30600             case "north":
30601                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30602             break;
30603             case "south":
30604                 return [0, cm.top+cm.bottom+c.getHeight()];
30605             break;
30606         }
30607     }
30608 });/*
30609  * Based on:
30610  * Ext JS Library 1.1.1
30611  * Copyright(c) 2006-2007, Ext JS, LLC.
30612  *
30613  * Originally Released Under LGPL - original licence link has changed is not relivant.
30614  *
30615  * Fork - LGPL
30616  * <script type="text/javascript">
30617  */
30618 /*
30619  * These classes are private internal classes
30620  */
30621 Roo.CenterLayoutRegion = function(mgr, config){
30622     Roo.LayoutRegion.call(this, mgr, config, "center");
30623     this.visible = true;
30624     this.minWidth = config.minWidth || 20;
30625     this.minHeight = config.minHeight || 20;
30626 };
30627
30628 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30629     hide : function(){
30630         // center panel can't be hidden
30631     },
30632     
30633     show : function(){
30634         // center panel can't be hidden
30635     },
30636     
30637     getMinWidth: function(){
30638         return this.minWidth;
30639     },
30640     
30641     getMinHeight: function(){
30642         return this.minHeight;
30643     }
30644 });
30645
30646
30647 Roo.NorthLayoutRegion = function(mgr, config){
30648     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30649     if(this.split){
30650         this.split.placement = Roo.SplitBar.TOP;
30651         this.split.orientation = Roo.SplitBar.VERTICAL;
30652         this.split.el.addClass("x-layout-split-v");
30653     }
30654     var size = config.initialSize || config.height;
30655     if(typeof size != "undefined"){
30656         this.el.setHeight(size);
30657     }
30658 };
30659 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30660     orientation: Roo.SplitBar.VERTICAL,
30661     getBox : function(){
30662         if(this.collapsed){
30663             return this.collapsedEl.getBox();
30664         }
30665         var box = this.el.getBox();
30666         if(this.split){
30667             box.height += this.split.el.getHeight();
30668         }
30669         return box;
30670     },
30671     
30672     updateBox : function(box){
30673         if(this.split && !this.collapsed){
30674             box.height -= this.split.el.getHeight();
30675             this.split.el.setLeft(box.x);
30676             this.split.el.setTop(box.y+box.height);
30677             this.split.el.setWidth(box.width);
30678         }
30679         if(this.collapsed){
30680             this.updateBody(box.width, null);
30681         }
30682         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30683     }
30684 });
30685
30686 Roo.SouthLayoutRegion = function(mgr, config){
30687     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30688     if(this.split){
30689         this.split.placement = Roo.SplitBar.BOTTOM;
30690         this.split.orientation = Roo.SplitBar.VERTICAL;
30691         this.split.el.addClass("x-layout-split-v");
30692     }
30693     var size = config.initialSize || config.height;
30694     if(typeof size != "undefined"){
30695         this.el.setHeight(size);
30696     }
30697 };
30698 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30699     orientation: Roo.SplitBar.VERTICAL,
30700     getBox : function(){
30701         if(this.collapsed){
30702             return this.collapsedEl.getBox();
30703         }
30704         var box = this.el.getBox();
30705         if(this.split){
30706             var sh = this.split.el.getHeight();
30707             box.height += sh;
30708             box.y -= sh;
30709         }
30710         return box;
30711     },
30712     
30713     updateBox : function(box){
30714         if(this.split && !this.collapsed){
30715             var sh = this.split.el.getHeight();
30716             box.height -= sh;
30717             box.y += sh;
30718             this.split.el.setLeft(box.x);
30719             this.split.el.setTop(box.y-sh);
30720             this.split.el.setWidth(box.width);
30721         }
30722         if(this.collapsed){
30723             this.updateBody(box.width, null);
30724         }
30725         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30726     }
30727 });
30728
30729 Roo.EastLayoutRegion = function(mgr, config){
30730     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30731     if(this.split){
30732         this.split.placement = Roo.SplitBar.RIGHT;
30733         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30734         this.split.el.addClass("x-layout-split-h");
30735     }
30736     var size = config.initialSize || config.width;
30737     if(typeof size != "undefined"){
30738         this.el.setWidth(size);
30739     }
30740 };
30741 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
30742     orientation: Roo.SplitBar.HORIZONTAL,
30743     getBox : function(){
30744         if(this.collapsed){
30745             return this.collapsedEl.getBox();
30746         }
30747         var box = this.el.getBox();
30748         if(this.split){
30749             var sw = this.split.el.getWidth();
30750             box.width += sw;
30751             box.x -= sw;
30752         }
30753         return box;
30754     },
30755
30756     updateBox : function(box){
30757         if(this.split && !this.collapsed){
30758             var sw = this.split.el.getWidth();
30759             box.width -= sw;
30760             this.split.el.setLeft(box.x);
30761             this.split.el.setTop(box.y);
30762             this.split.el.setHeight(box.height);
30763             box.x += sw;
30764         }
30765         if(this.collapsed){
30766             this.updateBody(null, box.height);
30767         }
30768         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30769     }
30770 });
30771
30772 Roo.WestLayoutRegion = function(mgr, config){
30773     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
30774     if(this.split){
30775         this.split.placement = Roo.SplitBar.LEFT;
30776         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30777         this.split.el.addClass("x-layout-split-h");
30778     }
30779     var size = config.initialSize || config.width;
30780     if(typeof size != "undefined"){
30781         this.el.setWidth(size);
30782     }
30783 };
30784 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
30785     orientation: Roo.SplitBar.HORIZONTAL,
30786     getBox : function(){
30787         if(this.collapsed){
30788             return this.collapsedEl.getBox();
30789         }
30790         var box = this.el.getBox();
30791         if(this.split){
30792             box.width += this.split.el.getWidth();
30793         }
30794         return box;
30795     },
30796     
30797     updateBox : function(box){
30798         if(this.split && !this.collapsed){
30799             var sw = this.split.el.getWidth();
30800             box.width -= sw;
30801             this.split.el.setLeft(box.x+box.width);
30802             this.split.el.setTop(box.y);
30803             this.split.el.setHeight(box.height);
30804         }
30805         if(this.collapsed){
30806             this.updateBody(null, box.height);
30807         }
30808         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30809     }
30810 });
30811 /*
30812  * Based on:
30813  * Ext JS Library 1.1.1
30814  * Copyright(c) 2006-2007, Ext JS, LLC.
30815  *
30816  * Originally Released Under LGPL - original licence link has changed is not relivant.
30817  *
30818  * Fork - LGPL
30819  * <script type="text/javascript">
30820  */
30821  
30822  
30823 /*
30824  * Private internal class for reading and applying state
30825  */
30826 Roo.LayoutStateManager = function(layout){
30827      // default empty state
30828      this.state = {
30829         north: {},
30830         south: {},
30831         east: {},
30832         west: {}       
30833     };
30834 };
30835
30836 Roo.LayoutStateManager.prototype = {
30837     init : function(layout, provider){
30838         this.provider = provider;
30839         var state = provider.get(layout.id+"-layout-state");
30840         if(state){
30841             var wasUpdating = layout.isUpdating();
30842             if(!wasUpdating){
30843                 layout.beginUpdate();
30844             }
30845             for(var key in state){
30846                 if(typeof state[key] != "function"){
30847                     var rstate = state[key];
30848                     var r = layout.getRegion(key);
30849                     if(r && rstate){
30850                         if(rstate.size){
30851                             r.resizeTo(rstate.size);
30852                         }
30853                         if(rstate.collapsed == true){
30854                             r.collapse(true);
30855                         }else{
30856                             r.expand(null, true);
30857                         }
30858                     }
30859                 }
30860             }
30861             if(!wasUpdating){
30862                 layout.endUpdate();
30863             }
30864             this.state = state; 
30865         }
30866         this.layout = layout;
30867         layout.on("regionresized", this.onRegionResized, this);
30868         layout.on("regioncollapsed", this.onRegionCollapsed, this);
30869         layout.on("regionexpanded", this.onRegionExpanded, this);
30870     },
30871     
30872     storeState : function(){
30873         this.provider.set(this.layout.id+"-layout-state", this.state);
30874     },
30875     
30876     onRegionResized : function(region, newSize){
30877         this.state[region.getPosition()].size = newSize;
30878         this.storeState();
30879     },
30880     
30881     onRegionCollapsed : function(region){
30882         this.state[region.getPosition()].collapsed = true;
30883         this.storeState();
30884     },
30885     
30886     onRegionExpanded : function(region){
30887         this.state[region.getPosition()].collapsed = false;
30888         this.storeState();
30889     }
30890 };/*
30891  * Based on:
30892  * Ext JS Library 1.1.1
30893  * Copyright(c) 2006-2007, Ext JS, LLC.
30894  *
30895  * Originally Released Under LGPL - original licence link has changed is not relivant.
30896  *
30897  * Fork - LGPL
30898  * <script type="text/javascript">
30899  */
30900 /**
30901  * @class Roo.ContentPanel
30902  * @extends Roo.util.Observable
30903  * A basic ContentPanel element.
30904  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
30905  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
30906  * @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
30907  * @cfg {Boolean} closable True if the panel can be closed/removed
30908  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
30909  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
30910  * @cfg {Toolbar} toolbar A toolbar for this panel
30911  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
30912  * @cfg {String} title The title for this panel
30913  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
30914  * @cfg {String} url Calls {@link #setUrl} with this value
30915  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
30916  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
30917  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
30918  * @constructor
30919  * Create a new ContentPanel.
30920  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
30921  * @param {String/Object} config A string to set only the title or a config object
30922  * @param {String} content (optional) Set the HTML content for this panel
30923  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
30924  */
30925 Roo.ContentPanel = function(el, config, content){
30926     
30927      
30928     /*
30929     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
30930         config = el;
30931         el = Roo.id();
30932     }
30933     if (config && config.parentLayout) { 
30934         el = config.parentLayout.el.createChild(); 
30935     }
30936     */
30937     if(el.autoCreate){ // xtype is available if this is called from factory
30938         config = el;
30939         el = Roo.id();
30940     }
30941     this.el = Roo.get(el);
30942     if(!this.el && config && config.autoCreate){
30943         if(typeof config.autoCreate == "object"){
30944             if(!config.autoCreate.id){
30945                 config.autoCreate.id = config.id||el;
30946             }
30947             this.el = Roo.DomHelper.append(document.body,
30948                         config.autoCreate, true);
30949         }else{
30950             this.el = Roo.DomHelper.append(document.body,
30951                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
30952         }
30953     }
30954     this.closable = false;
30955     this.loaded = false;
30956     this.active = false;
30957     if(typeof config == "string"){
30958         this.title = config;
30959     }else{
30960         Roo.apply(this, config);
30961     }
30962     
30963     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
30964         this.wrapEl = this.el.wrap();    
30965         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
30966         
30967     }
30968     
30969     
30970     
30971     if(this.resizeEl){
30972         this.resizeEl = Roo.get(this.resizeEl, true);
30973     }else{
30974         this.resizeEl = this.el;
30975     }
30976     this.addEvents({
30977         /**
30978          * @event activate
30979          * Fires when this panel is activated. 
30980          * @param {Roo.ContentPanel} this
30981          */
30982         "activate" : true,
30983         /**
30984          * @event deactivate
30985          * Fires when this panel is activated. 
30986          * @param {Roo.ContentPanel} this
30987          */
30988         "deactivate" : true,
30989
30990         /**
30991          * @event resize
30992          * Fires when this panel is resized if fitToFrame is true.
30993          * @param {Roo.ContentPanel} this
30994          * @param {Number} width The width after any component adjustments
30995          * @param {Number} height The height after any component adjustments
30996          */
30997         "resize" : true
30998     });
30999     if(this.autoScroll){
31000         this.resizeEl.setStyle("overflow", "auto");
31001     } else {
31002         // fix randome scrolling
31003         this.el.on('scroll', function() {
31004             this.scrollTo('top',0); 
31005         });
31006     }
31007     content = content || this.content;
31008     if(content){
31009         this.setContent(content);
31010     }
31011     if(config && config.url){
31012         this.setUrl(this.url, this.params, this.loadOnce);
31013     }
31014     
31015     
31016     
31017     Roo.ContentPanel.superclass.constructor.call(this);
31018 };
31019
31020 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31021     tabTip:'',
31022     setRegion : function(region){
31023         this.region = region;
31024         if(region){
31025            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31026         }else{
31027            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31028         } 
31029     },
31030     
31031     /**
31032      * Returns the toolbar for this Panel if one was configured. 
31033      * @return {Roo.Toolbar} 
31034      */
31035     getToolbar : function(){
31036         return this.toolbar;
31037     },
31038     
31039     setActiveState : function(active){
31040         this.active = active;
31041         if(!active){
31042             this.fireEvent("deactivate", this);
31043         }else{
31044             this.fireEvent("activate", this);
31045         }
31046     },
31047     /**
31048      * Updates this panel's element
31049      * @param {String} content The new content
31050      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31051     */
31052     setContent : function(content, loadScripts){
31053         this.el.update(content, loadScripts);
31054     },
31055
31056     ignoreResize : function(w, h){
31057         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31058             return true;
31059         }else{
31060             this.lastSize = {width: w, height: h};
31061             return false;
31062         }
31063     },
31064     /**
31065      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31066      * @return {Roo.UpdateManager} The UpdateManager
31067      */
31068     getUpdateManager : function(){
31069         return this.el.getUpdateManager();
31070     },
31071      /**
31072      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31073      * @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:
31074 <pre><code>
31075 panel.load({
31076     url: "your-url.php",
31077     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31078     callback: yourFunction,
31079     scope: yourObject, //(optional scope)
31080     discardUrl: false,
31081     nocache: false,
31082     text: "Loading...",
31083     timeout: 30,
31084     scripts: false
31085 });
31086 </code></pre>
31087      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31088      * 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.
31089      * @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}
31090      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31091      * @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.
31092      * @return {Roo.ContentPanel} this
31093      */
31094     load : function(){
31095         var um = this.el.getUpdateManager();
31096         um.update.apply(um, arguments);
31097         return this;
31098     },
31099
31100
31101     /**
31102      * 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.
31103      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31104      * @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)
31105      * @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)
31106      * @return {Roo.UpdateManager} The UpdateManager
31107      */
31108     setUrl : function(url, params, loadOnce){
31109         if(this.refreshDelegate){
31110             this.removeListener("activate", this.refreshDelegate);
31111         }
31112         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31113         this.on("activate", this.refreshDelegate);
31114         return this.el.getUpdateManager();
31115     },
31116     
31117     _handleRefresh : function(url, params, loadOnce){
31118         if(!loadOnce || !this.loaded){
31119             var updater = this.el.getUpdateManager();
31120             updater.update(url, params, this._setLoaded.createDelegate(this));
31121         }
31122     },
31123     
31124     _setLoaded : function(){
31125         this.loaded = true;
31126     }, 
31127     
31128     /**
31129      * Returns this panel's id
31130      * @return {String} 
31131      */
31132     getId : function(){
31133         return this.el.id;
31134     },
31135     
31136     /** 
31137      * Returns this panel's element - used by regiosn to add.
31138      * @return {Roo.Element} 
31139      */
31140     getEl : function(){
31141         return this.wrapEl || this.el;
31142     },
31143     
31144     adjustForComponents : function(width, height){
31145         if(this.resizeEl != this.el){
31146             width -= this.el.getFrameWidth('lr');
31147             height -= this.el.getFrameWidth('tb');
31148         }
31149         if(this.toolbar){
31150             var te = this.toolbar.getEl();
31151             height -= te.getHeight();
31152             te.setWidth(width);
31153         }
31154         if(this.adjustments){
31155             width += this.adjustments[0];
31156             height += this.adjustments[1];
31157         }
31158         return {"width": width, "height": height};
31159     },
31160     
31161     setSize : function(width, height){
31162         if(this.fitToFrame && !this.ignoreResize(width, height)){
31163             if(this.fitContainer && this.resizeEl != this.el){
31164                 this.el.setSize(width, height);
31165             }
31166             var size = this.adjustForComponents(width, height);
31167             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31168             this.fireEvent('resize', this, size.width, size.height);
31169         }
31170     },
31171     
31172     /**
31173      * Returns this panel's title
31174      * @return {String} 
31175      */
31176     getTitle : function(){
31177         return this.title;
31178     },
31179     
31180     /**
31181      * Set this panel's title
31182      * @param {String} title
31183      */
31184     setTitle : function(title){
31185         this.title = title;
31186         if(this.region){
31187             this.region.updatePanelTitle(this, title);
31188         }
31189     },
31190     
31191     /**
31192      * Returns true is this panel was configured to be closable
31193      * @return {Boolean} 
31194      */
31195     isClosable : function(){
31196         return this.closable;
31197     },
31198     
31199     beforeSlide : function(){
31200         this.el.clip();
31201         this.resizeEl.clip();
31202     },
31203     
31204     afterSlide : function(){
31205         this.el.unclip();
31206         this.resizeEl.unclip();
31207     },
31208     
31209     /**
31210      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31211      *   Will fail silently if the {@link #setUrl} method has not been called.
31212      *   This does not activate the panel, just updates its content.
31213      */
31214     refresh : function(){
31215         if(this.refreshDelegate){
31216            this.loaded = false;
31217            this.refreshDelegate();
31218         }
31219     },
31220     
31221     /**
31222      * Destroys this panel
31223      */
31224     destroy : function(){
31225         this.el.removeAllListeners();
31226         var tempEl = document.createElement("span");
31227         tempEl.appendChild(this.el.dom);
31228         tempEl.innerHTML = "";
31229         this.el.remove();
31230         this.el = null;
31231     },
31232     
31233       /**
31234      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31235      * <pre><code>
31236
31237 layout.addxtype({
31238        xtype : 'Form',
31239        items: [ .... ]
31240    }
31241 );
31242
31243 </code></pre>
31244      * @param {Object} cfg Xtype definition of item to add.
31245      */
31246     
31247     addxtype : function(cfg) {
31248         // add form..
31249         if (cfg.xtype.match(/^Form$/)) {
31250             var el = this.el.createChild();
31251
31252             this.form = new  Roo.form.Form(cfg);
31253             
31254             
31255             if ( this.form.allItems.length) this.form.render(el.dom);
31256             return this.form;
31257         }
31258         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
31259             // views..
31260             cfg.el = this.el.appendChild(document.createElement("div"));
31261             // factory?
31262             var ret = new Roo[cfg.xtype](cfg);
31263             ret.render(false, ''); // render blank..
31264             return ret;
31265             
31266         }
31267         return false;
31268         
31269     }
31270 });
31271
31272 /**
31273  * @class Roo.GridPanel
31274  * @extends Roo.ContentPanel
31275  * @constructor
31276  * Create a new GridPanel.
31277  * @param {Roo.grid.Grid} grid The grid for this panel
31278  * @param {String/Object} config A string to set only the panel's title, or a config object
31279  */
31280 Roo.GridPanel = function(grid, config){
31281     
31282   
31283     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31284         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31285         
31286     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31287     
31288     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31289     
31290     if(this.toolbar){
31291         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31292     }
31293     // xtype created footer. - not sure if will work as we normally have to render first..
31294     if (this.footer && !this.footer.el && this.footer.xtype) {
31295         
31296         this.footer.container = this.grid.getView().getFooterPanel(true);
31297         this.footer.dataSource = this.grid.dataSource;
31298         this.footer = Roo.factory(this.footer, Roo);
31299         
31300     }
31301     
31302     grid.monitorWindowResize = false; // turn off autosizing
31303     grid.autoHeight = false;
31304     grid.autoWidth = false;
31305     this.grid = grid;
31306     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31307 };
31308
31309 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31310     getId : function(){
31311         return this.grid.id;
31312     },
31313     
31314     /**
31315      * Returns the grid for this panel
31316      * @return {Roo.grid.Grid} 
31317      */
31318     getGrid : function(){
31319         return this.grid;    
31320     },
31321     
31322     setSize : function(width, height){
31323         if(!this.ignoreResize(width, height)){
31324             var grid = this.grid;
31325             var size = this.adjustForComponents(width, height);
31326             grid.getGridEl().setSize(size.width, size.height);
31327             grid.autoSize();
31328         }
31329     },
31330     
31331     beforeSlide : function(){
31332         this.grid.getView().scroller.clip();
31333     },
31334     
31335     afterSlide : function(){
31336         this.grid.getView().scroller.unclip();
31337     },
31338     
31339     destroy : function(){
31340         this.grid.destroy();
31341         delete this.grid;
31342         Roo.GridPanel.superclass.destroy.call(this); 
31343     }
31344 });
31345
31346
31347 /**
31348  * @class Roo.NestedLayoutPanel
31349  * @extends Roo.ContentPanel
31350  * @constructor
31351  * Create a new NestedLayoutPanel.
31352  * 
31353  * 
31354  * @param {Roo.BorderLayout} layout The layout for this panel
31355  * @param {String/Object} config A string to set only the title or a config object
31356  */
31357 Roo.NestedLayoutPanel = function(layout, config)
31358 {
31359     // construct with only one argument..
31360     /* FIXME - implement nicer consturctors
31361     if (layout.layout) {
31362         config = layout;
31363         layout = config.layout;
31364         delete config.layout;
31365     }
31366     if (layout.xtype && !layout.getEl) {
31367         // then layout needs constructing..
31368         layout = Roo.factory(layout, Roo);
31369     }
31370     */
31371     
31372     
31373     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31374     
31375     layout.monitorWindowResize = false; // turn off autosizing
31376     this.layout = layout;
31377     this.layout.getEl().addClass("x-layout-nested-layout");
31378     
31379     
31380     
31381     
31382 };
31383
31384 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31385
31386     setSize : function(width, height){
31387         if(!this.ignoreResize(width, height)){
31388             var size = this.adjustForComponents(width, height);
31389             var el = this.layout.getEl();
31390             el.setSize(size.width, size.height);
31391             var touch = el.dom.offsetWidth;
31392             this.layout.layout();
31393             // ie requires a double layout on the first pass
31394             if(Roo.isIE && !this.initialized){
31395                 this.initialized = true;
31396                 this.layout.layout();
31397             }
31398         }
31399     },
31400     
31401     // activate all subpanels if not currently active..
31402     
31403     setActiveState : function(active){
31404         this.active = active;
31405         if(!active){
31406             this.fireEvent("deactivate", this);
31407             return;
31408         }
31409         
31410         this.fireEvent("activate", this);
31411         // not sure if this should happen before or after..
31412         if (!this.layout) {
31413             return; // should not happen..
31414         }
31415         var reg = false;
31416         for (var r in this.layout.regions) {
31417             reg = this.layout.getRegion(r);
31418             if (reg.getActivePanel()) {
31419                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31420                 reg.setActivePanel(reg.getActivePanel());
31421                 continue;
31422             }
31423             if (!reg.panels.length) {
31424                 continue;
31425             }
31426             reg.showPanel(reg.getPanel(0));
31427         }
31428         
31429         
31430         
31431         
31432     },
31433     
31434     /**
31435      * Returns the nested BorderLayout for this panel
31436      * @return {Roo.BorderLayout} 
31437      */
31438     getLayout : function(){
31439         return this.layout;
31440     },
31441     
31442      /**
31443      * Adds a xtype elements to the layout of the nested panel
31444      * <pre><code>
31445
31446 panel.addxtype({
31447        xtype : 'ContentPanel',
31448        region: 'west',
31449        items: [ .... ]
31450    }
31451 );
31452
31453 panel.addxtype({
31454         xtype : 'NestedLayoutPanel',
31455         region: 'west',
31456         layout: {
31457            center: { },
31458            west: { }   
31459         },
31460         items : [ ... list of content panels or nested layout panels.. ]
31461    }
31462 );
31463 </code></pre>
31464      * @param {Object} cfg Xtype definition of item to add.
31465      */
31466     addxtype : function(cfg) {
31467         return this.layout.addxtype(cfg);
31468     
31469     }
31470 });
31471
31472 Roo.ScrollPanel = function(el, config, content){
31473     config = config || {};
31474     config.fitToFrame = true;
31475     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31476     
31477     this.el.dom.style.overflow = "hidden";
31478     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31479     this.el.removeClass("x-layout-inactive-content");
31480     this.el.on("mousewheel", this.onWheel, this);
31481
31482     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31483     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31484     up.unselectable(); down.unselectable();
31485     up.on("click", this.scrollUp, this);
31486     down.on("click", this.scrollDown, this);
31487     up.addClassOnOver("x-scroller-btn-over");
31488     down.addClassOnOver("x-scroller-btn-over");
31489     up.addClassOnClick("x-scroller-btn-click");
31490     down.addClassOnClick("x-scroller-btn-click");
31491     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31492
31493     this.resizeEl = this.el;
31494     this.el = wrap; this.up = up; this.down = down;
31495 };
31496
31497 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31498     increment : 100,
31499     wheelIncrement : 5,
31500     scrollUp : function(){
31501         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31502     },
31503
31504     scrollDown : function(){
31505         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31506     },
31507
31508     afterScroll : function(){
31509         var el = this.resizeEl;
31510         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31511         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31512         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31513     },
31514
31515     setSize : function(){
31516         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31517         this.afterScroll();
31518     },
31519
31520     onWheel : function(e){
31521         var d = e.getWheelDelta();
31522         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31523         this.afterScroll();
31524         e.stopEvent();
31525     },
31526
31527     setContent : function(content, loadScripts){
31528         this.resizeEl.update(content, loadScripts);
31529     }
31530
31531 });
31532
31533
31534
31535
31536
31537
31538
31539
31540
31541 /**
31542  * @class Roo.TreePanel
31543  * @extends Roo.ContentPanel
31544  * @constructor
31545  * Create a new TreePanel. - defaults to fit/scoll contents.
31546  * @param {String/Object} config A string to set only the panel's title, or a config object
31547  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31548  */
31549 Roo.TreePanel = function(config){
31550     var el = config.el;
31551     var tree = config.tree;
31552     delete config.tree; 
31553     delete config.el; // hopefull!
31554     
31555     // wrapper for IE7 strict & safari scroll issue
31556     
31557     var treeEl = el.createChild();
31558     config.resizeEl = treeEl;
31559     
31560     
31561     
31562     Roo.TreePanel.superclass.constructor.call(this, el, config);
31563  
31564  
31565     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31566     //console.log(tree);
31567     this.on('activate', function()
31568     {
31569         if (this.tree.rendered) {
31570             return;
31571         }
31572         //console.log('render tree');
31573         this.tree.render();
31574     });
31575     
31576     this.on('resize',  function (cp, w, h) {
31577             this.tree.innerCt.setWidth(w);
31578             this.tree.innerCt.setHeight(h);
31579             this.tree.innerCt.setStyle('overflow-y', 'auto');
31580     });
31581
31582         
31583     
31584 };
31585
31586 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31587     fitToFrame : true,
31588     autoScroll : true
31589 });
31590
31591
31592
31593
31594
31595
31596
31597
31598
31599
31600
31601 /*
31602  * Based on:
31603  * Ext JS Library 1.1.1
31604  * Copyright(c) 2006-2007, Ext JS, LLC.
31605  *
31606  * Originally Released Under LGPL - original licence link has changed is not relivant.
31607  *
31608  * Fork - LGPL
31609  * <script type="text/javascript">
31610  */
31611  
31612
31613 /**
31614  * @class Roo.ReaderLayout
31615  * @extends Roo.BorderLayout
31616  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31617  * center region containing two nested regions (a top one for a list view and one for item preview below),
31618  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31619  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31620  * expedites the setup of the overall layout and regions for this common application style.
31621  * Example:
31622  <pre><code>
31623 var reader = new Roo.ReaderLayout();
31624 var CP = Roo.ContentPanel;  // shortcut for adding
31625
31626 reader.beginUpdate();
31627 reader.add("north", new CP("north", "North"));
31628 reader.add("west", new CP("west", {title: "West"}));
31629 reader.add("east", new CP("east", {title: "East"}));
31630
31631 reader.regions.listView.add(new CP("listView", "List"));
31632 reader.regions.preview.add(new CP("preview", "Preview"));
31633 reader.endUpdate();
31634 </code></pre>
31635 * @constructor
31636 * Create a new ReaderLayout
31637 * @param {Object} config Configuration options
31638 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31639 * document.body if omitted)
31640 */
31641 Roo.ReaderLayout = function(config, renderTo){
31642     var c = config || {size:{}};
31643     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31644         north: c.north !== false ? Roo.apply({
31645             split:false,
31646             initialSize: 32,
31647             titlebar: false
31648         }, c.north) : false,
31649         west: c.west !== false ? Roo.apply({
31650             split:true,
31651             initialSize: 200,
31652             minSize: 175,
31653             maxSize: 400,
31654             titlebar: true,
31655             collapsible: true,
31656             animate: true,
31657             margins:{left:5,right:0,bottom:5,top:5},
31658             cmargins:{left:5,right:5,bottom:5,top:5}
31659         }, c.west) : false,
31660         east: c.east !== false ? Roo.apply({
31661             split:true,
31662             initialSize: 200,
31663             minSize: 175,
31664             maxSize: 400,
31665             titlebar: true,
31666             collapsible: true,
31667             animate: true,
31668             margins:{left:0,right:5,bottom:5,top:5},
31669             cmargins:{left:5,right:5,bottom:5,top:5}
31670         }, c.east) : false,
31671         center: Roo.apply({
31672             tabPosition: 'top',
31673             autoScroll:false,
31674             closeOnTab: true,
31675             titlebar:false,
31676             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
31677         }, c.center)
31678     });
31679
31680     this.el.addClass('x-reader');
31681
31682     this.beginUpdate();
31683
31684     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
31685         south: c.preview !== false ? Roo.apply({
31686             split:true,
31687             initialSize: 200,
31688             minSize: 100,
31689             autoScroll:true,
31690             collapsible:true,
31691             titlebar: true,
31692             cmargins:{top:5,left:0, right:0, bottom:0}
31693         }, c.preview) : false,
31694         center: Roo.apply({
31695             autoScroll:false,
31696             titlebar:false,
31697             minHeight:200
31698         }, c.listView)
31699     });
31700     this.add('center', new Roo.NestedLayoutPanel(inner,
31701             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
31702
31703     this.endUpdate();
31704
31705     this.regions.preview = inner.getRegion('south');
31706     this.regions.listView = inner.getRegion('center');
31707 };
31708
31709 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
31710  * Based on:
31711  * Ext JS Library 1.1.1
31712  * Copyright(c) 2006-2007, Ext JS, LLC.
31713  *
31714  * Originally Released Under LGPL - original licence link has changed is not relivant.
31715  *
31716  * Fork - LGPL
31717  * <script type="text/javascript">
31718  */
31719  
31720 /**
31721  * @class Roo.grid.Grid
31722  * @extends Roo.util.Observable
31723  * This class represents the primary interface of a component based grid control.
31724  * <br><br>Usage:<pre><code>
31725  var grid = new Roo.grid.Grid("my-container-id", {
31726      ds: myDataStore,
31727      cm: myColModel,
31728      selModel: mySelectionModel,
31729      autoSizeColumns: true,
31730      monitorWindowResize: false,
31731      trackMouseOver: true
31732  });
31733  // set any options
31734  grid.render();
31735  * </code></pre>
31736  * <b>Common Problems:</b><br/>
31737  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
31738  * element will correct this<br/>
31739  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
31740  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
31741  * are unpredictable.<br/>
31742  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
31743  * grid to calculate dimensions/offsets.<br/>
31744   * @constructor
31745  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
31746  * The container MUST have some type of size defined for the grid to fill. The container will be
31747  * automatically set to position relative if it isn't already.
31748  * @param {Object} config A config object that sets properties on this grid.
31749  */
31750 Roo.grid.Grid = function(container, config){
31751         // initialize the container
31752         this.container = Roo.get(container);
31753         this.container.update("");
31754         this.container.setStyle("overflow", "hidden");
31755     this.container.addClass('x-grid-container');
31756
31757     this.id = this.container.id;
31758
31759     Roo.apply(this, config);
31760     // check and correct shorthanded configs
31761     if(this.ds){
31762         this.dataSource = this.ds;
31763         delete this.ds;
31764     }
31765     if(this.cm){
31766         this.colModel = this.cm;
31767         delete this.cm;
31768     }
31769     if(this.sm){
31770         this.selModel = this.sm;
31771         delete this.sm;
31772     }
31773
31774     if (this.selModel) {
31775         this.selModel = Roo.factory(this.selModel, Roo.grid);
31776         this.sm = this.selModel;
31777         this.sm.xmodule = this.xmodule || false;
31778     }
31779     if (typeof(this.colModel.config) == 'undefined') {
31780         this.colModel = new Roo.grid.ColumnModel(this.colModel);
31781         this.cm = this.colModel;
31782         this.cm.xmodule = this.xmodule || false;
31783     }
31784     if (this.dataSource) {
31785         this.dataSource= Roo.factory(this.dataSource, Roo.data);
31786         this.ds = this.dataSource;
31787         this.ds.xmodule = this.xmodule || false;
31788         
31789     }
31790     
31791     
31792     
31793     if(this.width){
31794         this.container.setWidth(this.width);
31795     }
31796
31797     if(this.height){
31798         this.container.setHeight(this.height);
31799     }
31800     /** @private */
31801         this.addEvents({
31802             // raw events
31803             /**
31804              * @event click
31805              * The raw click event for the entire grid.
31806              * @param {Roo.EventObject} e
31807              */
31808             "click" : true,
31809             /**
31810              * @event dblclick
31811              * The raw dblclick event for the entire grid.
31812              * @param {Roo.EventObject} e
31813              */
31814             "dblclick" : true,
31815             /**
31816              * @event contextmenu
31817              * The raw contextmenu event for the entire grid.
31818              * @param {Roo.EventObject} e
31819              */
31820             "contextmenu" : true,
31821             /**
31822              * @event mousedown
31823              * The raw mousedown event for the entire grid.
31824              * @param {Roo.EventObject} e
31825              */
31826             "mousedown" : true,
31827             /**
31828              * @event mouseup
31829              * The raw mouseup event for the entire grid.
31830              * @param {Roo.EventObject} e
31831              */
31832             "mouseup" : true,
31833             /**
31834              * @event mouseover
31835              * The raw mouseover event for the entire grid.
31836              * @param {Roo.EventObject} e
31837              */
31838             "mouseover" : true,
31839             /**
31840              * @event mouseout
31841              * The raw mouseout event for the entire grid.
31842              * @param {Roo.EventObject} e
31843              */
31844             "mouseout" : true,
31845             /**
31846              * @event keypress
31847              * The raw keypress event for the entire grid.
31848              * @param {Roo.EventObject} e
31849              */
31850             "keypress" : true,
31851             /**
31852              * @event keydown
31853              * The raw keydown event for the entire grid.
31854              * @param {Roo.EventObject} e
31855              */
31856             "keydown" : true,
31857
31858             // custom events
31859
31860             /**
31861              * @event cellclick
31862              * Fires when a cell is clicked
31863              * @param {Grid} this
31864              * @param {Number} rowIndex
31865              * @param {Number} columnIndex
31866              * @param {Roo.EventObject} e
31867              */
31868             "cellclick" : true,
31869             /**
31870              * @event celldblclick
31871              * Fires when a cell is double clicked
31872              * @param {Grid} this
31873              * @param {Number} rowIndex
31874              * @param {Number} columnIndex
31875              * @param {Roo.EventObject} e
31876              */
31877             "celldblclick" : true,
31878             /**
31879              * @event rowclick
31880              * Fires when a row is clicked
31881              * @param {Grid} this
31882              * @param {Number} rowIndex
31883              * @param {Roo.EventObject} e
31884              */
31885             "rowclick" : true,
31886             /**
31887              * @event rowdblclick
31888              * Fires when a row is double clicked
31889              * @param {Grid} this
31890              * @param {Number} rowIndex
31891              * @param {Roo.EventObject} e
31892              */
31893             "rowdblclick" : true,
31894             /**
31895              * @event headerclick
31896              * Fires when a header is clicked
31897              * @param {Grid} this
31898              * @param {Number} columnIndex
31899              * @param {Roo.EventObject} e
31900              */
31901             "headerclick" : true,
31902             /**
31903              * @event headerdblclick
31904              * Fires when a header cell is double clicked
31905              * @param {Grid} this
31906              * @param {Number} columnIndex
31907              * @param {Roo.EventObject} e
31908              */
31909             "headerdblclick" : true,
31910             /**
31911              * @event rowcontextmenu
31912              * Fires when a row is right clicked
31913              * @param {Grid} this
31914              * @param {Number} rowIndex
31915              * @param {Roo.EventObject} e
31916              */
31917             "rowcontextmenu" : true,
31918             /**
31919          * @event cellcontextmenu
31920          * Fires when a cell is right clicked
31921          * @param {Grid} this
31922          * @param {Number} rowIndex
31923          * @param {Number} cellIndex
31924          * @param {Roo.EventObject} e
31925          */
31926          "cellcontextmenu" : true,
31927             /**
31928              * @event headercontextmenu
31929              * Fires when a header is right clicked
31930              * @param {Grid} this
31931              * @param {Number} columnIndex
31932              * @param {Roo.EventObject} e
31933              */
31934             "headercontextmenu" : true,
31935             /**
31936              * @event bodyscroll
31937              * Fires when the body element is scrolled
31938              * @param {Number} scrollLeft
31939              * @param {Number} scrollTop
31940              */
31941             "bodyscroll" : true,
31942             /**
31943              * @event columnresize
31944              * Fires when the user resizes a column
31945              * @param {Number} columnIndex
31946              * @param {Number} newSize
31947              */
31948             "columnresize" : true,
31949             /**
31950              * @event columnmove
31951              * Fires when the user moves a column
31952              * @param {Number} oldIndex
31953              * @param {Number} newIndex
31954              */
31955             "columnmove" : true,
31956             /**
31957              * @event startdrag
31958              * Fires when row(s) start being dragged
31959              * @param {Grid} this
31960              * @param {Roo.GridDD} dd The drag drop object
31961              * @param {event} e The raw browser event
31962              */
31963             "startdrag" : true,
31964             /**
31965              * @event enddrag
31966              * Fires when a drag operation is complete
31967              * @param {Grid} this
31968              * @param {Roo.GridDD} dd The drag drop object
31969              * @param {event} e The raw browser event
31970              */
31971             "enddrag" : true,
31972             /**
31973              * @event dragdrop
31974              * Fires when dragged row(s) are dropped on a valid DD target
31975              * @param {Grid} this
31976              * @param {Roo.GridDD} dd The drag drop object
31977              * @param {String} targetId The target drag drop object
31978              * @param {event} e The raw browser event
31979              */
31980             "dragdrop" : true,
31981             /**
31982              * @event dragover
31983              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
31984              * @param {Grid} this
31985              * @param {Roo.GridDD} dd The drag drop object
31986              * @param {String} targetId The target drag drop object
31987              * @param {event} e The raw browser event
31988              */
31989             "dragover" : true,
31990             /**
31991              * @event dragenter
31992              *  Fires when the dragged row(s) first cross another DD target while being dragged
31993              * @param {Grid} this
31994              * @param {Roo.GridDD} dd The drag drop object
31995              * @param {String} targetId The target drag drop object
31996              * @param {event} e The raw browser event
31997              */
31998             "dragenter" : true,
31999             /**
32000              * @event dragout
32001              * Fires when the dragged row(s) leave another DD target while being dragged
32002              * @param {Grid} this
32003              * @param {Roo.GridDD} dd The drag drop object
32004              * @param {String} targetId The target drag drop object
32005              * @param {event} e The raw browser event
32006              */
32007             "dragout" : true,
32008         /**
32009          * @event render
32010          * Fires when the grid is rendered
32011          * @param {Grid} grid
32012          */
32013         render : true
32014     });
32015
32016     Roo.grid.Grid.superclass.constructor.call(this);
32017 };
32018 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32019     
32020     /**
32021      * @cfg {String} ddGroup - drag drop group.
32022          */
32023     
32024     /**
32025      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32026          */
32027         minColumnWidth : 25,
32028
32029     /**
32030          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32031          * <b>on initial render.</b> It is more efficient to explicitly size the columns
32032          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32033          */
32034         autoSizeColumns : false,
32035
32036         /**
32037          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32038          */
32039         autoSizeHeaders : true,
32040
32041         /**
32042          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32043          */
32044         monitorWindowResize : true,
32045
32046         /**
32047          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32048          * rows measured to get a columns size. Default is 0 (all rows).
32049          */
32050         maxRowsToMeasure : 0,
32051
32052         /**
32053          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32054          */
32055         trackMouseOver : true,
32056
32057     /**
32058          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32059          */
32060     
32061         /**
32062          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32063          */
32064         enableDragDrop : false,
32065
32066         /**
32067          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32068          */
32069         enableColumnMove : true,
32070
32071         /**
32072          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32073          */
32074         enableColumnHide : true,
32075
32076         /**
32077          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32078          */
32079         enableRowHeightSync : false,
32080
32081         /**
32082          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32083          */
32084         stripeRows : true,
32085
32086         /**
32087          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32088          */
32089         autoHeight : false,
32090
32091     /**
32092      * @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.
32093      */
32094     autoExpandColumn : false,
32095
32096     /**
32097     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32098     * Default is 50.
32099     */
32100     autoExpandMin : 50,
32101
32102     /**
32103     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32104     */
32105     autoExpandMax : 1000,
32106
32107     /**
32108          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32109          */
32110         view : null,
32111
32112         /**
32113      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32114          */
32115         loadMask : false,
32116     /**
32117      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
32118          */
32119         dropTarget: false,
32120     // private
32121     rendered : false,
32122
32123     /**
32124     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32125     * of a fixed width. Default is false.
32126     */
32127     /**
32128     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32129     */
32130     /**
32131      * Called once after all setup has been completed and the grid is ready to be rendered.
32132      * @return {Roo.grid.Grid} this
32133      */
32134     render : function(){
32135         var c = this.container;
32136         // try to detect autoHeight/width mode
32137         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32138             this.autoHeight = true;
32139         }
32140         var view = this.getView();
32141         view.init(this);
32142
32143         c.on("click", this.onClick, this);
32144         c.on("dblclick", this.onDblClick, this);
32145         c.on("contextmenu", this.onContextMenu, this);
32146         c.on("keydown", this.onKeyDown, this);
32147
32148         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32149
32150         this.getSelectionModel().init(this);
32151
32152         view.render();
32153
32154         if(this.loadMask){
32155             this.loadMask = new Roo.LoadMask(this.container,
32156                     Roo.apply({store:this.dataSource}, this.loadMask));
32157         }
32158         
32159         
32160         if (this.toolbar && this.toolbar.xtype) {
32161             this.toolbar.container = this.getView().getHeaderPanel(true);
32162             this.toolbar = new Ext.Toolbar(this.toolbar);
32163         }
32164         if (this.footer && this.footer.xtype) {
32165             this.footer.dataSource = this.getDataSource();
32166             this.footer.container = this.getView().getFooterPanel(true);
32167             this.footer = Roo.factory(this.footer, Roo);
32168         }
32169         if (this.dropTarget && this.dropTarget.xtype) {
32170             delete this.dropTarget.xtype;
32171             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32172         }
32173         
32174         
32175         this.rendered = true;
32176         this.fireEvent('render', this);
32177         return this;
32178     },
32179
32180         /**
32181          * Reconfigures the grid to use a different Store and Column Model.
32182          * The View will be bound to the new objects and refreshed.
32183          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32184          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32185          */
32186     reconfigure : function(dataSource, colModel){
32187         if(this.loadMask){
32188             this.loadMask.destroy();
32189             this.loadMask = new Roo.LoadMask(this.container,
32190                     Roo.apply({store:dataSource}, this.loadMask));
32191         }
32192         this.view.bind(dataSource, colModel);
32193         this.dataSource = dataSource;
32194         this.colModel = colModel;
32195         this.view.refresh(true);
32196     },
32197
32198     // private
32199     onKeyDown : function(e){
32200         this.fireEvent("keydown", e);
32201     },
32202
32203     /**
32204      * Destroy this grid.
32205      * @param {Boolean} removeEl True to remove the element
32206      */
32207     destroy : function(removeEl, keepListeners){
32208         if(this.loadMask){
32209             this.loadMask.destroy();
32210         }
32211         var c = this.container;
32212         c.removeAllListeners();
32213         this.view.destroy();
32214         this.colModel.purgeListeners();
32215         if(!keepListeners){
32216             this.purgeListeners();
32217         }
32218         c.update("");
32219         if(removeEl === true){
32220             c.remove();
32221         }
32222     },
32223
32224     // private
32225     processEvent : function(name, e){
32226         this.fireEvent(name, e);
32227         var t = e.getTarget();
32228         var v = this.view;
32229         var header = v.findHeaderIndex(t);
32230         if(header !== false){
32231             this.fireEvent("header" + name, this, header, e);
32232         }else{
32233             var row = v.findRowIndex(t);
32234             var cell = v.findCellIndex(t);
32235             if(row !== false){
32236                 this.fireEvent("row" + name, this, row, e);
32237                 if(cell !== false){
32238                     this.fireEvent("cell" + name, this, row, cell, e);
32239                 }
32240             }
32241         }
32242     },
32243
32244     // private
32245     onClick : function(e){
32246         this.processEvent("click", e);
32247     },
32248
32249     // private
32250     onContextMenu : function(e, t){
32251         this.processEvent("contextmenu", e);
32252     },
32253
32254     // private
32255     onDblClick : function(e){
32256         this.processEvent("dblclick", e);
32257     },
32258
32259     // private
32260     walkCells : function(row, col, step, fn, scope){
32261         var cm = this.colModel, clen = cm.getColumnCount();
32262         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32263         if(step < 0){
32264             if(col < 0){
32265                 row--;
32266                 first = false;
32267             }
32268             while(row >= 0){
32269                 if(!first){
32270                     col = clen-1;
32271                 }
32272                 first = false;
32273                 while(col >= 0){
32274                     if(fn.call(scope || this, row, col, cm) === true){
32275                         return [row, col];
32276                     }
32277                     col--;
32278                 }
32279                 row--;
32280             }
32281         } else {
32282             if(col >= clen){
32283                 row++;
32284                 first = false;
32285             }
32286             while(row < rlen){
32287                 if(!first){
32288                     col = 0;
32289                 }
32290                 first = false;
32291                 while(col < clen){
32292                     if(fn.call(scope || this, row, col, cm) === true){
32293                         return [row, col];
32294                     }
32295                     col++;
32296                 }
32297                 row++;
32298             }
32299         }
32300         return null;
32301     },
32302
32303     // private
32304     getSelections : function(){
32305         return this.selModel.getSelections();
32306     },
32307
32308     /**
32309      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32310      * but if manual update is required this method will initiate it.
32311      */
32312     autoSize : function(){
32313         if(this.rendered){
32314             this.view.layout();
32315             if(this.view.adjustForScroll){
32316                 this.view.adjustForScroll();
32317             }
32318         }
32319     },
32320
32321     /**
32322      * Returns the grid's underlying element.
32323      * @return {Element} The element
32324      */
32325     getGridEl : function(){
32326         return this.container;
32327     },
32328
32329     // private for compatibility, overridden by editor grid
32330     stopEditing : function(){},
32331
32332     /**
32333      * Returns the grid's SelectionModel.
32334      * @return {SelectionModel}
32335      */
32336     getSelectionModel : function(){
32337         if(!this.selModel){
32338             this.selModel = new Roo.grid.RowSelectionModel();
32339         }
32340         return this.selModel;
32341     },
32342
32343     /**
32344      * Returns the grid's DataSource.
32345      * @return {DataSource}
32346      */
32347     getDataSource : function(){
32348         return this.dataSource;
32349     },
32350
32351     /**
32352      * Returns the grid's ColumnModel.
32353      * @return {ColumnModel}
32354      */
32355     getColumnModel : function(){
32356         return this.colModel;
32357     },
32358
32359     /**
32360      * Returns the grid's GridView object.
32361      * @return {GridView}
32362      */
32363     getView : function(){
32364         if(!this.view){
32365             this.view = new Roo.grid.GridView(this.viewConfig);
32366         }
32367         return this.view;
32368     },
32369     /**
32370      * Called to get grid's drag proxy text, by default returns this.ddText.
32371      * @return {String}
32372      */
32373     getDragDropText : function(){
32374         var count = this.selModel.getCount();
32375         return String.format(this.ddText, count, count == 1 ? '' : 's');
32376     }
32377 });
32378 /**
32379  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32380  * %0 is replaced with the number of selected rows.
32381  * @type String
32382  */
32383 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32384  * Based on:
32385  * Ext JS Library 1.1.1
32386  * Copyright(c) 2006-2007, Ext JS, LLC.
32387  *
32388  * Originally Released Under LGPL - original licence link has changed is not relivant.
32389  *
32390  * Fork - LGPL
32391  * <script type="text/javascript">
32392  */
32393  
32394 Roo.grid.AbstractGridView = function(){
32395         this.grid = null;
32396         
32397         this.events = {
32398             "beforerowremoved" : true,
32399             "beforerowsinserted" : true,
32400             "beforerefresh" : true,
32401             "rowremoved" : true,
32402             "rowsinserted" : true,
32403             "rowupdated" : true,
32404             "refresh" : true
32405         };
32406     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32407 };
32408
32409 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32410     rowClass : "x-grid-row",
32411     cellClass : "x-grid-cell",
32412     tdClass : "x-grid-td",
32413     hdClass : "x-grid-hd",
32414     splitClass : "x-grid-hd-split",
32415     
32416         init: function(grid){
32417         this.grid = grid;
32418                 var cid = this.grid.getGridEl().id;
32419         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32420         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32421         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32422         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32423         },
32424         
32425         getColumnRenderers : function(){
32426         var renderers = [];
32427         var cm = this.grid.colModel;
32428         var colCount = cm.getColumnCount();
32429         for(var i = 0; i < colCount; i++){
32430             renderers[i] = cm.getRenderer(i);
32431         }
32432         return renderers;
32433     },
32434     
32435     getColumnIds : function(){
32436         var ids = [];
32437         var cm = this.grid.colModel;
32438         var colCount = cm.getColumnCount();
32439         for(var i = 0; i < colCount; i++){
32440             ids[i] = cm.getColumnId(i);
32441         }
32442         return ids;
32443     },
32444     
32445     getDataIndexes : function(){
32446         if(!this.indexMap){
32447             this.indexMap = this.buildIndexMap();
32448         }
32449         return this.indexMap.colToData;
32450     },
32451     
32452     getColumnIndexByDataIndex : function(dataIndex){
32453         if(!this.indexMap){
32454             this.indexMap = this.buildIndexMap();
32455         }
32456         return this.indexMap.dataToCol[dataIndex];
32457     },
32458     
32459     /**
32460      * Set a css style for a column dynamically. 
32461      * @param {Number} colIndex The index of the column
32462      * @param {String} name The css property name
32463      * @param {String} value The css value
32464      */
32465     setCSSStyle : function(colIndex, name, value){
32466         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32467         Roo.util.CSS.updateRule(selector, name, value);
32468     },
32469     
32470     generateRules : function(cm){
32471         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32472         Roo.util.CSS.removeStyleSheet(rulesId);
32473         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32474             var cid = cm.getColumnId(i);
32475             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32476                          this.tdSelector, cid, " {\n}\n",
32477                          this.hdSelector, cid, " {\n}\n",
32478                          this.splitSelector, cid, " {\n}\n");
32479         }
32480         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32481     }
32482 });/*
32483  * Based on:
32484  * Ext JS Library 1.1.1
32485  * Copyright(c) 2006-2007, Ext JS, LLC.
32486  *
32487  * Originally Released Under LGPL - original licence link has changed is not relivant.
32488  *
32489  * Fork - LGPL
32490  * <script type="text/javascript">
32491  */
32492
32493 // private
32494 // This is a support class used internally by the Grid components
32495 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32496     this.grid = grid;
32497     this.view = grid.getView();
32498     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32499     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32500     if(hd2){
32501         this.setHandleElId(Roo.id(hd));
32502         this.setOuterHandleElId(Roo.id(hd2));
32503     }
32504     this.scroll = false;
32505 };
32506 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32507     maxDragWidth: 120,
32508     getDragData : function(e){
32509         var t = Roo.lib.Event.getTarget(e);
32510         var h = this.view.findHeaderCell(t);
32511         if(h){
32512             return {ddel: h.firstChild, header:h};
32513         }
32514         return false;
32515     },
32516
32517     onInitDrag : function(e){
32518         this.view.headersDisabled = true;
32519         var clone = this.dragData.ddel.cloneNode(true);
32520         clone.id = Roo.id();
32521         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32522         this.proxy.update(clone);
32523         return true;
32524     },
32525
32526     afterValidDrop : function(){
32527         var v = this.view;
32528         setTimeout(function(){
32529             v.headersDisabled = false;
32530         }, 50);
32531     },
32532
32533     afterInvalidDrop : function(){
32534         var v = this.view;
32535         setTimeout(function(){
32536             v.headersDisabled = false;
32537         }, 50);
32538     }
32539 });
32540 /*
32541  * Based on:
32542  * Ext JS Library 1.1.1
32543  * Copyright(c) 2006-2007, Ext JS, LLC.
32544  *
32545  * Originally Released Under LGPL - original licence link has changed is not relivant.
32546  *
32547  * Fork - LGPL
32548  * <script type="text/javascript">
32549  */
32550 // private
32551 // This is a support class used internally by the Grid components
32552 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32553     this.grid = grid;
32554     this.view = grid.getView();
32555     // split the proxies so they don't interfere with mouse events
32556     this.proxyTop = Roo.DomHelper.append(document.body, {
32557         cls:"col-move-top", html:"&#160;"
32558     }, true);
32559     this.proxyBottom = Roo.DomHelper.append(document.body, {
32560         cls:"col-move-bottom", html:"&#160;"
32561     }, true);
32562     this.proxyTop.hide = this.proxyBottom.hide = function(){
32563         this.setLeftTop(-100,-100);
32564         this.setStyle("visibility", "hidden");
32565     };
32566     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32567     // temporarily disabled
32568     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32569     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32570 };
32571 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32572     proxyOffsets : [-4, -9],
32573     fly: Roo.Element.fly,
32574
32575     getTargetFromEvent : function(e){
32576         var t = Roo.lib.Event.getTarget(e);
32577         var cindex = this.view.findCellIndex(t);
32578         if(cindex !== false){
32579             return this.view.getHeaderCell(cindex);
32580         }
32581     },
32582
32583     nextVisible : function(h){
32584         var v = this.view, cm = this.grid.colModel;
32585         h = h.nextSibling;
32586         while(h){
32587             if(!cm.isHidden(v.getCellIndex(h))){
32588                 return h;
32589             }
32590             h = h.nextSibling;
32591         }
32592         return null;
32593     },
32594
32595     prevVisible : function(h){
32596         var v = this.view, cm = this.grid.colModel;
32597         h = h.prevSibling;
32598         while(h){
32599             if(!cm.isHidden(v.getCellIndex(h))){
32600                 return h;
32601             }
32602             h = h.prevSibling;
32603         }
32604         return null;
32605     },
32606
32607     positionIndicator : function(h, n, e){
32608         var x = Roo.lib.Event.getPageX(e);
32609         var r = Roo.lib.Dom.getRegion(n.firstChild);
32610         var px, pt, py = r.top + this.proxyOffsets[1];
32611         if((r.right - x) <= (r.right-r.left)/2){
32612             px = r.right+this.view.borderWidth;
32613             pt = "after";
32614         }else{
32615             px = r.left;
32616             pt = "before";
32617         }
32618         var oldIndex = this.view.getCellIndex(h);
32619         var newIndex = this.view.getCellIndex(n);
32620
32621         if(this.grid.colModel.isFixed(newIndex)){
32622             return false;
32623         }
32624
32625         var locked = this.grid.colModel.isLocked(newIndex);
32626
32627         if(pt == "after"){
32628             newIndex++;
32629         }
32630         if(oldIndex < newIndex){
32631             newIndex--;
32632         }
32633         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
32634             return false;
32635         }
32636         px +=  this.proxyOffsets[0];
32637         this.proxyTop.setLeftTop(px, py);
32638         this.proxyTop.show();
32639         if(!this.bottomOffset){
32640             this.bottomOffset = this.view.mainHd.getHeight();
32641         }
32642         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
32643         this.proxyBottom.show();
32644         return pt;
32645     },
32646
32647     onNodeEnter : function(n, dd, e, data){
32648         if(data.header != n){
32649             this.positionIndicator(data.header, n, e);
32650         }
32651     },
32652
32653     onNodeOver : function(n, dd, e, data){
32654         var result = false;
32655         if(data.header != n){
32656             result = this.positionIndicator(data.header, n, e);
32657         }
32658         if(!result){
32659             this.proxyTop.hide();
32660             this.proxyBottom.hide();
32661         }
32662         return result ? this.dropAllowed : this.dropNotAllowed;
32663     },
32664
32665     onNodeOut : function(n, dd, e, data){
32666         this.proxyTop.hide();
32667         this.proxyBottom.hide();
32668     },
32669
32670     onNodeDrop : function(n, dd, e, data){
32671         var h = data.header;
32672         if(h != n){
32673             var cm = this.grid.colModel;
32674             var x = Roo.lib.Event.getPageX(e);
32675             var r = Roo.lib.Dom.getRegion(n.firstChild);
32676             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
32677             var oldIndex = this.view.getCellIndex(h);
32678             var newIndex = this.view.getCellIndex(n);
32679             var locked = cm.isLocked(newIndex);
32680             if(pt == "after"){
32681                 newIndex++;
32682             }
32683             if(oldIndex < newIndex){
32684                 newIndex--;
32685             }
32686             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
32687                 return false;
32688             }
32689             cm.setLocked(oldIndex, locked, true);
32690             cm.moveColumn(oldIndex, newIndex);
32691             this.grid.fireEvent("columnmove", oldIndex, newIndex);
32692             return true;
32693         }
32694         return false;
32695     }
32696 });
32697 /*
32698  * Based on:
32699  * Ext JS Library 1.1.1
32700  * Copyright(c) 2006-2007, Ext JS, LLC.
32701  *
32702  * Originally Released Under LGPL - original licence link has changed is not relivant.
32703  *
32704  * Fork - LGPL
32705  * <script type="text/javascript">
32706  */
32707   
32708 /**
32709  * @class Roo.grid.GridView
32710  * @extends Roo.util.Observable
32711  *
32712  * @constructor
32713  * @param {Object} config
32714  */
32715 Roo.grid.GridView = function(config){
32716     Roo.grid.GridView.superclass.constructor.call(this);
32717     this.el = null;
32718
32719     Roo.apply(this, config);
32720 };
32721
32722 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
32723
32724     /**
32725      * Override this function to apply custom css classes to rows during rendering
32726      * @param {Record} record The record
32727      * @param {Number} index
32728      * @method getRowClass
32729      */
32730     rowClass : "x-grid-row",
32731
32732     cellClass : "x-grid-col",
32733
32734     tdClass : "x-grid-td",
32735
32736     hdClass : "x-grid-hd",
32737
32738     splitClass : "x-grid-split",
32739
32740     sortClasses : ["sort-asc", "sort-desc"],
32741
32742     enableMoveAnim : false,
32743
32744     hlColor: "C3DAF9",
32745
32746     dh : Roo.DomHelper,
32747
32748     fly : Roo.Element.fly,
32749
32750     css : Roo.util.CSS,
32751
32752     borderWidth: 1,
32753
32754     splitOffset: 3,
32755
32756     scrollIncrement : 22,
32757
32758     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
32759
32760     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
32761
32762     bind : function(ds, cm){
32763         if(this.ds){
32764             this.ds.un("load", this.onLoad, this);
32765             this.ds.un("datachanged", this.onDataChange, this);
32766             this.ds.un("add", this.onAdd, this);
32767             this.ds.un("remove", this.onRemove, this);
32768             this.ds.un("update", this.onUpdate, this);
32769             this.ds.un("clear", this.onClear, this);
32770         }
32771         if(ds){
32772             ds.on("load", this.onLoad, this);
32773             ds.on("datachanged", this.onDataChange, this);
32774             ds.on("add", this.onAdd, this);
32775             ds.on("remove", this.onRemove, this);
32776             ds.on("update", this.onUpdate, this);
32777             ds.on("clear", this.onClear, this);
32778         }
32779         this.ds = ds;
32780
32781         if(this.cm){
32782             this.cm.un("widthchange", this.onColWidthChange, this);
32783             this.cm.un("headerchange", this.onHeaderChange, this);
32784             this.cm.un("hiddenchange", this.onHiddenChange, this);
32785             this.cm.un("columnmoved", this.onColumnMove, this);
32786             this.cm.un("columnlockchange", this.onColumnLock, this);
32787         }
32788         if(cm){
32789             this.generateRules(cm);
32790             cm.on("widthchange", this.onColWidthChange, this);
32791             cm.on("headerchange", this.onHeaderChange, this);
32792             cm.on("hiddenchange", this.onHiddenChange, this);
32793             cm.on("columnmoved", this.onColumnMove, this);
32794             cm.on("columnlockchange", this.onColumnLock, this);
32795         }
32796         this.cm = cm;
32797     },
32798
32799     init: function(grid){
32800                 Roo.grid.GridView.superclass.init.call(this, grid);
32801
32802                 this.bind(grid.dataSource, grid.colModel);
32803
32804             grid.on("headerclick", this.handleHeaderClick, this);
32805
32806         if(grid.trackMouseOver){
32807             grid.on("mouseover", this.onRowOver, this);
32808                 grid.on("mouseout", this.onRowOut, this);
32809             }
32810             grid.cancelTextSelection = function(){};
32811                 this.gridId = grid.id;
32812
32813                 var tpls = this.templates || {};
32814
32815                 if(!tpls.master){
32816                     tpls.master = new Roo.Template(
32817                        '<div class="x-grid" hidefocus="true">',
32818                           '<div class="x-grid-topbar"></div>',
32819                           '<div class="x-grid-scroller"><div></div></div>',
32820                           '<div class="x-grid-locked">',
32821                               '<div class="x-grid-header">{lockedHeader}</div>',
32822                               '<div class="x-grid-body">{lockedBody}</div>',
32823                           "</div>",
32824                           '<div class="x-grid-viewport">',
32825                               '<div class="x-grid-header">{header}</div>',
32826                               '<div class="x-grid-body">{body}</div>',
32827                           "</div>",
32828                           '<div class="x-grid-bottombar"></div>',
32829                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
32830                           '<div class="x-grid-resize-proxy">&#160;</div>',
32831                        "</div>"
32832                     );
32833                     tpls.master.disableformats = true;
32834                 }
32835
32836                 if(!tpls.header){
32837                     tpls.header = new Roo.Template(
32838                        '<table border="0" cellspacing="0" cellpadding="0">',
32839                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
32840                        "</table>{splits}"
32841                     );
32842                     tpls.header.disableformats = true;
32843                 }
32844                 tpls.header.compile();
32845
32846                 if(!tpls.hcell){
32847                     tpls.hcell = new Roo.Template(
32848                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
32849                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
32850                         "</div></td>"
32851                      );
32852                      tpls.hcell.disableFormats = true;
32853                 }
32854                 tpls.hcell.compile();
32855
32856                 if(!tpls.hsplit){
32857                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
32858                     tpls.hsplit.disableFormats = true;
32859                 }
32860                 tpls.hsplit.compile();
32861
32862                 if(!tpls.body){
32863                     tpls.body = new Roo.Template(
32864                        '<table border="0" cellspacing="0" cellpadding="0">',
32865                        "<tbody>{rows}</tbody>",
32866                        "</table>"
32867                     );
32868                     tpls.body.disableFormats = true;
32869                 }
32870                 tpls.body.compile();
32871
32872                 if(!tpls.row){
32873                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
32874                     tpls.row.disableFormats = true;
32875                 }
32876                 tpls.row.compile();
32877
32878                 if(!tpls.cell){
32879                     tpls.cell = new Roo.Template(
32880                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
32881                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
32882                         "</td>"
32883                     );
32884             tpls.cell.disableFormats = true;
32885         }
32886                 tpls.cell.compile();
32887
32888                 this.templates = tpls;
32889         },
32890
32891         // remap these for backwards compat
32892     onColWidthChange : function(){
32893         this.updateColumns.apply(this, arguments);
32894     },
32895     onHeaderChange : function(){
32896         this.updateHeaders.apply(this, arguments);
32897     }, 
32898     onHiddenChange : function(){
32899         this.handleHiddenChange.apply(this, arguments);
32900     },
32901     onColumnMove : function(){
32902         this.handleColumnMove.apply(this, arguments);
32903     },
32904     onColumnLock : function(){
32905         this.handleLockChange.apply(this, arguments);
32906     },
32907
32908     onDataChange : function(){
32909         this.refresh();
32910         this.updateHeaderSortState();
32911     },
32912
32913         onClear : function(){
32914         this.refresh();
32915     },
32916
32917         onUpdate : function(ds, record){
32918         this.refreshRow(record);
32919     },
32920
32921     refreshRow : function(record){
32922         var ds = this.ds, index;
32923         if(typeof record == 'number'){
32924             index = record;
32925             record = ds.getAt(index);
32926         }else{
32927             index = ds.indexOf(record);
32928         }
32929         this.insertRows(ds, index, index, true);
32930         this.onRemove(ds, record, index+1, true);
32931         this.syncRowHeights(index, index);
32932         this.layout();
32933         this.fireEvent("rowupdated", this, index, record);
32934     },
32935
32936     onAdd : function(ds, records, index){
32937         this.insertRows(ds, index, index + (records.length-1));
32938     },
32939
32940     onRemove : function(ds, record, index, isUpdate){
32941         if(isUpdate !== true){
32942             this.fireEvent("beforerowremoved", this, index, record);
32943         }
32944         var bt = this.getBodyTable(), lt = this.getLockedTable();
32945         if(bt.rows[index]){
32946             bt.firstChild.removeChild(bt.rows[index]);
32947         }
32948         if(lt.rows[index]){
32949             lt.firstChild.removeChild(lt.rows[index]);
32950         }
32951         if(isUpdate !== true){
32952             this.stripeRows(index);
32953             this.syncRowHeights(index, index);
32954             this.layout();
32955             this.fireEvent("rowremoved", this, index, record);
32956         }
32957     },
32958
32959     onLoad : function(){
32960         this.scrollToTop();
32961     },
32962
32963     /**
32964      * Scrolls the grid to the top
32965      */
32966     scrollToTop : function(){
32967         if(this.scroller){
32968             this.scroller.dom.scrollTop = 0;
32969             this.syncScroll();
32970         }
32971     },
32972
32973     /**
32974      * Gets a panel in the header of the grid that can be used for toolbars etc.
32975      * After modifying the contents of this panel a call to grid.autoSize() may be
32976      * required to register any changes in size.
32977      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
32978      * @return Roo.Element
32979      */
32980     getHeaderPanel : function(doShow){
32981         if(doShow){
32982             this.headerPanel.show();
32983         }
32984         return this.headerPanel;
32985         },
32986
32987         /**
32988      * Gets a panel in the footer of the grid that can be used for toolbars etc.
32989      * After modifying the contents of this panel a call to grid.autoSize() may be
32990      * required to register any changes in size.
32991      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
32992      * @return Roo.Element
32993      */
32994     getFooterPanel : function(doShow){
32995         if(doShow){
32996             this.footerPanel.show();
32997         }
32998         return this.footerPanel;
32999         },
33000
33001         initElements : function(){
33002             var E = Roo.Element;
33003             var el = this.grid.getGridEl().dom.firstChild;
33004             var cs = el.childNodes;
33005
33006             this.el = new E(el);
33007             this.headerPanel = new E(el.firstChild);
33008             this.headerPanel.enableDisplayMode("block");
33009
33010         this.scroller = new E(cs[1]);
33011             this.scrollSizer = new E(this.scroller.dom.firstChild);
33012
33013             this.lockedWrap = new E(cs[2]);
33014             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33015             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33016
33017             this.mainWrap = new E(cs[3]);
33018             this.mainHd = new E(this.mainWrap.dom.firstChild);
33019             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33020
33021             this.footerPanel = new E(cs[4]);
33022             this.footerPanel.enableDisplayMode("block");
33023
33024         this.focusEl = new E(cs[5]);
33025         this.focusEl.swallowEvent("click", true);
33026         this.resizeProxy = new E(cs[6]);
33027
33028             this.headerSelector = String.format(
33029                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33030                this.lockedHd.id, this.mainHd.id
33031             );
33032
33033             this.splitterSelector = String.format(
33034                '#{0} div.x-grid-split, #{1} div.x-grid-split',
33035                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33036             );
33037     },
33038     idToCssName : function(s)
33039     {
33040         return s.replace(/[^a-z0-9]+/ig, '-');
33041     },
33042
33043         getHeaderCell : function(index){
33044             return Roo.DomQuery.select(this.headerSelector)[index];
33045         },
33046
33047         getHeaderCellMeasure : function(index){
33048             return this.getHeaderCell(index).firstChild;
33049         },
33050
33051         getHeaderCellText : function(index){
33052             return this.getHeaderCell(index).firstChild.firstChild;
33053         },
33054
33055         getLockedTable : function(){
33056             return this.lockedBody.dom.firstChild;
33057         },
33058
33059         getBodyTable : function(){
33060             return this.mainBody.dom.firstChild;
33061         },
33062
33063         getLockedRow : function(index){
33064             return this.getLockedTable().rows[index];
33065         },
33066
33067         getRow : function(index){
33068             return this.getBodyTable().rows[index];
33069         },
33070
33071         getRowComposite : function(index){
33072             if(!this.rowEl){
33073                 this.rowEl = new Roo.CompositeElementLite();
33074             }
33075         var els = [], lrow, mrow;
33076         if(lrow = this.getLockedRow(index)){
33077             els.push(lrow);
33078         }
33079         if(mrow = this.getRow(index)){
33080             els.push(mrow);
33081         }
33082         this.rowEl.elements = els;
33083             return this.rowEl;
33084         },
33085
33086         getCell : function(rowIndex, colIndex){
33087             var locked = this.cm.getLockedCount();
33088             var source;
33089             if(colIndex < locked){
33090                 source = this.lockedBody.dom.firstChild;
33091             }else{
33092                 source = this.mainBody.dom.firstChild;
33093                 colIndex -= locked;
33094             }
33095         return source.rows[rowIndex].childNodes[colIndex];
33096         },
33097
33098         getCellText : function(rowIndex, colIndex){
33099             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33100         },
33101
33102         getCellBox : function(cell){
33103             var b = this.fly(cell).getBox();
33104         if(Roo.isOpera){ // opera fails to report the Y
33105             b.y = cell.offsetTop + this.mainBody.getY();
33106         }
33107         return b;
33108     },
33109
33110     getCellIndex : function(cell){
33111         var id = String(cell.className).match(this.cellRE);
33112         if(id){
33113             return parseInt(id[1], 10);
33114         }
33115         return 0;
33116     },
33117
33118     findHeaderIndex : function(n){
33119         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33120         return r ? this.getCellIndex(r) : false;
33121     },
33122
33123     findHeaderCell : function(n){
33124         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33125         return r ? r : false;
33126     },
33127
33128     findRowIndex : function(n){
33129         if(!n){
33130             return false;
33131         }
33132         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33133         return r ? r.rowIndex : false;
33134     },
33135
33136     findCellIndex : function(node){
33137         var stop = this.el.dom;
33138         while(node && node != stop){
33139             if(this.findRE.test(node.className)){
33140                 return this.getCellIndex(node);
33141             }
33142             node = node.parentNode;
33143         }
33144         return false;
33145     },
33146
33147     getColumnId : function(index){
33148             return this.cm.getColumnId(index);
33149         },
33150
33151         getSplitters : function(){
33152             if(this.splitterSelector){
33153                return Roo.DomQuery.select(this.splitterSelector);
33154             }else{
33155                 return null;
33156             }
33157         },
33158
33159         getSplitter : function(index){
33160             return this.getSplitters()[index];
33161         },
33162
33163     onRowOver : function(e, t){
33164         var row;
33165         if((row = this.findRowIndex(t)) !== false){
33166             this.getRowComposite(row).addClass("x-grid-row-over");
33167         }
33168     },
33169
33170     onRowOut : function(e, t){
33171         var row;
33172         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33173             this.getRowComposite(row).removeClass("x-grid-row-over");
33174         }
33175     },
33176
33177     renderHeaders : function(){
33178             var cm = this.cm;
33179         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33180         var cb = [], lb = [], sb = [], lsb = [], p = {};
33181         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33182             p.cellId = "x-grid-hd-0-" + i;
33183             p.splitId = "x-grid-csplit-0-" + i;
33184             p.id = cm.getColumnId(i);
33185             p.title = cm.getColumnTooltip(i) || "";
33186             p.value = cm.getColumnHeader(i) || "";
33187             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33188             if(!cm.isLocked(i)){
33189                 cb[cb.length] = ct.apply(p);
33190                 sb[sb.length] = st.apply(p);
33191             }else{
33192                 lb[lb.length] = ct.apply(p);
33193                 lsb[lsb.length] = st.apply(p);
33194             }
33195         }
33196         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33197                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33198         },
33199
33200         updateHeaders : function(){
33201         var html = this.renderHeaders();
33202         this.lockedHd.update(html[0]);
33203         this.mainHd.update(html[1]);
33204     },
33205
33206     /**
33207      * Focuses the specified row.
33208      * @param {Number} row The row index
33209      */
33210     focusRow : function(row){
33211         var x = this.scroller.dom.scrollLeft;
33212         this.focusCell(row, 0, false);
33213         this.scroller.dom.scrollLeft = x;
33214     },
33215
33216     /**
33217      * Focuses the specified cell.
33218      * @param {Number} row The row index
33219      * @param {Number} col The column index
33220      * @param {Boolean} hscroll false to disable horizontal scrolling
33221      */
33222     focusCell : function(row, col, hscroll){
33223         var el = this.ensureVisible(row, col, hscroll);
33224         this.focusEl.alignTo(el, "tl-tl");
33225         if(Roo.isGecko){
33226             this.focusEl.focus();
33227         }else{
33228             this.focusEl.focus.defer(1, this.focusEl);
33229         }
33230     },
33231
33232     /**
33233      * Scrolls the specified cell into view
33234      * @param {Number} row The row index
33235      * @param {Number} col The column index
33236      * @param {Boolean} hscroll false to disable horizontal scrolling
33237      */
33238     ensureVisible : function(row, col, hscroll){
33239         if(typeof row != "number"){
33240             row = row.rowIndex;
33241         }
33242         if(row < 0 && row >= this.ds.getCount()){
33243             return;
33244         }
33245         col = (col !== undefined ? col : 0);
33246         var cm = this.grid.colModel;
33247         while(cm.isHidden(col)){
33248             col++;
33249         }
33250
33251         var el = this.getCell(row, col);
33252         if(!el){
33253             return;
33254         }
33255         var c = this.scroller.dom;
33256
33257         var ctop = parseInt(el.offsetTop, 10);
33258         var cleft = parseInt(el.offsetLeft, 10);
33259         var cbot = ctop + el.offsetHeight;
33260         var cright = cleft + el.offsetWidth;
33261
33262         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33263         var stop = parseInt(c.scrollTop, 10);
33264         var sleft = parseInt(c.scrollLeft, 10);
33265         var sbot = stop + ch;
33266         var sright = sleft + c.clientWidth;
33267
33268         if(ctop < stop){
33269                 c.scrollTop = ctop;
33270         }else if(cbot > sbot){
33271             c.scrollTop = cbot-ch;
33272         }
33273
33274         if(hscroll !== false){
33275             if(cleft < sleft){
33276                 c.scrollLeft = cleft;
33277             }else if(cright > sright){
33278                 c.scrollLeft = cright-c.clientWidth;
33279             }
33280         }
33281         return el;
33282     },
33283
33284     updateColumns : function(){
33285         this.grid.stopEditing();
33286         var cm = this.grid.colModel, colIds = this.getColumnIds();
33287         //var totalWidth = cm.getTotalWidth();
33288         var pos = 0;
33289         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33290             //if(cm.isHidden(i)) continue;
33291             var w = cm.getColumnWidth(i);
33292             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33293             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33294         }
33295         this.updateSplitters();
33296     },
33297
33298     generateRules : function(cm){
33299         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33300         Roo.util.CSS.removeStyleSheet(rulesId);
33301         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33302             var cid = cm.getColumnId(i);
33303             var align = '';
33304             if(cm.config[i].align){
33305                 align = 'text-align:'+cm.config[i].align+';';
33306             }
33307             var hidden = '';
33308             if(cm.isHidden(i)){
33309                 hidden = 'display:none;';
33310             }
33311             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33312             ruleBuf.push(
33313                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33314                     this.hdSelector, cid, " {\n", align, width, "}\n",
33315                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33316                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33317         }
33318         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33319     },
33320
33321     updateSplitters : function(){
33322         var cm = this.cm, s = this.getSplitters();
33323         if(s){ // splitters not created yet
33324             var pos = 0, locked = true;
33325             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33326                 if(cm.isHidden(i)) continue;
33327                 var w = cm.getColumnWidth(i);
33328                 if(!cm.isLocked(i) && locked){
33329                     pos = 0;
33330                     locked = false;
33331                 }
33332                 pos += w;
33333                 s[i].style.left = (pos-this.splitOffset) + "px";
33334             }
33335         }
33336     },
33337
33338     handleHiddenChange : function(colModel, colIndex, hidden){
33339         if(hidden){
33340             this.hideColumn(colIndex);
33341         }else{
33342             this.unhideColumn(colIndex);
33343         }
33344     },
33345
33346     hideColumn : function(colIndex){
33347         var cid = this.getColumnId(colIndex);
33348         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33349         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33350         if(Roo.isSafari){
33351             this.updateHeaders();
33352         }
33353         this.updateSplitters();
33354         this.layout();
33355     },
33356
33357     unhideColumn : function(colIndex){
33358         var cid = this.getColumnId(colIndex);
33359         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33360         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33361
33362         if(Roo.isSafari){
33363             this.updateHeaders();
33364         }
33365         this.updateSplitters();
33366         this.layout();
33367     },
33368
33369     insertRows : function(dm, firstRow, lastRow, isUpdate){
33370         if(firstRow == 0 && lastRow == dm.getCount()-1){
33371             this.refresh();
33372         }else{
33373             if(!isUpdate){
33374                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33375             }
33376             var s = this.getScrollState();
33377             var markup = this.renderRows(firstRow, lastRow);
33378             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33379             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33380             this.restoreScroll(s);
33381             if(!isUpdate){
33382                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33383                 this.syncRowHeights(firstRow, lastRow);
33384                 this.stripeRows(firstRow);
33385                 this.layout();
33386             }
33387         }
33388     },
33389
33390     bufferRows : function(markup, target, index){
33391         var before = null, trows = target.rows, tbody = target.tBodies[0];
33392         if(index < trows.length){
33393             before = trows[index];
33394         }
33395         var b = document.createElement("div");
33396         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33397         var rows = b.firstChild.rows;
33398         for(var i = 0, len = rows.length; i < len; i++){
33399             if(before){
33400                 tbody.insertBefore(rows[0], before);
33401             }else{
33402                 tbody.appendChild(rows[0]);
33403             }
33404         }
33405         b.innerHTML = "";
33406         b = null;
33407     },
33408
33409     deleteRows : function(dm, firstRow, lastRow){
33410         if(dm.getRowCount()<1){
33411             this.fireEvent("beforerefresh", this);
33412             this.mainBody.update("");
33413             this.lockedBody.update("");
33414             this.fireEvent("refresh", this);
33415         }else{
33416             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33417             var bt = this.getBodyTable();
33418             var tbody = bt.firstChild;
33419             var rows = bt.rows;
33420             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33421                 tbody.removeChild(rows[firstRow]);
33422             }
33423             this.stripeRows(firstRow);
33424             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33425         }
33426     },
33427
33428     updateRows : function(dataSource, firstRow, lastRow){
33429         var s = this.getScrollState();
33430         this.refresh();
33431         this.restoreScroll(s);
33432     },
33433
33434     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33435         if(!noRefresh){
33436            this.refresh();
33437         }
33438         this.updateHeaderSortState();
33439     },
33440
33441     getScrollState : function(){
33442         var sb = this.scroller.dom;
33443         return {left: sb.scrollLeft, top: sb.scrollTop};
33444     },
33445
33446     stripeRows : function(startRow){
33447         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33448             return;
33449         }
33450         startRow = startRow || 0;
33451         var rows = this.getBodyTable().rows;
33452         var lrows = this.getLockedTable().rows;
33453         var cls = ' x-grid-row-alt ';
33454         for(var i = startRow, len = rows.length; i < len; i++){
33455             var row = rows[i], lrow = lrows[i];
33456             var isAlt = ((i+1) % 2 == 0);
33457             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33458             if(isAlt == hasAlt){
33459                 continue;
33460             }
33461             if(isAlt){
33462                 row.className += " x-grid-row-alt";
33463             }else{
33464                 row.className = row.className.replace("x-grid-row-alt", "");
33465             }
33466             if(lrow){
33467                 lrow.className = row.className;
33468             }
33469         }
33470     },
33471
33472     restoreScroll : function(state){
33473         var sb = this.scroller.dom;
33474         sb.scrollLeft = state.left;
33475         sb.scrollTop = state.top;
33476         this.syncScroll();
33477     },
33478
33479     syncScroll : function(){
33480         var sb = this.scroller.dom;
33481         var sh = this.mainHd.dom;
33482         var bs = this.mainBody.dom;
33483         var lv = this.lockedBody.dom;
33484         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33485         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33486     },
33487
33488     handleScroll : function(e){
33489         this.syncScroll();
33490         var sb = this.scroller.dom;
33491         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33492         e.stopEvent();
33493     },
33494
33495     handleWheel : function(e){
33496         var d = e.getWheelDelta();
33497         this.scroller.dom.scrollTop -= d*22;
33498         // set this here to prevent jumpy scrolling on large tables
33499         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33500         e.stopEvent();
33501     },
33502
33503     renderRows : function(startRow, endRow){
33504         // pull in all the crap needed to render rows
33505         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33506         var colCount = cm.getColumnCount();
33507
33508         if(ds.getCount() < 1){
33509             return ["", ""];
33510         }
33511
33512         // build a map for all the columns
33513         var cs = [];
33514         for(var i = 0; i < colCount; i++){
33515             var name = cm.getDataIndex(i);
33516             cs[i] = {
33517                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33518                 renderer : cm.getRenderer(i),
33519                 id : cm.getColumnId(i),
33520                 locked : cm.isLocked(i)
33521             };
33522         }
33523
33524         startRow = startRow || 0;
33525         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33526
33527         // records to render
33528         var rs = ds.getRange(startRow, endRow);
33529
33530         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33531     },
33532
33533     // As much as I hate to duplicate code, this was branched because FireFox really hates
33534     // [].join("") on strings. The performance difference was substantial enough to
33535     // branch this function
33536     doRender : Roo.isGecko ?
33537             function(cs, rs, ds, startRow, colCount, stripe){
33538                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33539                 // buffers
33540                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33541                 for(var j = 0, len = rs.length; j < len; j++){
33542                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
33543                     for(var i = 0; i < colCount; i++){
33544                         c = cs[i];
33545                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33546                         p.id = c.id;
33547                         p.css = p.attr = "";
33548                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33549                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33550                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33551                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33552                         }
33553                         var markup = ct.apply(p);
33554                         if(!c.locked){
33555                             cb+= markup;
33556                         }else{
33557                             lcb+= markup;
33558                         }
33559                     }
33560                     var alt = [];
33561                     if(stripe && ((rowIndex+1) % 2 == 0)){
33562                         alt[0] = "x-grid-row-alt";
33563                     }
33564                     if(r.dirty){
33565                         alt[1] = " x-grid-dirty-row";
33566                     }
33567                     rp.cells = lcb;
33568                     if(this.getRowClass){
33569                         alt[2] = this.getRowClass(r, rowIndex);
33570                     }
33571                     rp.alt = alt.join(" ");
33572                     lbuf+= rt.apply(rp);
33573                     rp.cells = cb;
33574                     buf+=  rt.apply(rp);
33575                 }
33576                 return [lbuf, buf];
33577             } :
33578             function(cs, rs, ds, startRow, colCount, stripe){
33579                 var ts = this.templates, ct = ts.cell, rt = ts.row;
33580                 // buffers
33581                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
33582                 for(var j = 0, len = rs.length; j < len; j++){
33583                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
33584                     for(var i = 0; i < colCount; i++){
33585                         c = cs[i];
33586                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
33587                         p.id = c.id;
33588                         p.css = p.attr = "";
33589                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
33590                         if(p.value == undefined || p.value === "") p.value = "&#160;";
33591                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
33592                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
33593                         }
33594                         var markup = ct.apply(p);
33595                         if(!c.locked){
33596                             cb[cb.length] = markup;
33597                         }else{
33598                             lcb[lcb.length] = markup;
33599                         }
33600                     }
33601                     var alt = [];
33602                     if(stripe && ((rowIndex+1) % 2 == 0)){
33603                         alt[0] = "x-grid-row-alt";
33604                     }
33605                     if(r.dirty){
33606                         alt[1] = " x-grid-dirty-row";
33607                     }
33608                     rp.cells = lcb;
33609                     if(this.getRowClass){
33610                         alt[2] = this.getRowClass(r, rowIndex);
33611                     }
33612                     rp.alt = alt.join(" ");
33613                     rp.cells = lcb.join("");
33614                     lbuf[lbuf.length] = rt.apply(rp);
33615                     rp.cells = cb.join("");
33616                     buf[buf.length] =  rt.apply(rp);
33617                 }
33618                 return [lbuf.join(""), buf.join("")];
33619             },
33620
33621     renderBody : function(){
33622         var markup = this.renderRows();
33623         var bt = this.templates.body;
33624         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
33625     },
33626
33627     /**
33628      * Refreshes the grid
33629      * @param {Boolean} headersToo
33630      */
33631     refresh : function(headersToo){
33632         this.fireEvent("beforerefresh", this);
33633         this.grid.stopEditing();
33634         var result = this.renderBody();
33635         this.lockedBody.update(result[0]);
33636         this.mainBody.update(result[1]);
33637         if(headersToo === true){
33638             this.updateHeaders();
33639             this.updateColumns();
33640             this.updateSplitters();
33641             this.updateHeaderSortState();
33642         }
33643         this.syncRowHeights();
33644         this.layout();
33645         this.fireEvent("refresh", this);
33646     },
33647
33648     handleColumnMove : function(cm, oldIndex, newIndex){
33649         this.indexMap = null;
33650         var s = this.getScrollState();
33651         this.refresh(true);
33652         this.restoreScroll(s);
33653         this.afterMove(newIndex);
33654     },
33655
33656     afterMove : function(colIndex){
33657         if(this.enableMoveAnim && Roo.enableFx){
33658             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
33659         }
33660     },
33661
33662     updateCell : function(dm, rowIndex, dataIndex){
33663         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
33664         if(typeof colIndex == "undefined"){ // not present in grid
33665             return;
33666         }
33667         var cm = this.grid.colModel;
33668         var cell = this.getCell(rowIndex, colIndex);
33669         var cellText = this.getCellText(rowIndex, colIndex);
33670
33671         var p = {
33672             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
33673             id : cm.getColumnId(colIndex),
33674             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
33675         };
33676         var renderer = cm.getRenderer(colIndex);
33677         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
33678         if(typeof val == "undefined" || val === "") val = "&#160;";
33679         cellText.innerHTML = val;
33680         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
33681         this.syncRowHeights(rowIndex, rowIndex);
33682     },
33683
33684     calcColumnWidth : function(colIndex, maxRowsToMeasure){
33685         var maxWidth = 0;
33686         if(this.grid.autoSizeHeaders){
33687             var h = this.getHeaderCellMeasure(colIndex);
33688             maxWidth = Math.max(maxWidth, h.scrollWidth);
33689         }
33690         var tb, index;
33691         if(this.cm.isLocked(colIndex)){
33692             tb = this.getLockedTable();
33693             index = colIndex;
33694         }else{
33695             tb = this.getBodyTable();
33696             index = colIndex - this.cm.getLockedCount();
33697         }
33698         if(tb && tb.rows){
33699             var rows = tb.rows;
33700             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
33701             for(var i = 0; i < stopIndex; i++){
33702                 var cell = rows[i].childNodes[index].firstChild;
33703                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
33704             }
33705         }
33706         return maxWidth + /*margin for error in IE*/ 5;
33707     },
33708     /**
33709      * Autofit a column to its content.
33710      * @param {Number} colIndex
33711      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
33712      */
33713      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
33714          if(this.cm.isHidden(colIndex)){
33715              return; // can't calc a hidden column
33716          }
33717         if(forceMinSize){
33718             var cid = this.cm.getColumnId(colIndex);
33719             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
33720            if(this.grid.autoSizeHeaders){
33721                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
33722            }
33723         }
33724         var newWidth = this.calcColumnWidth(colIndex);
33725         this.cm.setColumnWidth(colIndex,
33726             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
33727         if(!suppressEvent){
33728             this.grid.fireEvent("columnresize", colIndex, newWidth);
33729         }
33730     },
33731
33732     /**
33733      * Autofits all columns to their content and then expands to fit any extra space in the grid
33734      */
33735      autoSizeColumns : function(){
33736         var cm = this.grid.colModel;
33737         var colCount = cm.getColumnCount();
33738         for(var i = 0; i < colCount; i++){
33739             this.autoSizeColumn(i, true, true);
33740         }
33741         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
33742             this.fitColumns();
33743         }else{
33744             this.updateColumns();
33745             this.layout();
33746         }
33747     },
33748
33749     /**
33750      * Autofits all columns to the grid's width proportionate with their current size
33751      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
33752      */
33753     fitColumns : function(reserveScrollSpace){
33754         var cm = this.grid.colModel;
33755         var colCount = cm.getColumnCount();
33756         var cols = [];
33757         var width = 0;
33758         var i, w;
33759         for (i = 0; i < colCount; i++){
33760             if(!cm.isHidden(i) && !cm.isFixed(i)){
33761                 w = cm.getColumnWidth(i);
33762                 cols.push(i);
33763                 cols.push(w);
33764                 width += w;
33765             }
33766         }
33767         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
33768         if(reserveScrollSpace){
33769             avail -= 17;
33770         }
33771         var frac = (avail - cm.getTotalWidth())/width;
33772         while (cols.length){
33773             w = cols.pop();
33774             i = cols.pop();
33775             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
33776         }
33777         this.updateColumns();
33778         this.layout();
33779     },
33780
33781     onRowSelect : function(rowIndex){
33782         var row = this.getRowComposite(rowIndex);
33783         row.addClass("x-grid-row-selected");
33784     },
33785
33786     onRowDeselect : function(rowIndex){
33787         var row = this.getRowComposite(rowIndex);
33788         row.removeClass("x-grid-row-selected");
33789     },
33790
33791     onCellSelect : function(row, col){
33792         var cell = this.getCell(row, col);
33793         if(cell){
33794             Roo.fly(cell).addClass("x-grid-cell-selected");
33795         }
33796     },
33797
33798     onCellDeselect : function(row, col){
33799         var cell = this.getCell(row, col);
33800         if(cell){
33801             Roo.fly(cell).removeClass("x-grid-cell-selected");
33802         }
33803     },
33804
33805     updateHeaderSortState : function(){
33806         var state = this.ds.getSortState();
33807         if(!state){
33808             return;
33809         }
33810         this.sortState = state;
33811         var sortColumn = this.cm.findColumnIndex(state.field);
33812         if(sortColumn != -1){
33813             var sortDir = state.direction;
33814             var sc = this.sortClasses;
33815             var hds = this.el.select(this.headerSelector).removeClass(sc);
33816             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
33817         }
33818     },
33819
33820     handleHeaderClick : function(g, index){
33821         if(this.headersDisabled){
33822             return;
33823         }
33824         var dm = g.dataSource, cm = g.colModel;
33825             if(!cm.isSortable(index)){
33826             return;
33827         }
33828             g.stopEditing();
33829         dm.sort(cm.getDataIndex(index));
33830     },
33831
33832
33833     destroy : function(){
33834         if(this.colMenu){
33835             this.colMenu.removeAll();
33836             Roo.menu.MenuMgr.unregister(this.colMenu);
33837             this.colMenu.getEl().remove();
33838             delete this.colMenu;
33839         }
33840         if(this.hmenu){
33841             this.hmenu.removeAll();
33842             Roo.menu.MenuMgr.unregister(this.hmenu);
33843             this.hmenu.getEl().remove();
33844             delete this.hmenu;
33845         }
33846         if(this.grid.enableColumnMove){
33847             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33848             if(dds){
33849                 for(var dd in dds){
33850                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
33851                         var elid = dds[dd].dragElId;
33852                         dds[dd].unreg();
33853                         Roo.get(elid).remove();
33854                     } else if(dds[dd].config.isTarget){
33855                         dds[dd].proxyTop.remove();
33856                         dds[dd].proxyBottom.remove();
33857                         dds[dd].unreg();
33858                     }
33859                     if(Roo.dd.DDM.locationCache[dd]){
33860                         delete Roo.dd.DDM.locationCache[dd];
33861                     }
33862                 }
33863                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
33864             }
33865         }
33866         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
33867         this.bind(null, null);
33868         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
33869     },
33870
33871     handleLockChange : function(){
33872         this.refresh(true);
33873     },
33874
33875     onDenyColumnLock : function(){
33876
33877     },
33878
33879     onDenyColumnHide : function(){
33880
33881     },
33882
33883     handleHdMenuClick : function(item){
33884         var index = this.hdCtxIndex;
33885         var cm = this.cm, ds = this.ds;
33886         switch(item.id){
33887             case "asc":
33888                 ds.sort(cm.getDataIndex(index), "ASC");
33889                 break;
33890             case "desc":
33891                 ds.sort(cm.getDataIndex(index), "DESC");
33892                 break;
33893             case "lock":
33894                 var lc = cm.getLockedCount();
33895                 if(cm.getColumnCount(true) <= lc+1){
33896                     this.onDenyColumnLock();
33897                     return;
33898                 }
33899                 if(lc != index){
33900                     cm.setLocked(index, true, true);
33901                     cm.moveColumn(index, lc);
33902                     this.grid.fireEvent("columnmove", index, lc);
33903                 }else{
33904                     cm.setLocked(index, true);
33905                 }
33906             break;
33907             case "unlock":
33908                 var lc = cm.getLockedCount();
33909                 if((lc-1) != index){
33910                     cm.setLocked(index, false, true);
33911                     cm.moveColumn(index, lc-1);
33912                     this.grid.fireEvent("columnmove", index, lc-1);
33913                 }else{
33914                     cm.setLocked(index, false);
33915                 }
33916             break;
33917             default:
33918                 index = cm.getIndexById(item.id.substr(4));
33919                 if(index != -1){
33920                     if(item.checked && cm.getColumnCount(true) <= 1){
33921                         this.onDenyColumnHide();
33922                         return false;
33923                     }
33924                     cm.setHidden(index, item.checked);
33925                 }
33926         }
33927         return true;
33928     },
33929
33930     beforeColMenuShow : function(){
33931         var cm = this.cm,  colCount = cm.getColumnCount();
33932         this.colMenu.removeAll();
33933         for(var i = 0; i < colCount; i++){
33934             this.colMenu.add(new Roo.menu.CheckItem({
33935                 id: "col-"+cm.getColumnId(i),
33936                 text: cm.getColumnHeader(i),
33937                 checked: !cm.isHidden(i),
33938                 hideOnClick:false
33939             }));
33940         }
33941     },
33942
33943     handleHdCtx : function(g, index, e){
33944         e.stopEvent();
33945         var hd = this.getHeaderCell(index);
33946         this.hdCtxIndex = index;
33947         var ms = this.hmenu.items, cm = this.cm;
33948         ms.get("asc").setDisabled(!cm.isSortable(index));
33949         ms.get("desc").setDisabled(!cm.isSortable(index));
33950         if(this.grid.enableColLock !== false){
33951             ms.get("lock").setDisabled(cm.isLocked(index));
33952             ms.get("unlock").setDisabled(!cm.isLocked(index));
33953         }
33954         this.hmenu.show(hd, "tl-bl");
33955     },
33956
33957     handleHdOver : function(e){
33958         var hd = this.findHeaderCell(e.getTarget());
33959         if(hd && !this.headersDisabled){
33960             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
33961                this.fly(hd).addClass("x-grid-hd-over");
33962             }
33963         }
33964     },
33965
33966     handleHdOut : function(e){
33967         var hd = this.findHeaderCell(e.getTarget());
33968         if(hd){
33969             this.fly(hd).removeClass("x-grid-hd-over");
33970         }
33971     },
33972
33973     handleSplitDblClick : function(e, t){
33974         var i = this.getCellIndex(t);
33975         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
33976             this.autoSizeColumn(i, true);
33977             this.layout();
33978         }
33979     },
33980
33981     render : function(){
33982
33983         var cm = this.cm;
33984         var colCount = cm.getColumnCount();
33985
33986         if(this.grid.monitorWindowResize === true){
33987             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33988         }
33989         var header = this.renderHeaders();
33990         var body = this.templates.body.apply({rows:""});
33991         var html = this.templates.master.apply({
33992             lockedBody: body,
33993             body: body,
33994             lockedHeader: header[0],
33995             header: header[1]
33996         });
33997
33998         //this.updateColumns();
33999
34000         this.grid.getGridEl().dom.innerHTML = html;
34001
34002         this.initElements();
34003         
34004         // a kludge to fix the random scolling effect in webkit
34005         this.el.on("scroll", function() {
34006             this.el.dom.scrollTop=0; // hopefully not recursive..
34007         },this);
34008
34009         this.scroller.on("scroll", this.handleScroll, this);
34010         this.lockedBody.on("mousewheel", this.handleWheel, this);
34011         this.mainBody.on("mousewheel", this.handleWheel, this);
34012
34013         this.mainHd.on("mouseover", this.handleHdOver, this);
34014         this.mainHd.on("mouseout", this.handleHdOut, this);
34015         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34016                 {delegate: "."+this.splitClass});
34017
34018         this.lockedHd.on("mouseover", this.handleHdOver, this);
34019         this.lockedHd.on("mouseout", this.handleHdOut, this);
34020         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34021                 {delegate: "."+this.splitClass});
34022
34023         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34024             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34025         }
34026
34027         this.updateSplitters();
34028
34029         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34030             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34031             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34032         }
34033
34034         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34035             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34036             this.hmenu.add(
34037                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34038                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34039             );
34040             if(this.grid.enableColLock !== false){
34041                 this.hmenu.add('-',
34042                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34043                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34044                 );
34045             }
34046             if(this.grid.enableColumnHide !== false){
34047
34048                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34049                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34050                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34051
34052                 this.hmenu.add('-',
34053                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34054                 );
34055             }
34056             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34057
34058             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34059         }
34060
34061         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34062             this.dd = new Roo.grid.GridDragZone(this.grid, {
34063                 ddGroup : this.grid.ddGroup || 'GridDD'
34064             });
34065         }
34066
34067         /*
34068         for(var i = 0; i < colCount; i++){
34069             if(cm.isHidden(i)){
34070                 this.hideColumn(i);
34071             }
34072             if(cm.config[i].align){
34073                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34074                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34075             }
34076         }*/
34077         
34078         this.updateHeaderSortState();
34079
34080         this.beforeInitialResize();
34081         this.layout(true);
34082
34083         // two part rendering gives faster view to the user
34084         this.renderPhase2.defer(1, this);
34085     },
34086
34087     renderPhase2 : function(){
34088         // render the rows now
34089         this.refresh();
34090         if(this.grid.autoSizeColumns){
34091             this.autoSizeColumns();
34092         }
34093     },
34094
34095     beforeInitialResize : function(){
34096
34097     },
34098
34099     onColumnSplitterMoved : function(i, w){
34100         this.userResized = true;
34101         var cm = this.grid.colModel;
34102         cm.setColumnWidth(i, w, true);
34103         var cid = cm.getColumnId(i);
34104         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34105         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34106         this.updateSplitters();
34107         this.layout();
34108         this.grid.fireEvent("columnresize", i, w);
34109     },
34110
34111     syncRowHeights : function(startIndex, endIndex){
34112         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34113             startIndex = startIndex || 0;
34114             var mrows = this.getBodyTable().rows;
34115             var lrows = this.getLockedTable().rows;
34116             var len = mrows.length-1;
34117             endIndex = Math.min(endIndex || len, len);
34118             for(var i = startIndex; i <= endIndex; i++){
34119                 var m = mrows[i], l = lrows[i];
34120                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34121                 m.style.height = l.style.height = h + "px";
34122             }
34123         }
34124     },
34125
34126     layout : function(initialRender, is2ndPass){
34127         var g = this.grid;
34128         var auto = g.autoHeight;
34129         var scrollOffset = 16;
34130         var c = g.getGridEl(), cm = this.cm,
34131                 expandCol = g.autoExpandColumn,
34132                 gv = this;
34133         //c.beginMeasure();
34134
34135         if(!c.dom.offsetWidth){ // display:none?
34136             if(initialRender){
34137                 this.lockedWrap.show();
34138                 this.mainWrap.show();
34139             }
34140             return;
34141         }
34142
34143         var hasLock = this.cm.isLocked(0);
34144
34145         var tbh = this.headerPanel.getHeight();
34146         var bbh = this.footerPanel.getHeight();
34147
34148         if(auto){
34149             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34150             var newHeight = ch + c.getBorderWidth("tb");
34151             if(g.maxHeight){
34152                 newHeight = Math.min(g.maxHeight, newHeight);
34153             }
34154             c.setHeight(newHeight);
34155         }
34156
34157         if(g.autoWidth){
34158             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34159         }
34160
34161         var s = this.scroller;
34162
34163         var csize = c.getSize(true);
34164
34165         this.el.setSize(csize.width, csize.height);
34166
34167         this.headerPanel.setWidth(csize.width);
34168         this.footerPanel.setWidth(csize.width);
34169
34170         var hdHeight = this.mainHd.getHeight();
34171         var vw = csize.width;
34172         var vh = csize.height - (tbh + bbh);
34173
34174         s.setSize(vw, vh);
34175
34176         var bt = this.getBodyTable();
34177         var ltWidth = hasLock ?
34178                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34179
34180         var scrollHeight = bt.offsetHeight;
34181         var scrollWidth = ltWidth + bt.offsetWidth;
34182         var vscroll = false, hscroll = false;
34183
34184         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34185
34186         var lw = this.lockedWrap, mw = this.mainWrap;
34187         var lb = this.lockedBody, mb = this.mainBody;
34188
34189         setTimeout(function(){
34190             var t = s.dom.offsetTop;
34191             var w = s.dom.clientWidth,
34192                 h = s.dom.clientHeight;
34193
34194             lw.setTop(t);
34195             lw.setSize(ltWidth, h);
34196
34197             mw.setLeftTop(ltWidth, t);
34198             mw.setSize(w-ltWidth, h);
34199
34200             lb.setHeight(h-hdHeight);
34201             mb.setHeight(h-hdHeight);
34202
34203             if(is2ndPass !== true && !gv.userResized && expandCol){
34204                 // high speed resize without full column calculation
34205                 
34206                 var ci = cm.getIndexById(expandCol);
34207                 if (ci < 0) {
34208                     ci = cm.findColumnIndex(expandCol);
34209                 }
34210                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34211                 var expandId = cm.getColumnId(ci);
34212                 var  tw = cm.getTotalWidth(false);
34213                 var currentWidth = cm.getColumnWidth(ci);
34214                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34215                 if(currentWidth != cw){
34216                     cm.setColumnWidth(ci, cw, true);
34217                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34218                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34219                     gv.updateSplitters();
34220                     gv.layout(false, true);
34221                 }
34222             }
34223
34224             if(initialRender){
34225                 lw.show();
34226                 mw.show();
34227             }
34228             //c.endMeasure();
34229         }, 10);
34230     },
34231
34232     onWindowResize : function(){
34233         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34234             return;
34235         }
34236         this.layout();
34237     },
34238
34239     appendFooter : function(parentEl){
34240         return null;
34241     },
34242
34243     sortAscText : "Sort Ascending",
34244     sortDescText : "Sort Descending",
34245     lockText : "Lock Column",
34246     unlockText : "Unlock Column",
34247     columnsText : "Columns"
34248 });
34249
34250
34251 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34252     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34253     this.proxy.el.addClass('x-grid3-col-dd');
34254 };
34255
34256 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34257     handleMouseDown : function(e){
34258
34259     },
34260
34261     callHandleMouseDown : function(e){
34262         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34263     }
34264 });
34265 /*
34266  * Based on:
34267  * Ext JS Library 1.1.1
34268  * Copyright(c) 2006-2007, Ext JS, LLC.
34269  *
34270  * Originally Released Under LGPL - original licence link has changed is not relivant.
34271  *
34272  * Fork - LGPL
34273  * <script type="text/javascript">
34274  */
34275  
34276 // private
34277 // This is a support class used internally by the Grid components
34278 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34279     this.grid = grid;
34280     this.view = grid.getView();
34281     this.proxy = this.view.resizeProxy;
34282     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34283         "gridSplitters" + this.grid.getGridEl().id, {
34284         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34285     });
34286     this.setHandleElId(Roo.id(hd));
34287     this.setOuterHandleElId(Roo.id(hd2));
34288     this.scroll = false;
34289 };
34290 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34291     fly: Roo.Element.fly,
34292
34293     b4StartDrag : function(x, y){
34294         this.view.headersDisabled = true;
34295         this.proxy.setHeight(this.view.mainWrap.getHeight());
34296         var w = this.cm.getColumnWidth(this.cellIndex);
34297         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34298         this.resetConstraints();
34299         this.setXConstraint(minw, 1000);
34300         this.setYConstraint(0, 0);
34301         this.minX = x - minw;
34302         this.maxX = x + 1000;
34303         this.startPos = x;
34304         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34305     },
34306
34307
34308     handleMouseDown : function(e){
34309         ev = Roo.EventObject.setEvent(e);
34310         var t = this.fly(ev.getTarget());
34311         if(t.hasClass("x-grid-split")){
34312             this.cellIndex = this.view.getCellIndex(t.dom);
34313             this.split = t.dom;
34314             this.cm = this.grid.colModel;
34315             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34316                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34317             }
34318         }
34319     },
34320
34321     endDrag : function(e){
34322         this.view.headersDisabled = false;
34323         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34324         var diff = endX - this.startPos;
34325         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34326     },
34327
34328     autoOffset : function(){
34329         this.setDelta(0,0);
34330     }
34331 });/*
34332  * Based on:
34333  * Ext JS Library 1.1.1
34334  * Copyright(c) 2006-2007, Ext JS, LLC.
34335  *
34336  * Originally Released Under LGPL - original licence link has changed is not relivant.
34337  *
34338  * Fork - LGPL
34339  * <script type="text/javascript">
34340  */
34341  
34342 // private
34343 // This is a support class used internally by the Grid components
34344 Roo.grid.GridDragZone = function(grid, config){
34345     this.view = grid.getView();
34346     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34347     if(this.view.lockedBody){
34348         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34349         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34350     }
34351     this.scroll = false;
34352     this.grid = grid;
34353     this.ddel = document.createElement('div');
34354     this.ddel.className = 'x-grid-dd-wrap';
34355 };
34356
34357 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34358     ddGroup : "GridDD",
34359
34360     getDragData : function(e){
34361         var t = Roo.lib.Event.getTarget(e);
34362         var rowIndex = this.view.findRowIndex(t);
34363         if(rowIndex !== false){
34364             var sm = this.grid.selModel;
34365             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34366               //  sm.mouseDown(e, t);
34367             //}
34368             if (e.hasModifier()){
34369                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34370             }
34371             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
34372         }
34373         return false;
34374     },
34375
34376     onInitDrag : function(e){
34377         var data = this.dragData;
34378         this.ddel.innerHTML = this.grid.getDragDropText();
34379         this.proxy.update(this.ddel);
34380         // fire start drag?
34381     },
34382
34383     afterRepair : function(){
34384         this.dragging = false;
34385     },
34386
34387     getRepairXY : function(e, data){
34388         return false;
34389     },
34390
34391     onEndDrag : function(data, e){
34392         // fire end drag?
34393     },
34394
34395     onValidDrop : function(dd, e, id){
34396         // fire drag drop?
34397         this.hideProxy();
34398     },
34399
34400     beforeInvalidDrop : function(e, id){
34401
34402     }
34403 });/*
34404  * Based on:
34405  * Ext JS Library 1.1.1
34406  * Copyright(c) 2006-2007, Ext JS, LLC.
34407  *
34408  * Originally Released Under LGPL - original licence link has changed is not relivant.
34409  *
34410  * Fork - LGPL
34411  * <script type="text/javascript">
34412  */
34413  
34414
34415 /**
34416  * @class Roo.grid.ColumnModel
34417  * @extends Roo.util.Observable
34418  * This is the default implementation of a ColumnModel used by the Grid. It defines
34419  * the columns in the grid.
34420  * <br>Usage:<br>
34421  <pre><code>
34422  var colModel = new Roo.grid.ColumnModel([
34423         {header: "Ticker", width: 60, sortable: true, locked: true},
34424         {header: "Company Name", width: 150, sortable: true},
34425         {header: "Market Cap.", width: 100, sortable: true},
34426         {header: "$ Sales", width: 100, sortable: true, renderer: money},
34427         {header: "Employees", width: 100, sortable: true, resizable: false}
34428  ]);
34429  </code></pre>
34430  * <p>
34431  
34432  * The config options listed for this class are options which may appear in each
34433  * individual column definition.
34434  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
34435  * @constructor
34436  * @param {Object} config An Array of column config objects. See this class's
34437  * config objects for details.
34438 */
34439 Roo.grid.ColumnModel = function(config){
34440         /**
34441      * The config passed into the constructor
34442      */
34443     this.config = config;
34444     this.lookup = {};
34445
34446     // if no id, create one
34447     // if the column does not have a dataIndex mapping,
34448     // map it to the order it is in the config
34449     for(var i = 0, len = config.length; i < len; i++){
34450         var c = config[i];
34451         if(typeof c.dataIndex == "undefined"){
34452             c.dataIndex = i;
34453         }
34454         if(typeof c.renderer == "string"){
34455             c.renderer = Roo.util.Format[c.renderer];
34456         }
34457         if(typeof c.id == "undefined"){
34458             c.id = Roo.id();
34459         }
34460         if(c.editor && c.editor.xtype){
34461             c.editor  = Roo.factory(c.editor, Roo.grid);
34462         }
34463         if(c.editor && c.editor.isFormField){
34464             c.editor = new Roo.grid.GridEditor(c.editor);
34465         }
34466         this.lookup[c.id] = c;
34467     }
34468
34469     /**
34470      * The width of columns which have no width specified (defaults to 100)
34471      * @type Number
34472      */
34473     this.defaultWidth = 100;
34474
34475     /**
34476      * Default sortable of columns which have no sortable specified (defaults to false)
34477      * @type Boolean
34478      */
34479     this.defaultSortable = false;
34480
34481     this.addEvents({
34482         /**
34483              * @event widthchange
34484              * Fires when the width of a column changes.
34485              * @param {ColumnModel} this
34486              * @param {Number} columnIndex The column index
34487              * @param {Number} newWidth The new width
34488              */
34489             "widthchange": true,
34490         /**
34491              * @event headerchange
34492              * Fires when the text of a header changes.
34493              * @param {ColumnModel} this
34494              * @param {Number} columnIndex The column index
34495              * @param {Number} newText The new header text
34496              */
34497             "headerchange": true,
34498         /**
34499              * @event hiddenchange
34500              * Fires when a column is hidden or "unhidden".
34501              * @param {ColumnModel} this
34502              * @param {Number} columnIndex The column index
34503              * @param {Boolean} hidden true if hidden, false otherwise
34504              */
34505             "hiddenchange": true,
34506             /**
34507          * @event columnmoved
34508          * Fires when a column is moved.
34509          * @param {ColumnModel} this
34510          * @param {Number} oldIndex
34511          * @param {Number} newIndex
34512          */
34513         "columnmoved" : true,
34514         /**
34515          * @event columlockchange
34516          * Fires when a column's locked state is changed
34517          * @param {ColumnModel} this
34518          * @param {Number} colIndex
34519          * @param {Boolean} locked true if locked
34520          */
34521         "columnlockchange" : true
34522     });
34523     Roo.grid.ColumnModel.superclass.constructor.call(this);
34524 };
34525 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
34526     /**
34527      * @cfg {String} header The header text to display in the Grid view.
34528      */
34529     /**
34530      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
34531      * {@link Roo.data.Record} definition from which to draw the column's value. If not
34532      * specified, the column's index is used as an index into the Record's data Array.
34533      */
34534     /**
34535      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
34536      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
34537      */
34538     /**
34539      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
34540      * Defaults to the value of the {@link #defaultSortable} property.
34541      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
34542      */
34543     /**
34544      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
34545      */
34546     /**
34547      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
34548      */
34549     /**
34550      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
34551      */
34552     /**
34553      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
34554      */
34555     /**
34556      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
34557      * given the cell's data value. See {@link #setRenderer}. If not specified, the
34558      * default renderer uses the raw data value.
34559      */
34560        /**
34561      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
34562      */
34563     /**
34564      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
34565      */
34566
34567     /**
34568      * Returns the id of the column at the specified index.
34569      * @param {Number} index The column index
34570      * @return {String} the id
34571      */
34572     getColumnId : function(index){
34573         return this.config[index].id;
34574     },
34575
34576     /**
34577      * Returns the column for a specified id.
34578      * @param {String} id The column id
34579      * @return {Object} the column
34580      */
34581     getColumnById : function(id){
34582         return this.lookup[id];
34583     },
34584
34585     
34586     /**
34587      * Returns the column for a specified dataIndex.
34588      * @param {String} dataIndex The column dataIndex
34589      * @return {Object|Boolean} the column or false if not found
34590      */
34591     getColumnByDataIndex: function(dataIndex){
34592         var index = this.findColumnIndex(dataIndex);
34593         return index > -1 ? this.config[index] : false;
34594     },
34595     
34596     /**
34597      * Returns the index for a specified column id.
34598      * @param {String} id The column id
34599      * @return {Number} the index, or -1 if not found
34600      */
34601     getIndexById : function(id){
34602         for(var i = 0, len = this.config.length; i < len; i++){
34603             if(this.config[i].id == id){
34604                 return i;
34605             }
34606         }
34607         return -1;
34608     },
34609     
34610     /**
34611      * Returns the index for a specified column dataIndex.
34612      * @param {String} dataIndex The column dataIndex
34613      * @return {Number} the index, or -1 if not found
34614      */
34615     
34616     findColumnIndex : function(dataIndex){
34617         for(var i = 0, len = this.config.length; i < len; i++){
34618             if(this.config[i].dataIndex == dataIndex){
34619                 return i;
34620             }
34621         }
34622         return -1;
34623     },
34624     
34625     
34626     moveColumn : function(oldIndex, newIndex){
34627         var c = this.config[oldIndex];
34628         this.config.splice(oldIndex, 1);
34629         this.config.splice(newIndex, 0, c);
34630         this.dataMap = null;
34631         this.fireEvent("columnmoved", this, oldIndex, newIndex);
34632     },
34633
34634     isLocked : function(colIndex){
34635         return this.config[colIndex].locked === true;
34636     },
34637
34638     setLocked : function(colIndex, value, suppressEvent){
34639         if(this.isLocked(colIndex) == value){
34640             return;
34641         }
34642         this.config[colIndex].locked = value;
34643         if(!suppressEvent){
34644             this.fireEvent("columnlockchange", this, colIndex, value);
34645         }
34646     },
34647
34648     getTotalLockedWidth : function(){
34649         var totalWidth = 0;
34650         for(var i = 0; i < this.config.length; i++){
34651             if(this.isLocked(i) && !this.isHidden(i)){
34652                 this.totalWidth += this.getColumnWidth(i);
34653             }
34654         }
34655         return totalWidth;
34656     },
34657
34658     getLockedCount : function(){
34659         for(var i = 0, len = this.config.length; i < len; i++){
34660             if(!this.isLocked(i)){
34661                 return i;
34662             }
34663         }
34664     },
34665
34666     /**
34667      * Returns the number of columns.
34668      * @return {Number}
34669      */
34670     getColumnCount : function(visibleOnly){
34671         if(visibleOnly === true){
34672             var c = 0;
34673             for(var i = 0, len = this.config.length; i < len; i++){
34674                 if(!this.isHidden(i)){
34675                     c++;
34676                 }
34677             }
34678             return c;
34679         }
34680         return this.config.length;
34681     },
34682
34683     /**
34684      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
34685      * @param {Function} fn
34686      * @param {Object} scope (optional)
34687      * @return {Array} result
34688      */
34689     getColumnsBy : function(fn, scope){
34690         var r = [];
34691         for(var i = 0, len = this.config.length; i < len; i++){
34692             var c = this.config[i];
34693             if(fn.call(scope||this, c, i) === true){
34694                 r[r.length] = c;
34695             }
34696         }
34697         return r;
34698     },
34699
34700     /**
34701      * Returns true if the specified column is sortable.
34702      * @param {Number} col The column index
34703      * @return {Boolean}
34704      */
34705     isSortable : function(col){
34706         if(typeof this.config[col].sortable == "undefined"){
34707             return this.defaultSortable;
34708         }
34709         return this.config[col].sortable;
34710     },
34711
34712     /**
34713      * Returns the rendering (formatting) function defined for the column.
34714      * @param {Number} col The column index.
34715      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
34716      */
34717     getRenderer : function(col){
34718         if(!this.config[col].renderer){
34719             return Roo.grid.ColumnModel.defaultRenderer;
34720         }
34721         return this.config[col].renderer;
34722     },
34723
34724     /**
34725      * Sets the rendering (formatting) function for a column.
34726      * @param {Number} col The column index
34727      * @param {Function} fn The function to use to process the cell's raw data
34728      * to return HTML markup for the grid view. The render function is called with
34729      * the following parameters:<ul>
34730      * <li>Data value.</li>
34731      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
34732      * <li>css A CSS style string to apply to the table cell.</li>
34733      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
34734      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
34735      * <li>Row index</li>
34736      * <li>Column index</li>
34737      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
34738      */
34739     setRenderer : function(col, fn){
34740         this.config[col].renderer = fn;
34741     },
34742
34743     /**
34744      * Returns the width for the specified column.
34745      * @param {Number} col The column index
34746      * @return {Number}
34747      */
34748     getColumnWidth : function(col){
34749         return this.config[col].width || this.defaultWidth;
34750     },
34751
34752     /**
34753      * Sets the width for a column.
34754      * @param {Number} col The column index
34755      * @param {Number} width The new width
34756      */
34757     setColumnWidth : function(col, width, suppressEvent){
34758         this.config[col].width = width;
34759         this.totalWidth = null;
34760         if(!suppressEvent){
34761              this.fireEvent("widthchange", this, col, width);
34762         }
34763     },
34764
34765     /**
34766      * Returns the total width of all columns.
34767      * @param {Boolean} includeHidden True to include hidden column widths
34768      * @return {Number}
34769      */
34770     getTotalWidth : function(includeHidden){
34771         if(!this.totalWidth){
34772             this.totalWidth = 0;
34773             for(var i = 0, len = this.config.length; i < len; i++){
34774                 if(includeHidden || !this.isHidden(i)){
34775                     this.totalWidth += this.getColumnWidth(i);
34776                 }
34777             }
34778         }
34779         return this.totalWidth;
34780     },
34781
34782     /**
34783      * Returns the header for the specified column.
34784      * @param {Number} col The column index
34785      * @return {String}
34786      */
34787     getColumnHeader : function(col){
34788         return this.config[col].header;
34789     },
34790
34791     /**
34792      * Sets the header for a column.
34793      * @param {Number} col The column index
34794      * @param {String} header The new header
34795      */
34796     setColumnHeader : function(col, header){
34797         this.config[col].header = header;
34798         this.fireEvent("headerchange", this, col, header);
34799     },
34800
34801     /**
34802      * Returns the tooltip for the specified column.
34803      * @param {Number} col The column index
34804      * @return {String}
34805      */
34806     getColumnTooltip : function(col){
34807             return this.config[col].tooltip;
34808     },
34809     /**
34810      * Sets the tooltip for a column.
34811      * @param {Number} col The column index
34812      * @param {String} tooltip The new tooltip
34813      */
34814     setColumnTooltip : function(col, tooltip){
34815             this.config[col].tooltip = tooltip;
34816     },
34817
34818     /**
34819      * Returns the dataIndex for the specified column.
34820      * @param {Number} col The column index
34821      * @return {Number}
34822      */
34823     getDataIndex : function(col){
34824         return this.config[col].dataIndex;
34825     },
34826
34827     /**
34828      * Sets the dataIndex for a column.
34829      * @param {Number} col The column index
34830      * @param {Number} dataIndex The new dataIndex
34831      */
34832     setDataIndex : function(col, dataIndex){
34833         this.config[col].dataIndex = dataIndex;
34834     },
34835
34836     
34837     
34838     /**
34839      * Returns true if the cell is editable.
34840      * @param {Number} colIndex The column index
34841      * @param {Number} rowIndex The row index
34842      * @return {Boolean}
34843      */
34844     isCellEditable : function(colIndex, rowIndex){
34845         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
34846     },
34847
34848     /**
34849      * Returns the editor defined for the cell/column.
34850      * return false or null to disable editing.
34851      * @param {Number} colIndex The column index
34852      * @param {Number} rowIndex The row index
34853      * @return {Object}
34854      */
34855     getCellEditor : function(colIndex, rowIndex){
34856         return this.config[colIndex].editor;
34857     },
34858
34859     /**
34860      * Sets if a column is editable.
34861      * @param {Number} col The column index
34862      * @param {Boolean} editable True if the column is editable
34863      */
34864     setEditable : function(col, editable){
34865         this.config[col].editable = editable;
34866     },
34867
34868
34869     /**
34870      * Returns true if the column is hidden.
34871      * @param {Number} colIndex The column index
34872      * @return {Boolean}
34873      */
34874     isHidden : function(colIndex){
34875         return this.config[colIndex].hidden;
34876     },
34877
34878
34879     /**
34880      * Returns true if the column width cannot be changed
34881      */
34882     isFixed : function(colIndex){
34883         return this.config[colIndex].fixed;
34884     },
34885
34886     /**
34887      * Returns true if the column can be resized
34888      * @return {Boolean}
34889      */
34890     isResizable : function(colIndex){
34891         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
34892     },
34893     /**
34894      * Sets if a column is hidden.
34895      * @param {Number} colIndex The column index
34896      * @param {Boolean} hidden True if the column is hidden
34897      */
34898     setHidden : function(colIndex, hidden){
34899         this.config[colIndex].hidden = hidden;
34900         this.totalWidth = null;
34901         this.fireEvent("hiddenchange", this, colIndex, hidden);
34902     },
34903
34904     /**
34905      * Sets the editor for a column.
34906      * @param {Number} col The column index
34907      * @param {Object} editor The editor object
34908      */
34909     setEditor : function(col, editor){
34910         this.config[col].editor = editor;
34911     }
34912 });
34913
34914 Roo.grid.ColumnModel.defaultRenderer = function(value){
34915         if(typeof value == "string" && value.length < 1){
34916             return "&#160;";
34917         }
34918         return value;
34919 };
34920
34921 // Alias for backwards compatibility
34922 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
34923 /*
34924  * Based on:
34925  * Ext JS Library 1.1.1
34926  * Copyright(c) 2006-2007, Ext JS, LLC.
34927  *
34928  * Originally Released Under LGPL - original licence link has changed is not relivant.
34929  *
34930  * Fork - LGPL
34931  * <script type="text/javascript">
34932  */
34933
34934 /**
34935  * @class Roo.grid.AbstractSelectionModel
34936  * @extends Roo.util.Observable
34937  * Abstract base class for grid SelectionModels.  It provides the interface that should be
34938  * implemented by descendant classes.  This class should not be directly instantiated.
34939  * @constructor
34940  */
34941 Roo.grid.AbstractSelectionModel = function(){
34942     this.locked = false;
34943     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
34944 };
34945
34946 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
34947     /** @ignore Called by the grid automatically. Do not call directly. */
34948     init : function(grid){
34949         this.grid = grid;
34950         this.initEvents();
34951     },
34952
34953     /**
34954      * Locks the selections.
34955      */
34956     lock : function(){
34957         this.locked = true;
34958     },
34959
34960     /**
34961      * Unlocks the selections.
34962      */
34963     unlock : function(){
34964         this.locked = false;
34965     },
34966
34967     /**
34968      * Returns true if the selections are locked.
34969      * @return {Boolean}
34970      */
34971     isLocked : function(){
34972         return this.locked;
34973     }
34974 });/*
34975  * Based on:
34976  * Ext JS Library 1.1.1
34977  * Copyright(c) 2006-2007, Ext JS, LLC.
34978  *
34979  * Originally Released Under LGPL - original licence link has changed is not relivant.
34980  *
34981  * Fork - LGPL
34982  * <script type="text/javascript">
34983  */
34984 /**
34985  * @extends Roo.grid.AbstractSelectionModel
34986  * @class Roo.grid.RowSelectionModel
34987  * The default SelectionModel used by {@link Roo.grid.Grid}.
34988  * It supports multiple selections and keyboard selection/navigation. 
34989  * @constructor
34990  * @param {Object} config
34991  */
34992 Roo.grid.RowSelectionModel = function(config){
34993     Roo.apply(this, config);
34994     this.selections = new Roo.util.MixedCollection(false, function(o){
34995         return o.id;
34996     });
34997
34998     this.last = false;
34999     this.lastActive = false;
35000
35001     this.addEvents({
35002         /**
35003              * @event selectionchange
35004              * Fires when the selection changes
35005              * @param {SelectionModel} this
35006              */
35007             "selectionchange" : true,
35008         /**
35009              * @event afterselectionchange
35010              * Fires after the selection changes (eg. by key press or clicking)
35011              * @param {SelectionModel} this
35012              */
35013             "afterselectionchange" : true,
35014         /**
35015              * @event beforerowselect
35016              * Fires when a row is selected being selected, return false to cancel.
35017              * @param {SelectionModel} this
35018              * @param {Number} rowIndex The selected index
35019              * @param {Boolean} keepExisting False if other selections will be cleared
35020              */
35021             "beforerowselect" : true,
35022         /**
35023              * @event rowselect
35024              * Fires when a row is selected.
35025              * @param {SelectionModel} this
35026              * @param {Number} rowIndex The selected index
35027              * @param {Roo.data.Record} r The record
35028              */
35029             "rowselect" : true,
35030         /**
35031              * @event rowdeselect
35032              * Fires when a row is deselected.
35033              * @param {SelectionModel} this
35034              * @param {Number} rowIndex The selected index
35035              */
35036         "rowdeselect" : true
35037     });
35038     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35039     this.locked = false;
35040 };
35041
35042 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35043     /**
35044      * @cfg {Boolean} singleSelect
35045      * True to allow selection of only one row at a time (defaults to false)
35046      */
35047     singleSelect : false,
35048
35049     // private
35050     initEvents : function(){
35051
35052         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35053             this.grid.on("mousedown", this.handleMouseDown, this);
35054         }else{ // allow click to work like normal
35055             this.grid.on("rowclick", this.handleDragableRowClick, this);
35056         }
35057
35058         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35059             "up" : function(e){
35060                 if(!e.shiftKey){
35061                     this.selectPrevious(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             "down" : function(e){
35075                 if(!e.shiftKey){
35076                     this.selectNext(e.shiftKey);
35077                 }else if(this.last !== false && this.lastActive !== false){
35078                     var last = this.last;
35079                     this.selectRange(this.last,  this.lastActive+1);
35080                     this.grid.getView().focusRow(this.lastActive);
35081                     if(last !== false){
35082                         this.last = last;
35083                     }
35084                 }else{
35085                     this.selectFirstRow();
35086                 }
35087                 this.fireEvent("afterselectionchange", this);
35088             },
35089             scope: this
35090         });
35091
35092         var view = this.grid.view;
35093         view.on("refresh", this.onRefresh, this);
35094         view.on("rowupdated", this.onRowUpdated, this);
35095         view.on("rowremoved", this.onRemove, this);
35096     },
35097
35098     // private
35099     onRefresh : function(){
35100         var ds = this.grid.dataSource, i, v = this.grid.view;
35101         var s = this.selections;
35102         s.each(function(r){
35103             if((i = ds.indexOfId(r.id)) != -1){
35104                 v.onRowSelect(i);
35105             }else{
35106                 s.remove(r);
35107             }
35108         });
35109     },
35110
35111     // private
35112     onRemove : function(v, index, r){
35113         this.selections.remove(r);
35114     },
35115
35116     // private
35117     onRowUpdated : function(v, index, r){
35118         if(this.isSelected(r)){
35119             v.onRowSelect(index);
35120         }
35121     },
35122
35123     /**
35124      * Select records.
35125      * @param {Array} records The records to select
35126      * @param {Boolean} keepExisting (optional) True to keep existing selections
35127      */
35128     selectRecords : function(records, keepExisting){
35129         if(!keepExisting){
35130             this.clearSelections();
35131         }
35132         var ds = this.grid.dataSource;
35133         for(var i = 0, len = records.length; i < len; i++){
35134             this.selectRow(ds.indexOf(records[i]), true);
35135         }
35136     },
35137
35138     /**
35139      * Gets the number of selected rows.
35140      * @return {Number}
35141      */
35142     getCount : function(){
35143         return this.selections.length;
35144     },
35145
35146     /**
35147      * Selects the first row in the grid.
35148      */
35149     selectFirstRow : function(){
35150         this.selectRow(0);
35151     },
35152
35153     /**
35154      * Select the last row.
35155      * @param {Boolean} keepExisting (optional) True to keep existing selections
35156      */
35157     selectLastRow : function(keepExisting){
35158         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35159     },
35160
35161     /**
35162      * Selects the row immediately following the last selected row.
35163      * @param {Boolean} keepExisting (optional) True to keep existing selections
35164      */
35165     selectNext : function(keepExisting){
35166         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35167             this.selectRow(this.last+1, keepExisting);
35168             this.grid.getView().focusRow(this.last);
35169         }
35170     },
35171
35172     /**
35173      * Selects the row that precedes the last selected row.
35174      * @param {Boolean} keepExisting (optional) True to keep existing selections
35175      */
35176     selectPrevious : function(keepExisting){
35177         if(this.last){
35178             this.selectRow(this.last-1, keepExisting);
35179             this.grid.getView().focusRow(this.last);
35180         }
35181     },
35182
35183     /**
35184      * Returns the selected records
35185      * @return {Array} Array of selected records
35186      */
35187     getSelections : function(){
35188         return [].concat(this.selections.items);
35189     },
35190
35191     /**
35192      * Returns the first selected record.
35193      * @return {Record}
35194      */
35195     getSelected : function(){
35196         return this.selections.itemAt(0);
35197     },
35198
35199
35200     /**
35201      * Clears all selections.
35202      */
35203     clearSelections : function(fast){
35204         if(this.locked) return;
35205         if(fast !== true){
35206             var ds = this.grid.dataSource;
35207             var s = this.selections;
35208             s.each(function(r){
35209                 this.deselectRow(ds.indexOfId(r.id));
35210             }, this);
35211             s.clear();
35212         }else{
35213             this.selections.clear();
35214         }
35215         this.last = false;
35216     },
35217
35218
35219     /**
35220      * Selects all rows.
35221      */
35222     selectAll : function(){
35223         if(this.locked) return;
35224         this.selections.clear();
35225         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35226             this.selectRow(i, true);
35227         }
35228     },
35229
35230     /**
35231      * Returns True if there is a selection.
35232      * @return {Boolean}
35233      */
35234     hasSelection : function(){
35235         return this.selections.length > 0;
35236     },
35237
35238     /**
35239      * Returns True if the specified row is selected.
35240      * @param {Number/Record} record The record or index of the record to check
35241      * @return {Boolean}
35242      */
35243     isSelected : function(index){
35244         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35245         return (r && this.selections.key(r.id) ? true : false);
35246     },
35247
35248     /**
35249      * Returns True if the specified record id is selected.
35250      * @param {String} id The id of record to check
35251      * @return {Boolean}
35252      */
35253     isIdSelected : function(id){
35254         return (this.selections.key(id) ? true : false);
35255     },
35256
35257     // private
35258     handleMouseDown : function(e, t){
35259         var view = this.grid.getView(), rowIndex;
35260         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35261             return;
35262         };
35263         if(e.shiftKey && this.last !== false){
35264             var last = this.last;
35265             this.selectRange(last, rowIndex, e.ctrlKey);
35266             this.last = last; // reset the last
35267             view.focusRow(rowIndex);
35268         }else{
35269             var isSelected = this.isSelected(rowIndex);
35270             if(e.button !== 0 && isSelected){
35271                 view.focusRow(rowIndex);
35272             }else if(e.ctrlKey && isSelected){
35273                 this.deselectRow(rowIndex);
35274             }else if(!isSelected){
35275                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35276                 view.focusRow(rowIndex);
35277             }
35278         }
35279         this.fireEvent("afterselectionchange", this);
35280     },
35281     // private
35282     handleDragableRowClick :  function(grid, rowIndex, e) 
35283     {
35284         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35285             this.selectRow(rowIndex, false);
35286             grid.view.focusRow(rowIndex);
35287              this.fireEvent("afterselectionchange", this);
35288         }
35289     },
35290     
35291     /**
35292      * Selects multiple rows.
35293      * @param {Array} rows Array of the indexes of the row to select
35294      * @param {Boolean} keepExisting (optional) True to keep existing selections
35295      */
35296     selectRows : function(rows, keepExisting){
35297         if(!keepExisting){
35298             this.clearSelections();
35299         }
35300         for(var i = 0, len = rows.length; i < len; i++){
35301             this.selectRow(rows[i], true);
35302         }
35303     },
35304
35305     /**
35306      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35307      * @param {Number} startRow The index of the first row in the range
35308      * @param {Number} endRow The index of the last row in the range
35309      * @param {Boolean} keepExisting (optional) True to retain existing selections
35310      */
35311     selectRange : function(startRow, endRow, keepExisting){
35312         if(this.locked) return;
35313         if(!keepExisting){
35314             this.clearSelections();
35315         }
35316         if(startRow <= endRow){
35317             for(var i = startRow; i <= endRow; i++){
35318                 this.selectRow(i, true);
35319             }
35320         }else{
35321             for(var i = startRow; i >= endRow; i--){
35322                 this.selectRow(i, true);
35323             }
35324         }
35325     },
35326
35327     /**
35328      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35329      * @param {Number} startRow The index of the first row in the range
35330      * @param {Number} endRow The index of the last row in the range
35331      */
35332     deselectRange : function(startRow, endRow, preventViewNotify){
35333         if(this.locked) return;
35334         for(var i = startRow; i <= endRow; i++){
35335             this.deselectRow(i, preventViewNotify);
35336         }
35337     },
35338
35339     /**
35340      * Selects a row.
35341      * @param {Number} row The index of the row to select
35342      * @param {Boolean} keepExisting (optional) True to keep existing selections
35343      */
35344     selectRow : function(index, keepExisting, preventViewNotify){
35345         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
35346         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
35347             if(!keepExisting || this.singleSelect){
35348                 this.clearSelections();
35349             }
35350             var r = this.grid.dataSource.getAt(index);
35351             this.selections.add(r);
35352             this.last = this.lastActive = index;
35353             if(!preventViewNotify){
35354                 this.grid.getView().onRowSelect(index);
35355             }
35356             this.fireEvent("rowselect", this, index, r);
35357             this.fireEvent("selectionchange", this);
35358         }
35359     },
35360
35361     /**
35362      * Deselects a row.
35363      * @param {Number} row The index of the row to deselect
35364      */
35365     deselectRow : function(index, preventViewNotify){
35366         if(this.locked) return;
35367         if(this.last == index){
35368             this.last = false;
35369         }
35370         if(this.lastActive == index){
35371             this.lastActive = false;
35372         }
35373         var r = this.grid.dataSource.getAt(index);
35374         this.selections.remove(r);
35375         if(!preventViewNotify){
35376             this.grid.getView().onRowDeselect(index);
35377         }
35378         this.fireEvent("rowdeselect", this, index);
35379         this.fireEvent("selectionchange", this);
35380     },
35381
35382     // private
35383     restoreLast : function(){
35384         if(this._last){
35385             this.last = this._last;
35386         }
35387     },
35388
35389     // private
35390     acceptsNav : function(row, col, cm){
35391         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35392     },
35393
35394     // private
35395     onEditorKey : function(field, e){
35396         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35397         if(k == e.TAB){
35398             e.stopEvent();
35399             ed.completeEdit();
35400             if(e.shiftKey){
35401                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35402             }else{
35403                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35404             }
35405         }else if(k == e.ENTER && !e.ctrlKey){
35406             e.stopEvent();
35407             ed.completeEdit();
35408             if(e.shiftKey){
35409                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
35410             }else{
35411                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
35412             }
35413         }else if(k == e.ESC){
35414             ed.cancelEdit();
35415         }
35416         if(newCell){
35417             g.startEditing(newCell[0], newCell[1]);
35418         }
35419     }
35420 });/*
35421  * Based on:
35422  * Ext JS Library 1.1.1
35423  * Copyright(c) 2006-2007, Ext JS, LLC.
35424  *
35425  * Originally Released Under LGPL - original licence link has changed is not relivant.
35426  *
35427  * Fork - LGPL
35428  * <script type="text/javascript">
35429  */
35430 /**
35431  * @class Roo.grid.CellSelectionModel
35432  * @extends Roo.grid.AbstractSelectionModel
35433  * This class provides the basic implementation for cell selection in a grid.
35434  * @constructor
35435  * @param {Object} config The object containing the configuration of this model.
35436  */
35437 Roo.grid.CellSelectionModel = function(config){
35438     Roo.apply(this, config);
35439
35440     this.selection = null;
35441
35442     this.addEvents({
35443         /**
35444              * @event beforerowselect
35445              * Fires before a cell is selected.
35446              * @param {SelectionModel} this
35447              * @param {Number} rowIndex The selected row index
35448              * @param {Number} colIndex The selected cell index
35449              */
35450             "beforecellselect" : true,
35451         /**
35452              * @event cellselect
35453              * Fires when a cell is selected.
35454              * @param {SelectionModel} this
35455              * @param {Number} rowIndex The selected row index
35456              * @param {Number} colIndex The selected cell index
35457              */
35458             "cellselect" : true,
35459         /**
35460              * @event selectionchange
35461              * Fires when the active selection changes.
35462              * @param {SelectionModel} this
35463              * @param {Object} selection null for no selection or an object (o) with two properties
35464                 <ul>
35465                 <li>o.record: the record object for the row the selection is in</li>
35466                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
35467                 </ul>
35468              */
35469             "selectionchange" : true
35470     });
35471     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
35472 };
35473
35474 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
35475
35476     /** @ignore */
35477     initEvents : function(){
35478         this.grid.on("mousedown", this.handleMouseDown, this);
35479         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
35480         var view = this.grid.view;
35481         view.on("refresh", this.onViewChange, this);
35482         view.on("rowupdated", this.onRowUpdated, this);
35483         view.on("beforerowremoved", this.clearSelections, this);
35484         view.on("beforerowsinserted", this.clearSelections, this);
35485         if(this.grid.isEditor){
35486             this.grid.on("beforeedit", this.beforeEdit,  this);
35487         }
35488     },
35489
35490         //private
35491     beforeEdit : function(e){
35492         this.select(e.row, e.column, false, true, e.record);
35493     },
35494
35495         //private
35496     onRowUpdated : function(v, index, r){
35497         if(this.selection && this.selection.record == r){
35498             v.onCellSelect(index, this.selection.cell[1]);
35499         }
35500     },
35501
35502         //private
35503     onViewChange : function(){
35504         this.clearSelections(true);
35505     },
35506
35507         /**
35508          * Returns the currently selected cell,.
35509          * @return {Array} The selected cell (row, column) or null if none selected.
35510          */
35511     getSelectedCell : function(){
35512         return this.selection ? this.selection.cell : null;
35513     },
35514
35515     /**
35516      * Clears all selections.
35517      * @param {Boolean} true to prevent the gridview from being notified about the change.
35518      */
35519     clearSelections : function(preventNotify){
35520         var s = this.selection;
35521         if(s){
35522             if(preventNotify !== true){
35523                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
35524             }
35525             this.selection = null;
35526             this.fireEvent("selectionchange", this, null);
35527         }
35528     },
35529
35530     /**
35531      * Returns true if there is a selection.
35532      * @return {Boolean}
35533      */
35534     hasSelection : function(){
35535         return this.selection ? true : false;
35536     },
35537
35538     /** @ignore */
35539     handleMouseDown : function(e, t){
35540         var v = this.grid.getView();
35541         if(this.isLocked()){
35542             return;
35543         };
35544         var row = v.findRowIndex(t);
35545         var cell = v.findCellIndex(t);
35546         if(row !== false && cell !== false){
35547             this.select(row, cell);
35548         }
35549     },
35550
35551     /**
35552      * Selects a cell.
35553      * @param {Number} rowIndex
35554      * @param {Number} collIndex
35555      */
35556     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
35557         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
35558             this.clearSelections();
35559             r = r || this.grid.dataSource.getAt(rowIndex);
35560             this.selection = {
35561                 record : r,
35562                 cell : [rowIndex, colIndex]
35563             };
35564             if(!preventViewNotify){
35565                 var v = this.grid.getView();
35566                 v.onCellSelect(rowIndex, colIndex);
35567                 if(preventFocus !== true){
35568                     v.focusCell(rowIndex, colIndex);
35569                 }
35570             }
35571             this.fireEvent("cellselect", this, rowIndex, colIndex);
35572             this.fireEvent("selectionchange", this, this.selection);
35573         }
35574     },
35575
35576         //private
35577     isSelectable : function(rowIndex, colIndex, cm){
35578         return !cm.isHidden(colIndex);
35579     },
35580
35581     /** @ignore */
35582     handleKeyDown : function(e){
35583         Roo.log('Cell Sel Model handleKeyDown');
35584         if(!e.isNavKeyPress()){
35585             return;
35586         }
35587         var g = this.grid, s = this.selection;
35588         if(!s){
35589             e.stopEvent();
35590             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
35591             if(cell){
35592                 this.select(cell[0], cell[1]);
35593             }
35594             return;
35595         }
35596         var sm = this;
35597         var walk = function(row, col, step){
35598             return g.walkCells(row, col, step, sm.isSelectable,  sm);
35599         };
35600         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
35601         var newCell;
35602
35603         switch(k){
35604             case e.TAB:
35605                 // handled by onEditorKey
35606                 if (g.isEditor && g.editing) {
35607                     return;
35608                 }
35609                 if(e.shiftKey){
35610                      newCell = walk(r, c-1, -1);
35611                 }else{
35612                      newCell = walk(r, c+1, 1);
35613                 }
35614              break;
35615              case e.DOWN:
35616                  newCell = walk(r+1, c, 1);
35617              break;
35618              case e.UP:
35619                  newCell = walk(r-1, c, -1);
35620              break;
35621              case e.RIGHT:
35622                  newCell = walk(r, c+1, 1);
35623              break;
35624              case e.LEFT:
35625                  newCell = walk(r, c-1, -1);
35626              break;
35627              case e.ENTER:
35628                  if(g.isEditor && !g.editing){
35629                     g.startEditing(r, c);
35630                     e.stopEvent();
35631                     return;
35632                 }
35633              break;
35634         };
35635         if(newCell){
35636             this.select(newCell[0], newCell[1]);
35637             e.stopEvent();
35638         }
35639     },
35640
35641     acceptsNav : function(row, col, cm){
35642         return !cm.isHidden(col) && cm.isCellEditable(col, row);
35643     },
35644
35645     onEditorKey : function(field, e){
35646         
35647         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
35648         ///Roo.log('onEditorKey' + k);
35649         
35650         if(k == e.TAB){
35651             if(e.shiftKey){
35652                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
35653             }else{
35654                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35655             }
35656             e.stopEvent();
35657         }else if(k == e.ENTER && !e.ctrlKey){
35658             ed.completeEdit();
35659             e.stopEvent();
35660             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
35661         }else if(k == e.ESC){
35662             ed.cancelEdit();
35663         }
35664         
35665         
35666         if(newCell){
35667             //Roo.log('next cell after edit');
35668             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
35669         }
35670     }
35671 });/*
35672  * Based on:
35673  * Ext JS Library 1.1.1
35674  * Copyright(c) 2006-2007, Ext JS, LLC.
35675  *
35676  * Originally Released Under LGPL - original licence link has changed is not relivant.
35677  *
35678  * Fork - LGPL
35679  * <script type="text/javascript">
35680  */
35681  
35682 /**
35683  * @class Roo.grid.EditorGrid
35684  * @extends Roo.grid.Grid
35685  * Class for creating and editable grid.
35686  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
35687  * The container MUST have some type of size defined for the grid to fill. The container will be 
35688  * automatically set to position relative if it isn't already.
35689  * @param {Object} dataSource The data model to bind to
35690  * @param {Object} colModel The column model with info about this grid's columns
35691  */
35692 Roo.grid.EditorGrid = function(container, config){
35693     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
35694     this.getGridEl().addClass("xedit-grid");
35695
35696     if(!this.selModel){
35697         this.selModel = new Roo.grid.CellSelectionModel();
35698     }
35699
35700     this.activeEditor = null;
35701
35702         this.addEvents({
35703             /**
35704              * @event beforeedit
35705              * Fires before cell editing is triggered. The edit event object has the following properties <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 for the field being edited.</li>
35711              * <li>row - The grid row index</li>
35712              * <li>column - The grid column index</li>
35713              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35714              * </ul>
35715              * @param {Object} e An edit event (see above for description)
35716              */
35717             "beforeedit" : true,
35718             /**
35719              * @event afteredit
35720              * Fires after a cell is edited. <br />
35721              * <ul style="padding:5px;padding-left:16px;">
35722              * <li>grid - This grid</li>
35723              * <li>record - The record being edited</li>
35724              * <li>field - The field name being edited</li>
35725              * <li>value - The value being set</li>
35726              * <li>originalValue - The original value for the field, before the edit.</li>
35727              * <li>row - The grid row index</li>
35728              * <li>column - The grid column index</li>
35729              * </ul>
35730              * @param {Object} e An edit event (see above for description)
35731              */
35732             "afteredit" : true,
35733             /**
35734              * @event validateedit
35735              * Fires after a cell is edited, but before the value is set in the record. 
35736          * You can use this to modify the value being set in the field, Return false
35737              * to cancel the change. The edit event object has the following properties <br />
35738              * <ul style="padding:5px;padding-left:16px;">
35739          * <li>editor - This editor</li>
35740              * <li>grid - This grid</li>
35741              * <li>record - The record being edited</li>
35742              * <li>field - The field name being edited</li>
35743              * <li>value - The value being set</li>
35744              * <li>originalValue - The original value for the field, before the edit.</li>
35745              * <li>row - The grid row index</li>
35746              * <li>column - The grid column index</li>
35747              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
35748              * </ul>
35749              * @param {Object} e An edit event (see above for description)
35750              */
35751             "validateedit" : true
35752         });
35753     this.on("bodyscroll", this.stopEditing,  this);
35754     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
35755 };
35756
35757 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
35758     /**
35759      * @cfg {Number} clicksToEdit
35760      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
35761      */
35762     clicksToEdit: 2,
35763
35764     // private
35765     isEditor : true,
35766     // private
35767     trackMouseOver: false, // causes very odd FF errors
35768
35769     onCellDblClick : function(g, row, col){
35770         this.startEditing(row, col);
35771     },
35772
35773     onEditComplete : function(ed, value, startValue){
35774         this.editing = false;
35775         this.activeEditor = null;
35776         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
35777         var r = ed.record;
35778         var field = this.colModel.getDataIndex(ed.col);
35779         var e = {
35780             grid: this,
35781             record: r,
35782             field: field,
35783             originalValue: startValue,
35784             value: value,
35785             row: ed.row,
35786             column: ed.col,
35787             cancel:false,
35788             editor: ed
35789         };
35790         if(String(value) !== String(startValue)){
35791             
35792             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
35793                 r.set(field, e.value);
35794                 // if we are dealing with a combo box..
35795                 // then we also set the 'name' colum to be the displayField
35796                 if (ed.field.displayField && ed.field.name) {
35797                     r.set(ed.field.name, ed.field.el.dom.value);
35798                 }
35799                 
35800                 delete e.cancel; //?? why!!!
35801                 this.fireEvent("afteredit", e);
35802             }
35803         } else {
35804             this.fireEvent("afteredit", e); // always fire it!
35805         }
35806         this.view.focusCell(ed.row, ed.col);
35807     },
35808
35809     /**
35810      * Starts editing the specified for the specified row/column
35811      * @param {Number} rowIndex
35812      * @param {Number} colIndex
35813      */
35814     startEditing : function(row, col){
35815         this.stopEditing();
35816         if(this.colModel.isCellEditable(col, row)){
35817             this.view.ensureVisible(row, col, true);
35818             var r = this.dataSource.getAt(row);
35819             var field = this.colModel.getDataIndex(col);
35820             var e = {
35821                 grid: this,
35822                 record: r,
35823                 field: field,
35824                 value: r.data[field],
35825                 row: row,
35826                 column: col,
35827                 cancel:false
35828             };
35829             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
35830                 this.editing = true;
35831                 var ed = this.colModel.getCellEditor(col, row);
35832                 
35833                 if (!ed) {
35834                     return;
35835                 }
35836                 if(!ed.rendered){
35837                     ed.render(ed.parentEl || document.body);
35838                 }
35839                 ed.field.reset();
35840                 (function(){ // complex but required for focus issues in safari, ie and opera
35841                     ed.row = row;
35842                     ed.col = col;
35843                     ed.record = r;
35844                     ed.on("complete", this.onEditComplete, this, {single: true});
35845                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
35846                     this.activeEditor = ed;
35847                     var v = r.data[field];
35848                     ed.startEdit(this.view.getCell(row, col), v);
35849                     // combo's with 'displayField and name set
35850                     if (ed.field.displayField && ed.field.name) {
35851                         ed.field.el.dom.value = r.data[ed.field.name];
35852                     }
35853                     
35854                     
35855                 }).defer(50, this);
35856             }
35857         }
35858     },
35859         
35860     /**
35861      * Stops any active editing
35862      */
35863     stopEditing : function(){
35864         if(this.activeEditor){
35865             this.activeEditor.completeEdit();
35866         }
35867         this.activeEditor = null;
35868     }
35869 });/*
35870  * Based on:
35871  * Ext JS Library 1.1.1
35872  * Copyright(c) 2006-2007, Ext JS, LLC.
35873  *
35874  * Originally Released Under LGPL - original licence link has changed is not relivant.
35875  *
35876  * Fork - LGPL
35877  * <script type="text/javascript">
35878  */
35879
35880 // private - not really -- you end up using it !
35881 // This is a support class used internally by the Grid components
35882
35883 /**
35884  * @class Roo.grid.GridEditor
35885  * @extends Roo.Editor
35886  * Class for creating and editable grid elements.
35887  * @param {Object} config any settings (must include field)
35888  */
35889 Roo.grid.GridEditor = function(field, config){
35890     if (!config && field.field) {
35891         config = field;
35892         field = Roo.factory(config.field, Roo.form);
35893     }
35894     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
35895     field.monitorTab = false;
35896 };
35897
35898 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
35899     
35900     /**
35901      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
35902      */
35903     
35904     alignment: "tl-tl",
35905     autoSize: "width",
35906     hideEl : false,
35907     cls: "x-small-editor x-grid-editor",
35908     shim:false,
35909     shadow:"frame"
35910 });/*
35911  * Based on:
35912  * Ext JS Library 1.1.1
35913  * Copyright(c) 2006-2007, Ext JS, LLC.
35914  *
35915  * Originally Released Under LGPL - original licence link has changed is not relivant.
35916  *
35917  * Fork - LGPL
35918  * <script type="text/javascript">
35919  */
35920   
35921
35922   
35923 Roo.grid.PropertyRecord = Roo.data.Record.create([
35924     {name:'name',type:'string'},  'value'
35925 ]);
35926
35927
35928 Roo.grid.PropertyStore = function(grid, source){
35929     this.grid = grid;
35930     this.store = new Roo.data.Store({
35931         recordType : Roo.grid.PropertyRecord
35932     });
35933     this.store.on('update', this.onUpdate,  this);
35934     if(source){
35935         this.setSource(source);
35936     }
35937     Roo.grid.PropertyStore.superclass.constructor.call(this);
35938 };
35939
35940
35941
35942 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
35943     setSource : function(o){
35944         this.source = o;
35945         this.store.removeAll();
35946         var data = [];
35947         for(var k in o){
35948             if(this.isEditableValue(o[k])){
35949                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
35950             }
35951         }
35952         this.store.loadRecords({records: data}, {}, true);
35953     },
35954
35955     onUpdate : function(ds, record, type){
35956         if(type == Roo.data.Record.EDIT){
35957             var v = record.data['value'];
35958             var oldValue = record.modified['value'];
35959             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
35960                 this.source[record.id] = v;
35961                 record.commit();
35962                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
35963             }else{
35964                 record.reject();
35965             }
35966         }
35967     },
35968
35969     getProperty : function(row){
35970        return this.store.getAt(row);
35971     },
35972
35973     isEditableValue: function(val){
35974         if(val && val instanceof Date){
35975             return true;
35976         }else if(typeof val == 'object' || typeof val == 'function'){
35977             return false;
35978         }
35979         return true;
35980     },
35981
35982     setValue : function(prop, value){
35983         this.source[prop] = value;
35984         this.store.getById(prop).set('value', value);
35985     },
35986
35987     getSource : function(){
35988         return this.source;
35989     }
35990 });
35991
35992 Roo.grid.PropertyColumnModel = function(grid, store){
35993     this.grid = grid;
35994     var g = Roo.grid;
35995     g.PropertyColumnModel.superclass.constructor.call(this, [
35996         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
35997         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
35998     ]);
35999     this.store = store;
36000     this.bselect = Roo.DomHelper.append(document.body, {
36001         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36002             {tag: 'option', value: 'true', html: 'true'},
36003             {tag: 'option', value: 'false', html: 'false'}
36004         ]
36005     });
36006     Roo.id(this.bselect);
36007     var f = Roo.form;
36008     this.editors = {
36009         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36010         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36011         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36012         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36013         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36014     };
36015     this.renderCellDelegate = this.renderCell.createDelegate(this);
36016     this.renderPropDelegate = this.renderProp.createDelegate(this);
36017 };
36018
36019 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36020     
36021     
36022     nameText : 'Name',
36023     valueText : 'Value',
36024     
36025     dateFormat : 'm/j/Y',
36026     
36027     
36028     renderDate : function(dateVal){
36029         return dateVal.dateFormat(this.dateFormat);
36030     },
36031
36032     renderBool : function(bVal){
36033         return bVal ? 'true' : 'false';
36034     },
36035
36036     isCellEditable : function(colIndex, rowIndex){
36037         return colIndex == 1;
36038     },
36039
36040     getRenderer : function(col){
36041         return col == 1 ?
36042             this.renderCellDelegate : this.renderPropDelegate;
36043     },
36044
36045     renderProp : function(v){
36046         return this.getPropertyName(v);
36047     },
36048
36049     renderCell : function(val){
36050         var rv = val;
36051         if(val instanceof Date){
36052             rv = this.renderDate(val);
36053         }else if(typeof val == 'boolean'){
36054             rv = this.renderBool(val);
36055         }
36056         return Roo.util.Format.htmlEncode(rv);
36057     },
36058
36059     getPropertyName : function(name){
36060         var pn = this.grid.propertyNames;
36061         return pn && pn[name] ? pn[name] : name;
36062     },
36063
36064     getCellEditor : function(colIndex, rowIndex){
36065         var p = this.store.getProperty(rowIndex);
36066         var n = p.data['name'], val = p.data['value'];
36067         
36068         if(typeof(this.grid.customEditors[n]) == 'string'){
36069             return this.editors[this.grid.customEditors[n]];
36070         }
36071         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36072             return this.grid.customEditors[n];
36073         }
36074         if(val instanceof Date){
36075             return this.editors['date'];
36076         }else if(typeof val == 'number'){
36077             return this.editors['number'];
36078         }else if(typeof val == 'boolean'){
36079             return this.editors['boolean'];
36080         }else{
36081             return this.editors['string'];
36082         }
36083     }
36084 });
36085
36086 /**
36087  * @class Roo.grid.PropertyGrid
36088  * @extends Roo.grid.EditorGrid
36089  * This class represents the  interface of a component based property grid control.
36090  * <br><br>Usage:<pre><code>
36091  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36092       
36093  });
36094  // set any options
36095  grid.render();
36096  * </code></pre>
36097   
36098  * @constructor
36099  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36100  * The container MUST have some type of size defined for the grid to fill. The container will be
36101  * automatically set to position relative if it isn't already.
36102  * @param {Object} config A config object that sets properties on this grid.
36103  */
36104 Roo.grid.PropertyGrid = function(container, config){
36105     config = config || {};
36106     var store = new Roo.grid.PropertyStore(this);
36107     this.store = store;
36108     var cm = new Roo.grid.PropertyColumnModel(this, store);
36109     store.store.sort('name', 'ASC');
36110     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36111         ds: store.store,
36112         cm: cm,
36113         enableColLock:false,
36114         enableColumnMove:false,
36115         stripeRows:false,
36116         trackMouseOver: false,
36117         clicksToEdit:1
36118     }, config));
36119     this.getGridEl().addClass('x-props-grid');
36120     this.lastEditRow = null;
36121     this.on('columnresize', this.onColumnResize, this);
36122     this.addEvents({
36123          /**
36124              * @event beforepropertychange
36125              * Fires before a property changes (return false to stop?)
36126              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36127              * @param {String} id Record Id
36128              * @param {String} newval New Value
36129          * @param {String} oldval Old Value
36130              */
36131         "beforepropertychange": true,
36132         /**
36133              * @event propertychange
36134              * Fires after a property changes
36135              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36136              * @param {String} id Record Id
36137              * @param {String} newval New Value
36138          * @param {String} oldval Old Value
36139              */
36140         "propertychange": true
36141     });
36142     this.customEditors = this.customEditors || {};
36143 };
36144 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36145     
36146      /**
36147      * @cfg {Object} customEditors map of colnames=> custom editors.
36148      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36149      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36150      * false disables editing of the field.
36151          */
36152     
36153       /**
36154      * @cfg {Object} propertyNames map of property Names to their displayed value
36155          */
36156     
36157     render : function(){
36158         Roo.grid.PropertyGrid.superclass.render.call(this);
36159         this.autoSize.defer(100, this);
36160     },
36161
36162     autoSize : function(){
36163         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36164         if(this.view){
36165             this.view.fitColumns();
36166         }
36167     },
36168
36169     onColumnResize : function(){
36170         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36171         this.autoSize();
36172     },
36173     /**
36174      * Sets the data for the Grid
36175      * accepts a Key => Value object of all the elements avaiable.
36176      * @param {Object} data  to appear in grid.
36177      */
36178     setSource : function(source){
36179         this.store.setSource(source);
36180         //this.autoSize();
36181     },
36182     /**
36183      * Gets all the data from the grid.
36184      * @return {Object} data  data stored in grid
36185      */
36186     getSource : function(){
36187         return this.store.getSource();
36188     }
36189 });/*
36190  * Based on:
36191  * Ext JS Library 1.1.1
36192  * Copyright(c) 2006-2007, Ext JS, LLC.
36193  *
36194  * Originally Released Under LGPL - original licence link has changed is not relivant.
36195  *
36196  * Fork - LGPL
36197  * <script type="text/javascript">
36198  */
36199  
36200 /**
36201  * @class Roo.LoadMask
36202  * A simple utility class for generically masking elements while loading data.  If the element being masked has
36203  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
36204  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
36205  * element's UpdateManager load indicator and will be destroyed after the initial load.
36206  * @constructor
36207  * Create a new LoadMask
36208  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
36209  * @param {Object} config The config object
36210  */
36211 Roo.LoadMask = function(el, config){
36212     this.el = Roo.get(el);
36213     Roo.apply(this, config);
36214     if(this.store){
36215         this.store.on('beforeload', this.onBeforeLoad, this);
36216         this.store.on('load', this.onLoad, this);
36217         this.store.on('loadexception', this.onLoad, this);
36218         this.removeMask = false;
36219     }else{
36220         var um = this.el.getUpdateManager();
36221         um.showLoadIndicator = false; // disable the default indicator
36222         um.on('beforeupdate', this.onBeforeLoad, this);
36223         um.on('update', this.onLoad, this);
36224         um.on('failure', this.onLoad, this);
36225         this.removeMask = true;
36226     }
36227 };
36228
36229 Roo.LoadMask.prototype = {
36230     /**
36231      * @cfg {Boolean} removeMask
36232      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
36233      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
36234      */
36235     /**
36236      * @cfg {String} msg
36237      * The text to display in a centered loading message box (defaults to 'Loading...')
36238      */
36239     msg : 'Loading...',
36240     /**
36241      * @cfg {String} msgCls
36242      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
36243      */
36244     msgCls : 'x-mask-loading',
36245
36246     /**
36247      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
36248      * @type Boolean
36249      */
36250     disabled: false,
36251
36252     /**
36253      * Disables the mask to prevent it from being displayed
36254      */
36255     disable : function(){
36256        this.disabled = true;
36257     },
36258
36259     /**
36260      * Enables the mask so that it can be displayed
36261      */
36262     enable : function(){
36263         this.disabled = false;
36264     },
36265
36266     // private
36267     onLoad : function(){
36268         this.el.unmask(this.removeMask);
36269     },
36270
36271     // private
36272     onBeforeLoad : function(){
36273         if(!this.disabled){
36274             this.el.mask(this.msg, this.msgCls);
36275         }
36276     },
36277
36278     // private
36279     destroy : function(){
36280         if(this.store){
36281             this.store.un('beforeload', this.onBeforeLoad, this);
36282             this.store.un('load', this.onLoad, this);
36283             this.store.un('loadexception', this.onLoad, this);
36284         }else{
36285             var um = this.el.getUpdateManager();
36286             um.un('beforeupdate', this.onBeforeLoad, this);
36287             um.un('update', this.onLoad, this);
36288             um.un('failure', this.onLoad, this);
36289         }
36290     }
36291 };/*
36292  * Based on:
36293  * Ext JS Library 1.1.1
36294  * Copyright(c) 2006-2007, Ext JS, LLC.
36295  *
36296  * Originally Released Under LGPL - original licence link has changed is not relivant.
36297  *
36298  * Fork - LGPL
36299  * <script type="text/javascript">
36300  */
36301 Roo.XTemplate = function(){
36302     Roo.XTemplate.superclass.constructor.apply(this, arguments);
36303     var s = this.html;
36304
36305     s = ['<tpl>', s, '</tpl>'].join('');
36306
36307     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
36308
36309     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
36310     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
36311     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
36312     var m, id = 0;
36313     var tpls = [];
36314
36315     while(m = s.match(re)){
36316        var m2 = m[0].match(nameRe);
36317        var m3 = m[0].match(ifRe);
36318        var m4 = m[0].match(execRe);
36319        var exp = null, fn = null, exec = null;
36320        var name = m2 && m2[1] ? m2[1] : '';
36321        if(m3){
36322            exp = m3 && m3[1] ? m3[1] : null;
36323            if(exp){
36324                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
36325            }
36326        }
36327        if(m4){
36328            exp = m4 && m4[1] ? m4[1] : null;
36329            if(exp){
36330                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
36331            }
36332        }
36333        if(name){
36334            switch(name){
36335                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
36336                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
36337                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
36338            }
36339        }
36340        tpls.push({
36341             id: id,
36342             target: name,
36343             exec: exec,
36344             test: fn,
36345             body: m[1]||''
36346         });
36347        s = s.replace(m[0], '{xtpl'+ id + '}');
36348        ++id;
36349     }
36350     for(var i = tpls.length-1; i >= 0; --i){
36351         this.compileTpl(tpls[i]);
36352     }
36353     this.master = tpls[tpls.length-1];
36354     this.tpls = tpls;
36355 };
36356 Roo.extend(Roo.XTemplate, Roo.Template, {
36357
36358     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
36359
36360     applySubTemplate : function(id, values, parent){
36361         var t = this.tpls[id];
36362         if(t.test && !t.test.call(this, values, parent)){
36363             return '';
36364         }
36365         if(t.exec && t.exec.call(this, values, parent)){
36366             return '';
36367         }
36368         var vs = t.target ? t.target.call(this, values, parent) : values;
36369         parent = t.target ? values : parent;
36370         if(t.target && vs instanceof Array){
36371             var buf = [];
36372             for(var i = 0, len = vs.length; i < len; i++){
36373                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
36374             }
36375             return buf.join('');
36376         }
36377         return t.compiled.call(this, vs, parent);
36378     },
36379
36380     compileTpl : function(tpl){
36381         var fm = Roo.util.Format;
36382         var useF = this.disableFormats !== true;
36383         var sep = Roo.isGecko ? "+" : ",";
36384         var fn = function(m, name, format, args){
36385             if(name.substr(0, 4) == 'xtpl'){
36386                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
36387             }
36388             var v;
36389             if(name.indexOf('.') != -1){
36390                 v = name;
36391             }else{
36392                 v = "values['" + name + "']";
36393             }
36394             if(format && useF){
36395                 args = args ? ',' + args : "";
36396                 if(format.substr(0, 5) != "this."){
36397                     format = "fm." + format + '(';
36398                 }else{
36399                     format = 'this.call("'+ format.substr(5) + '", ';
36400                     args = ", values";
36401                 }
36402             }else{
36403                 args= ''; format = "("+v+" === undefined ? '' : ";
36404             }
36405             return "'"+ sep + format + v + args + ")"+sep+"'";
36406         };
36407         var body;
36408         // branched to use + in gecko and [].join() in others
36409         if(Roo.isGecko){
36410             body = "tpl.compiled = function(values, parent){ return '" +
36411                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
36412                     "';};";
36413         }else{
36414             body = ["tpl.compiled = function(values, parent){ return ['"];
36415             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
36416             body.push("'].join('');};");
36417             body = body.join('');
36418         }
36419         /** eval:var:zzzzzzz */
36420         eval(body);
36421         return this;
36422     },
36423
36424     applyTemplate : function(values){
36425         return this.master.compiled.call(this, values, {});
36426         var s = this.subs;
36427     },
36428
36429     apply : function(){
36430         return this.applyTemplate.apply(this, arguments);
36431     },
36432
36433     compile : function(){return this;}
36434 });
36435
36436 Roo.XTemplate.from = function(el){
36437     el = Roo.getDom(el);
36438     return new Roo.XTemplate(el.value || el.innerHTML);
36439 };/*
36440  * Original code for Roojs - LGPL
36441  * <script type="text/javascript">
36442  */
36443  
36444 /**
36445  * @class Roo.XComponent
36446  * A delayed Element creator...
36447  * 
36448  * Mypart.xyx = new Roo.XComponent({
36449
36450     parent : 'Mypart.xyz', // empty == document.element.!!
36451     order : '001',
36452     name : 'xxxx'
36453     region : 'xxxx'
36454     disabled : function() {} 
36455      
36456     tree : function() { // return an tree of xtype declared components
36457         var MODULE = this;
36458         return 
36459         {
36460             xtype : 'NestedLayoutPanel',
36461             // technicall
36462         }
36463      ]
36464  *})
36465  * @extends Roo.util.Observable
36466  * @constructor
36467  * @param cfg {Object} configuration of component
36468  * 
36469  */
36470 Roo.XComponent = function(cfg) {
36471     Roo.apply(this, cfg);
36472     this.addEvents({ 
36473         /**
36474              * @event built
36475              * Fires when this the componnt is built
36476              * @param {Roo.XComponent} c the component
36477              */
36478         'built' : true,
36479         /**
36480              * @event buildcomplete
36481              * Fires on the top level element when all elements have been built
36482              * @param {Roo.XComponent} c the top level component.
36483          */
36484         'buildcomplete' : true
36485         
36486     });
36487     
36488     Roo.XComponent.register(this);
36489     this.modules = false;
36490     this.el = false; // where the layout goes..
36491     
36492     
36493 }
36494 Roo.extend(Roo.XComponent, Roo.util.Observable, {
36495     /**
36496      * @property el
36497      * The created element (with Roo.factory())
36498      * @type {Roo.Layout}
36499      */
36500     el  : false,
36501     
36502     /**
36503      * @property el
36504      * for BC  - use el in new code
36505      * @type {Roo.Layout}
36506      */
36507     panel : false,
36508     
36509     /**
36510      * @property layout
36511      * for BC  - use el in new code
36512      * @type {Roo.Layout}
36513      */
36514     layout : false,
36515     
36516      /**
36517      * @cfg {Function|boolean} disabled
36518      * If this module is disabled by some rule, return true from the funtion
36519      */
36520     disabled : false,
36521     
36522     /**
36523      * @cfg {String} parent 
36524      * Name of parent element which it get xtype added to..
36525      */
36526     parent: false,
36527     
36528     /**
36529      * @cfg {String} order
36530      * Used to set the order in which elements are created (usefull for multiple tabs)
36531      */
36532     
36533     order : false,
36534     /**
36535      * @cfg {String} name
36536      * String to display while loading.
36537      */
36538     name : false,
36539     /**
36540      * @cfg {Array} items
36541      * A single item array - the first element is the root of the tree..
36542      * It's done this way to stay compatible with the Xtype system...
36543      */
36544     items : false
36545      
36546      
36547     
36548 });
36549
36550 Roo.apply(Roo.XComponent, {
36551     
36552     /**
36553      * @property  buildCompleted
36554      * True when the builder has completed building the interface.
36555      * @type Boolean
36556      */
36557     buildCompleted : false,
36558      
36559     /**
36560      * @property  topModule
36561      * the upper most module - uses document.element as it's constructor.
36562      * @type Object
36563      */
36564      
36565     topModule  : false,
36566       
36567     /**
36568      * @property  modules
36569      * array of modules to be created by registration system.
36570      * @type Roo.XComponent
36571      */
36572     
36573     modules : [],
36574       
36575     
36576     /**
36577      * Register components to be built later.
36578      *
36579      * This solves the following issues
36580      * - Building is not done on page load, but after an authentication process has occured.
36581      * - Interface elements are registered on page load
36582      * - Parent Interface elements may not be loaded before child, so this handles that..
36583      * 
36584      *
36585      * example:
36586      * 
36587      * MyApp.register({
36588           order : '000001',
36589           module : 'Pman.Tab.projectMgr',
36590           region : 'center',
36591           parent : 'Pman.layout',
36592           disabled : false,  // or use a function..
36593         })
36594      
36595      * * @param {Object} details about module
36596      */
36597     register : function(obj) {
36598         this.modules.push(obj);
36599          
36600     },
36601     /**
36602      * convert a string to an object..
36603      * 
36604      */
36605     
36606     toObject : function(str)
36607     {
36608         if (!str || typeof(str) == 'object') {
36609             return str;
36610         }
36611         var ar = str.split('.');
36612         var rt, o;
36613         rt = ar.shift();
36614             /** eval:var:o */
36615         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
36616         if (o === false) {
36617             throw "Module not found : " + str;
36618         }
36619         Roo.each(ar, function(e) {
36620             if (typeof(o[e]) == 'undefined') {
36621                 throw "Module not found : " + str;
36622             }
36623             o = o[e];
36624         });
36625         return o;
36626         
36627     },
36628     
36629     
36630     /**
36631      * move modules into their correct place in the tree..
36632      * 
36633      */
36634     preBuild : function ()
36635     {
36636         
36637         Roo.each(this.modules , function (obj)
36638         {
36639             obj.parent = this.toObject(obj.parent);
36640             
36641             if (!obj.parent) {
36642                 this.topModule = obj;
36643                 return;
36644             }
36645             
36646             if (!obj.parent.modules) {
36647                 obj.parent.modules = new Roo.util.MixedCollection(false, 
36648                     function(o) { return o.order + '' }
36649                 );
36650             }
36651             
36652             obj.parent.modules.add(obj);
36653         }, this);
36654     },
36655     
36656      /**
36657      * make a list of modules to build.
36658      * @return {Array} list of modules. 
36659      */ 
36660     
36661     buildOrder : function()
36662     {
36663         var _this = this;
36664         var cmp = function(a,b) {   
36665             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
36666         };
36667         
36668         if (!this.topModule || !this.topModule.modules) {
36669             throw "No top level modules to build";
36670         }
36671        
36672         // make a flat list in order of modules to build.
36673         var mods = [ this.topModule ];
36674         
36675         
36676         // add modules to their parents..
36677         var addMod = function(m) {
36678            // Roo.debug && Roo.log(m.modKey);
36679             
36680             mods.push(m);
36681             if (m.modules) {
36682                 m.modules.keySort('ASC',  cmp );
36683                 m.modules.each(addMod);
36684             }
36685             // not sure if this is used any more..
36686             if (m.finalize) {
36687                 m.finalize.name = m.name + " (clean up) ";
36688                 mods.push(m.finalize);
36689             }
36690             
36691         }
36692         this.topModule.modules.keySort('ASC',  cmp );
36693         this.topModule.modules.each(addMod);
36694         return mods;
36695     },
36696     
36697      /**
36698      * Build the registered modules.
36699      * @param {Object} parent element.
36700      * @param {Function} optional method to call after module has been added.
36701      * 
36702      */ 
36703    
36704     build : function() 
36705     {
36706         
36707         this.preBuild();
36708         var mods = this.buildOrder();
36709       
36710         //this.allmods = mods;
36711         //Roo.debug && Roo.log(mods);
36712         //return;
36713         if (!mods.length) { // should not happen
36714             throw "NO modules!!!";
36715         }
36716         
36717         
36718         
36719         // flash it up as modal - so we store the mask!?
36720         Roo.MessageBox.show({ title: 'loading' });
36721         Roo.MessageBox.show({
36722            title: "Please wait...",
36723            msg: "Building Interface...",
36724            width:450,
36725            progress:true,
36726            closable:false,
36727            modal: false
36728           
36729         });
36730         var total = mods.length;
36731         
36732         var _this = this;
36733         var progressRun = function() {
36734             if (!mods.length) {
36735                 Roo.debug && Roo.log('hide?');
36736                 Roo.MessageBox.hide();
36737                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
36738                 return;    
36739             }
36740             
36741             var m = mods.shift();
36742             Roo.debug && Roo.log(m);
36743             if (typeof(m) == 'function') { // not sure if this is supported any more..
36744                 m.call(this);
36745                 return progressRun.defer(10, _this);
36746             } 
36747             
36748             Roo.MessageBox.updateProgress(
36749                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
36750                     " of " + total + 
36751                     (m.name ? (' - ' + m.name) : '')
36752                     );
36753             
36754          
36755             
36756             var disabled = (typeof(m.disabled) == 'function') ?
36757                 m.disabled.call(m.module.disabled) : m.disabled;    
36758             
36759             
36760             if (disabled) {
36761                 return progressRun(); // we do not update the display!
36762             }
36763             
36764             if (!m.parent) {
36765                 // it's a top level one..
36766                 var layoutbase = new Ext.BorderLayout(document.body, {
36767                
36768                     center: {
36769                          titlebar: false,
36770                          autoScroll:false,
36771                          closeOnTab: true,
36772                          tabPosition: 'top',
36773                          //resizeTabs: true,
36774                          alwaysShowTabs: true,
36775                          minTabWidth: 140
36776                     }
36777                 });
36778                 var tree = m.tree();
36779                 tree.region = 'center';
36780                 m.el = layoutbase.addxtype(tree);
36781                 m.panel = m.el;
36782                 m.layout = m.panel.layout;    
36783                 return progressRun.defer(10, _this);
36784             }
36785             
36786             var tree = m.tree();
36787             tree.region = tree.region || m.region;
36788             m.el = m.parent.el.addxtype(tree);
36789             m.fireEvent('built', m);
36790             m.panel = m.el;
36791             m.layout = m.panel.layout;    
36792             progressRun.defer(10, _this); 
36793             
36794         }
36795         progressRun.defer(1, _this);
36796      
36797         
36798         
36799     }
36800      
36801    
36802     
36803     
36804 });
36805  //<script type="text/javascript">
36806
36807
36808 /**
36809  * @class Roo.Login
36810  * @extends Roo.LayoutDialog
36811  * A generic Login Dialog..... - only one needed in theory!?!?
36812  *
36813  * Fires XComponent builder on success...
36814  * 
36815  * Sends 
36816  *    username,password, lang = for login actions.
36817  *    check = 1 for periodic checking that sesion is valid.
36818  *    passwordRequest = email request password
36819  *    logout = 1 = to logout
36820  * 
36821  * Affects: (this id="????" elements)
36822  *   loading  (removed) (used to indicate application is loading)
36823  *   loading-mask (hides) (used to hide application when it's building loading)
36824  *   
36825  * 
36826  * Usage: 
36827  *    
36828  * 
36829  * Myapp.login = Roo.Login({
36830      url: xxxx,
36831    
36832      realm : 'Myapp', 
36833      
36834      
36835      method : 'POST',
36836      
36837      
36838      * 
36839  })
36840  * 
36841  * 
36842  * 
36843  **/
36844  
36845 Roo.Login = function(cfg)
36846 {
36847     this.addEvents({
36848         'refreshed' : true
36849     });
36850     
36851     Roo.apply(this,cfg);
36852     
36853     Roo.onReady(function() {
36854         this.onLoad();
36855     }, this);
36856     // call parent..
36857     
36858    
36859     Roo.Login.superclass.constructor.call(this, this);
36860     //this.addxtype(this.items[0]);
36861     
36862     
36863 }
36864
36865
36866 Roo.extend(Roo.Login, Roo.LayoutDialog, {
36867     
36868     /**
36869      * @cfg {String} method
36870      * Method used to query for login details.
36871      */
36872     
36873     method : 'POST',
36874     /**
36875      * @cfg {String} url
36876      * URL to query login data. - eg. baseURL + '/Login.php'
36877      */
36878     url : '',
36879     
36880     /**
36881      * @property user
36882      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
36883      * @type {Object} 
36884      */
36885     user : false,
36886     /**
36887      * @property checkFails
36888      * Number of times we have attempted to get authentication check, and failed.
36889      * @type {Number} 
36890      */
36891     checkFails : 0,
36892       /**
36893      * @property intervalID
36894      * The window interval that does the constant login checking.
36895      * @type {Number} 
36896      */
36897     intervalID : 0,
36898     
36899     
36900     onLoad : function() // called on page load...
36901     {
36902         // load 
36903          
36904         if (Roo.get('loading')) { // clear any loading indicator..
36905             Roo.get('loading').remove();
36906         }
36907         
36908         //this.switchLang('en'); // set the language to english..
36909        
36910         this.check({
36911             success:  function(response, opts)  {  // check successfull...
36912             
36913                 var res = this.processResponse(response);
36914                 this.checkFails =0;
36915                 if (!res.success) { // error!
36916                     this.checkFails = 5;
36917                     //console.log('call failure');
36918                     return this.failure(response,opts);
36919                 }
36920                 
36921                 if (!res.data.id) { // id=0 == login failure.
36922                     return this.show();
36923                 }
36924                 
36925                               
36926                         //console.log(success);
36927                 this.fillAuth(res.data);   
36928                 this.checkFails =0;
36929                 Roo.XComponent.build();
36930             },
36931             failure : this.show
36932         });
36933         
36934     }, 
36935     
36936     
36937     check: function(cfg) // called every so often to refresh cookie etc..
36938     {
36939         if (cfg.again) { // could be undefined..
36940             this.checkFails++;
36941         } else {
36942             this.checkFails = 0;
36943         }
36944         var _this = this;
36945         if (this.sending) {
36946             if ( this.checkFails > 4) {
36947                 Roo.MessageBox.alert("Error",  
36948                     "Error getting authentication status. - try reloading, or wait a while", function() {
36949                         _this.sending = false;
36950                     }); 
36951                 return;
36952             }
36953             cfg.again = true;
36954             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
36955             return;
36956         }
36957         this.sending = true;
36958         
36959         Roo.Ajax.request({  
36960             url: this.url,
36961             params: {
36962                 getAuthUser: true
36963             },  
36964             method: this.method,
36965             success:  cfg.success || this.success,
36966             failure : cfg.failure || this.failure,
36967             scope : this,
36968             callCfg : cfg
36969               
36970         });  
36971     }, 
36972     
36973     
36974     logout: function()
36975     {
36976         window.onbeforeunload = function() { }; // false does not work for IE..
36977         this.user = false;
36978         var _this = this;
36979         
36980         Roo.Ajax.request({  
36981             url: this.url,
36982             params: {
36983                 logout: 1
36984             },  
36985             method: 'GET',
36986             failure : function() {
36987                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
36988                     document.location = document.location.toString() + '?ts=' + Math.random();
36989                 });
36990                 
36991             },
36992             success : function() {
36993                 _this.user = false;
36994                 this.checkFails =0;
36995                 // fixme..
36996                 document.location = document.location.toString() + '?ts=' + Math.random();
36997             }
36998               
36999               
37000         }); 
37001     },
37002     
37003     processResponse : function (response)
37004     {
37005         var res = '';
37006         try {
37007             res = Roo.decode(response.responseText);
37008             // oops...
37009             if (typeof(res) != 'object') {
37010                 res = { success : false, errorMsg : res, errors : true };
37011             }
37012             if (typeof(res.success) == 'undefined') {
37013                 res.success = false;
37014             }
37015             
37016         } catch(e) {
37017             res = { success : false,  errorMsg : response.responseText, errors : true };
37018         }
37019         return res;
37020     },
37021     
37022     success : function(response, opts)  // check successfull...
37023     {  
37024         this.sending = false;
37025         var res = this.processResponse(response);
37026         if (!res.success) {
37027             return this.failure(response, opts);
37028         }
37029         if (!res.data || !res.data.id) {
37030             return this.failure(response,opts);
37031         }
37032         //console.log(res);
37033         this.fillAuth(res.data);
37034         
37035         this.checkFails =0;
37036         
37037     },
37038     
37039     
37040     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
37041     {
37042         this.authUser = -1;
37043         this.sending = false;
37044         var res = this.processResponse(response);
37045         //console.log(res);
37046         if ( this.checkFails > 2) {
37047         
37048             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
37049                 "Error getting authentication status. - try reloading"); 
37050             return;
37051         }
37052         opts.callCfg.again = true;
37053         this.check.defer(1000, this, [ opts.callCfg ]);
37054         return;  
37055     },
37056     
37057     
37058     
37059     fillAuth: function(au) {
37060         this.startAuthCheck();
37061         this.authUserId = au.id;
37062         this.authUser = au;
37063         this.lastChecked = new Date();
37064         this.fireEvent('refreshed', au);
37065         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
37066         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
37067         au.lang = au.lang || 'en';
37068         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
37069         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
37070         this.switchLang(au.lang );
37071         
37072      
37073         // open system... - -on setyp..
37074         if (this.authUserId  < 0) {
37075             Roo.MessageBox.alert("Warning", 
37076                 "This is an open system - please set up a admin user with a password.");  
37077         }
37078          
37079         //Pman.onload(); // which should do nothing if it's a re-auth result...
37080         
37081              
37082     },
37083     
37084     startAuthCheck : function() // starter for timeout checking..
37085     {
37086         if (this.intervalID) { // timer already in place...
37087             return false;
37088         }
37089         var _this = this;
37090         this.intervalID =  window.setInterval(function() {
37091               _this.check(false);
37092             }, 120000); // every 120 secs = 2mins..
37093         
37094         
37095     },
37096          
37097     
37098     switchLang : function (lang) 
37099     {
37100         _T = typeof(_T) == 'undefined' ? false : _T;
37101           if (!_T || !lang.length) {
37102             return;
37103         }
37104         
37105         if (!_T && lang != 'en') {
37106             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37107             return;
37108         }
37109         
37110         if (typeof(_T.en) == 'undefined') {
37111             _T.en = {};
37112             Roo.apply(_T.en, _T);
37113         }
37114         
37115         if (typeof(_T[lang]) == 'undefined') {
37116             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
37117             return;
37118         }
37119         
37120         
37121         Roo.apply(_T, _T[lang]);
37122         // just need to set the text values for everything...
37123         var _this = this;
37124         /* this will not work ...
37125         if (this.form) { 
37126             
37127                
37128             function formLabel(name, val) {
37129                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
37130             }
37131             
37132             formLabel('password', "Password"+':');
37133             formLabel('username', "Email Address"+':');
37134             formLabel('lang', "Language"+':');
37135             this.dialog.setTitle("Login");
37136             this.dialog.buttons[0].setText("Forgot Password");
37137             this.dialog.buttons[1].setText("Login");
37138         }
37139         */
37140         
37141         
37142     },
37143     
37144     
37145     title: "Login",
37146     modal: true,
37147     width:  350,
37148     //height: 230,
37149     height: 180,
37150     shadow: true,
37151     minWidth:200,
37152     minHeight:180,
37153     //proxyDrag: true,
37154     closable: false,
37155     draggable: false,
37156     collapsible: false,
37157     resizable: false,
37158     center: {  // needed??
37159         autoScroll:false,
37160         titlebar: false,
37161        // tabPosition: 'top',
37162         hideTabs: true,
37163         closeOnTab: true,
37164         alwaysShowTabs: false
37165     } ,
37166     listeners : {
37167         
37168         show  : function(dlg)
37169         {
37170             //console.log(this);
37171             this.form = this.layout.getRegion('center').activePanel.form;
37172             this.form.dialog = dlg;
37173             this.buttons[0].form = this.form;
37174             this.buttons[0].dialog = dlg;
37175             this.buttons[1].form = this.form;
37176             this.buttons[1].dialog = dlg;
37177            
37178            //this.resizeToLogo.defer(1000,this);
37179             // this is all related to resizing for logos..
37180             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
37181            //// if (!sz) {
37182              //   this.resizeToLogo.defer(1000,this);
37183              //   return;
37184            // }
37185             //var w = Ext.lib.Dom.getViewWidth() - 100;
37186             //var h = Ext.lib.Dom.getViewHeight() - 100;
37187             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
37188             //this.center();
37189             if (this.disabled) {
37190                 this.hide();
37191                 return;
37192             }
37193             
37194             if (this.user.id < 0) { // used for inital setup situations.
37195                 return;
37196             }
37197             
37198             if (this.intervalID) {
37199                 // remove the timer
37200                 window.clearInterval(this.intervalID);
37201                 this.intervalID = false;
37202             }
37203             
37204             
37205             if (Roo.get('loading')) {
37206                 Roo.get('loading').remove();
37207             }
37208             if (Roo.get('loading-mask')) {
37209                 Roo.get('loading-mask').hide();
37210             }
37211             
37212             //incomming._node = tnode;
37213             this.form.reset();
37214             //this.dialog.modal = !modal;
37215             //this.dialog.show();
37216             this.el.unmask(); 
37217             
37218             
37219             this.form.setValues({
37220                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
37221                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
37222             });
37223             
37224             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
37225             if (this.form.findField('username').getValue().length > 0 ){
37226                 this.form.findField('password').focus();
37227             } else {
37228                this.form.findField('username').focus();
37229             }
37230     
37231         }
37232     },
37233     items : [
37234          {
37235        
37236             xtype : 'ContentPanel',
37237             xns : Roo,
37238             region: 'center',
37239             fitToFrame : true,
37240             
37241             items : [
37242     
37243                 {
37244                
37245                     xtype : 'Form',
37246                     xns : Roo.form,
37247                     labelWidth: 100,
37248                     style : 'margin: 10px;',
37249                     
37250                     listeners : {
37251                         actionfailed : function(f, act) {
37252                             // form can return { errors: .... }
37253                                 
37254                             //act.result.errors // invalid form element list...
37255                             //act.result.errorMsg// invalid form element list...
37256                             
37257                             this.dialog.el.unmask();
37258                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
37259                                         "Login failed - communication error - try again.");
37260                                       
37261                         },
37262                         actioncomplete: function(re, act) {
37263                              
37264                             Roo.state.Manager.set(
37265                                 this.dialog.realm + '.username',  
37266                                     this.findField('username').getValue()
37267                             );
37268                             Roo.state.Manager.set(
37269                                 this.dialog.realm + '.lang',  
37270                                 this.findField('lang').getValue() 
37271                             );
37272                             
37273                             this.dialog.fillAuth(act.result.data);
37274                               
37275                             this.dialog.hide();
37276                             
37277                             if (Roo.get('loading-mask')) {
37278                                 Roo.get('loading-mask').show();
37279                             }
37280                             Roo.XComponent.build();
37281                             
37282                              
37283                             
37284                         }
37285                     },
37286                     items : [
37287                         {
37288                             xtype : 'TextField',
37289                             xns : Roo.form,
37290                             fieldLabel: "Email Address",
37291                             name: 'username',
37292                             width:200,
37293                             autoCreate : {tag: "input", type: "text", size: "20"}
37294                         },
37295                         {
37296                             xtype : 'TextField',
37297                             xns : Roo.form,
37298                             fieldLabel: "Password",
37299                             inputType: 'password',
37300                             name: 'password',
37301                             width:200,
37302                             autoCreate : {tag: "input", type: "text", size: "20"},
37303                             listeners : {
37304                                 specialkey : function(e,ev) {
37305                                     if (ev.keyCode == 13) {
37306                                         this.form.dialog.el.mask("Logging in");
37307                                         this.form.doAction('submit', {
37308                                             url: this.form.dialog.url,
37309                                             method: this.form.dialog.method
37310                                         });
37311                                     }
37312                                 }
37313                             }  
37314                         },
37315                         {
37316                             xtype : 'ComboBox',
37317                             xns : Roo.form,
37318                             fieldLabel: "Language",
37319                             name : 'langdisp',
37320                             store: {
37321                                 xtype : 'SimpleStore',
37322                                 fields: ['lang', 'ldisp'],
37323                                 data : [
37324                                     [ 'en', 'English' ],
37325                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
37326                                     [ 'zh_CN', '\u7C21\u4E2D' ]
37327                                 ]
37328                             },
37329                             
37330                             valueField : 'lang',
37331                             hiddenName:  'lang',
37332                             width: 200,
37333                             displayField:'ldisp',
37334                             typeAhead: false,
37335                             editable: false,
37336                             mode: 'local',
37337                             triggerAction: 'all',
37338                             emptyText:'Select a Language...',
37339                             selectOnFocus:true,
37340                             listeners : {
37341                                 select :  function(cb, rec, ix) {
37342                                     this.form.switchLang(rec.data.lang);
37343                                 }
37344                             }
37345                         
37346                         }
37347                     ]
37348                 }
37349                   
37350                 
37351             ]
37352         }
37353     ],
37354     buttons : [
37355         {
37356             xtype : 'Button',
37357             xns : 'Roo',
37358             text : "Forgot Password",
37359             listeners : {
37360                 click : function() {
37361                     //console.log(this);
37362                     var n = this.form.findField('username').getValue();
37363                     if (!n.length) {
37364                         Roo.MessageBox.alert("Error", "Fill in your email address");
37365                         return;
37366                     }
37367                     Roo.Ajax.request({
37368                         url: this.dialog.url,
37369                         params: {
37370                             passwordRequest: n
37371                         },
37372                         method: this.dialog.method,
37373                         success:  function(response, opts)  {  // check successfull...
37374                         
37375                             var res = this.dialog.processResponse(response);
37376                             if (!res.success) { // error!
37377                                Roo.MessageBox.alert("Error" ,
37378                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
37379                                return;
37380                             }
37381                             Roo.MessageBox.alert("Notice" ,
37382                                 "Please check you email for the Password Reset message");
37383                         },
37384                         failure : function() {
37385                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
37386                         }
37387                         
37388                     });
37389                 }
37390             }
37391         },
37392         {
37393             xtype : 'Button',
37394             xns : 'Roo',
37395             text : "Login",
37396             listeners : {
37397                 
37398                 click : function () {
37399                         
37400                     this.dialog.el.mask("Logging in");
37401                     this.form.doAction('submit', {
37402                             url: this.dialog.url,
37403                             method: this.dialog.method
37404                     });
37405                 }
37406             }
37407         }
37408     ]
37409   
37410   
37411 })
37412  
37413
37414
37415