roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     if (config.listeners || config.events) { 
76         Roo.BasicLayoutRegion.superclass.constructor.call(this,  { 
77             listeners : config.listeners || {}, 
78             events : config.events || {} 
79         });    
80     }
81 };
82
83 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
84
85     /**
86      * The id of the element associated with this object.  This is what we
87      * refer to as the "linked element" because the size and position of
88      * this element is used to determine when the drag and drop objects have
89      * interacted.
90      * @property id
91      * @type String
92      */
93     id: null,
94
95     /**
96      * Configuration attributes passed into the constructor
97      * @property config
98      * @type object
99      */
100     config: null,
101
102     /**
103      * The id of the element that will be dragged.  By default this is same
104      * as the linked element , but could be changed to another element. Ex:
105      * Roo.dd.DDProxy
106      * @property dragElId
107      * @type String
108      * @private
109      */
110     dragElId: null,
111
112     /**
113      * the id of the element that initiates the drag operation.  By default
114      * this is the linked element, but could be changed to be a child of this
115      * element.  This lets us do things like only starting the drag when the
116      * header element within the linked html element is clicked.
117      * @property handleElId
118      * @type String
119      * @private
120      */
121     handleElId: null,
122
123     /**
124      * An associative array of HTML tags that will be ignored if clicked.
125      * @property invalidHandleTypes
126      * @type {string: string}
127      */
128     invalidHandleTypes: null,
129
130     /**
131      * An associative array of ids for elements that will be ignored if clicked
132      * @property invalidHandleIds
133      * @type {string: string}
134      */
135     invalidHandleIds: null,
136
137     /**
138      * An indexted array of css class names for elements that will be ignored
139      * if clicked.
140      * @property invalidHandleClasses
141      * @type string[]
142      */
143     invalidHandleClasses: null,
144
145     /**
146      * The linked element's absolute X position at the time the drag was
147      * started
148      * @property startPageX
149      * @type int
150      * @private
151      */
152     startPageX: 0,
153
154     /**
155      * The linked element's absolute X position at the time the drag was
156      * started
157      * @property startPageY
158      * @type int
159      * @private
160      */
161     startPageY: 0,
162
163     /**
164      * The group defines a logical collection of DragDrop objects that are
165      * related.  Instances only get events when interacting with other
166      * DragDrop object in the same group.  This lets us define multiple
167      * groups using a single DragDrop subclass if we want.
168      * @property groups
169      * @type {string: string}
170      */
171     groups: null,
172
173     /**
174      * Individual drag/drop instances can be locked.  This will prevent
175      * onmousedown start drag.
176      * @property locked
177      * @type boolean
178      * @private
179      */
180     locked: false,
181
182     /**
183      * Lock this instance
184      * @method lock
185      */
186     lock: function() { this.locked = true; },
187
188     /**
189      * Unlock this instace
190      * @method unlock
191      */
192     unlock: function() { this.locked = false; },
193
194     /**
195      * By default, all insances can be a drop target.  This can be disabled by
196      * setting isTarget to false.
197      * @method isTarget
198      * @type boolean
199      */
200     isTarget: true,
201
202     /**
203      * The padding configured for this drag and drop object for calculating
204      * the drop zone intersection with this object.
205      * @method padding
206      * @type int[]
207      */
208     padding: null,
209
210     /**
211      * Cached reference to the linked element
212      * @property _domRef
213      * @private
214      */
215     _domRef: null,
216
217     /**
218      * Internal typeof flag
219      * @property __ygDragDrop
220      * @private
221      */
222     __ygDragDrop: true,
223
224     /**
225      * Set to true when horizontal contraints are applied
226      * @property constrainX
227      * @type boolean
228      * @private
229      */
230     constrainX: false,
231
232     /**
233      * Set to true when vertical contraints are applied
234      * @property constrainY
235      * @type boolean
236      * @private
237      */
238     constrainY: false,
239
240     /**
241      * The left constraint
242      * @property minX
243      * @type int
244      * @private
245      */
246     minX: 0,
247
248     /**
249      * The right constraint
250      * @property maxX
251      * @type int
252      * @private
253      */
254     maxX: 0,
255
256     /**
257      * The up constraint
258      * @property minY
259      * @type int
260      * @type int
261      * @private
262      */
263     minY: 0,
264
265     /**
266      * The down constraint
267      * @property maxY
268      * @type int
269      * @private
270      */
271     maxY: 0,
272
273     /**
274      * Maintain offsets when we resetconstraints.  Set to true when you want
275      * the position of the element relative to its parent to stay the same
276      * when the page changes
277      *
278      * @property maintainOffset
279      * @type boolean
280      */
281     maintainOffset: false,
282
283     /**
284      * Array of pixel locations the element will snap to if we specified a
285      * horizontal graduation/interval.  This array is generated automatically
286      * when you define a tick interval.
287      * @property xTicks
288      * @type int[]
289      */
290     xTicks: null,
291
292     /**
293      * Array of pixel locations the element will snap to if we specified a
294      * vertical graduation/interval.  This array is generated automatically
295      * when you define a tick interval.
296      * @property yTicks
297      * @type int[]
298      */
299     yTicks: null,
300
301     /**
302      * By default the drag and drop instance will only respond to the primary
303      * button click (left button for a right-handed mouse).  Set to true to
304      * allow drag and drop to start with any mouse click that is propogated
305      * by the browser
306      * @property primaryButtonOnly
307      * @type boolean
308      */
309     primaryButtonOnly: true,
310
311     /**
312      * The availabe property is false until the linked dom element is accessible.
313      * @property available
314      * @type boolean
315      */
316     available: false,
317
318     /**
319      * By default, drags can only be initiated if the mousedown occurs in the
320      * region the linked element is.  This is done in part to work around a
321      * bug in some browsers that mis-report the mousedown if the previous
322      * mouseup happened outside of the window.  This property is set to true
323      * if outer handles are defined.
324      *
325      * @property hasOuterHandles
326      * @type boolean
327      * @default false
328      */
329     hasOuterHandles: false,
330
331     /**
332      * Code that executes immediately before the startDrag event
333      * @method b4StartDrag
334      * @private
335      */
336     b4StartDrag: function(x, y) { },
337
338     /**
339      * Abstract method called after a drag/drop object is clicked
340      * and the drag or mousedown time thresholds have beeen met.
341      * @method startDrag
342      * @param {int} X click location
343      * @param {int} Y click location
344      */
345     startDrag: function(x, y) { /* override this */ },
346
347     /**
348      * Code that executes immediately before the onDrag event
349      * @method b4Drag
350      * @private
351      */
352     b4Drag: function(e) { },
353
354     /**
355      * Abstract method called during the onMouseMove event while dragging an
356      * object.
357      * @method onDrag
358      * @param {Event} e the mousemove event
359      */
360     onDrag: function(e) { /* override this */ },
361
362     /**
363      * Abstract method called when this element fist begins hovering over
364      * another DragDrop obj
365      * @method onDragEnter
366      * @param {Event} e the mousemove event
367      * @param {String|DragDrop[]} id In POINT mode, the element
368      * id this is hovering over.  In INTERSECT mode, an array of one or more
369      * dragdrop items being hovered over.
370      */
371     onDragEnter: function(e, id) { /* override this */ },
372
373     /**
374      * Code that executes immediately before the onDragOver event
375      * @method b4DragOver
376      * @private
377      */
378     b4DragOver: function(e) { },
379
380     /**
381      * Abstract method called when this element is hovering over another
382      * DragDrop obj
383      * @method onDragOver
384      * @param {Event} e the mousemove event
385      * @param {String|DragDrop[]} id In POINT mode, the element
386      * id this is hovering over.  In INTERSECT mode, an array of dd items
387      * being hovered over.
388      */
389     onDragOver: function(e, id) { /* override this */ },
390
391     /**
392      * Code that executes immediately before the onDragOut event
393      * @method b4DragOut
394      * @private
395      */
396     b4DragOut: function(e) { },
397
398     /**
399      * Abstract method called when we are no longer hovering over an element
400      * @method onDragOut
401      * @param {Event} e the mousemove event
402      * @param {String|DragDrop[]} id In POINT mode, the element
403      * id this was hovering over.  In INTERSECT mode, an array of dd items
404      * that the mouse is no longer over.
405      */
406     onDragOut: function(e, id) { /* override this */ },
407
408     /**
409      * Code that executes immediately before the onDragDrop event
410      * @method b4DragDrop
411      * @private
412      */
413     b4DragDrop: function(e) { },
414
415     /**
416      * Abstract method called when this item is dropped on another DragDrop
417      * obj
418      * @method onDragDrop
419      * @param {Event} e the mouseup event
420      * @param {String|DragDrop[]} id In POINT mode, the element
421      * id this was dropped on.  In INTERSECT mode, an array of dd items this
422      * was dropped on.
423      */
424     onDragDrop: function(e, id) { /* override this */ },
425
426     /**
427      * Abstract method called when this item is dropped on an area with no
428      * drop target
429      * @method onInvalidDrop
430      * @param {Event} e the mouseup event
431      */
432     onInvalidDrop: function(e) { /* override this */ },
433
434     /**
435      * Code that executes immediately before the endDrag event
436      * @method b4EndDrag
437      * @private
438      */
439     b4EndDrag: function(e) { },
440
441     /**
442      * Fired when we are done dragging the object
443      * @method endDrag
444      * @param {Event} e the mouseup event
445      */
446     endDrag: function(e) { /* override this */ },
447
448     /**
449      * Code executed immediately before the onMouseDown event
450      * @method b4MouseDown
451      * @param {Event} e the mousedown event
452      * @private
453      */
454     b4MouseDown: function(e) {  },
455
456     /**
457      * Event handler that fires when a drag/drop obj gets a mousedown
458      * @method onMouseDown
459      * @param {Event} e the mousedown event
460      */
461     onMouseDown: function(e) { /* override this */ },
462
463     /**
464      * Event handler that fires when a drag/drop obj gets a mouseup
465      * @method onMouseUp
466      * @param {Event} e the mouseup event
467      */
468     onMouseUp: function(e) { /* override this */ },
469
470     /**
471      * Override the onAvailable method to do what is needed after the initial
472      * position was determined.
473      * @method onAvailable
474      */
475     onAvailable: function () {
476     },
477
478     /*
479      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
480      * @type Object
481      */
482     defaultPadding : {left:0, right:0, top:0, bottom:0},
483
484     /*
485      * Initializes the drag drop object's constraints to restrict movement to a certain element.
486  *
487  * Usage:
488  <pre><code>
489  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
490                 { dragElId: "existingProxyDiv" });
491  dd.startDrag = function(){
492      this.constrainTo("parent-id");
493  };
494  </code></pre>
495  * Or you can initalize it using the {@link Roo.Element} object:
496  <pre><code>
497  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
498      startDrag : function(){
499          this.constrainTo("parent-id");
500      }
501  });
502  </code></pre>
503      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
504      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
505      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
506      * an object containing the sides to pad. For example: {right:10, bottom:10}
507      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
508      */
509     constrainTo : function(constrainTo, pad, inContent){
510         if(typeof pad == "number"){
511             pad = {left: pad, right:pad, top:pad, bottom:pad};
512         }
513         pad = pad || this.defaultPadding;
514         var b = Roo.get(this.getEl()).getBox();
515         var ce = Roo.get(constrainTo);
516         var s = ce.getScroll();
517         var c, cd = ce.dom;
518         if(cd == document.body){
519             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
520         }else{
521             xy = ce.getXY();
522             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
523         }
524
525
526         var topSpace = b.y - c.y;
527         var leftSpace = b.x - c.x;
528
529         this.resetConstraints();
530         this.setXConstraint(leftSpace - (pad.left||0), // left
531                 c.width - leftSpace - b.width - (pad.right||0) //right
532         );
533         this.setYConstraint(topSpace - (pad.top||0), //top
534                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
535         );
536     },
537
538     /**
539      * Returns a reference to the linked element
540      * @method getEl
541      * @return {HTMLElement} the html element
542      */
543     getEl: function() {
544         if (!this._domRef) {
545             this._domRef = Roo.getDom(this.id);
546         }
547
548         return this._domRef;
549     },
550
551     /**
552      * Returns a reference to the actual element to drag.  By default this is
553      * the same as the html element, but it can be assigned to another
554      * element. An example of this can be found in Roo.dd.DDProxy
555      * @method getDragEl
556      * @return {HTMLElement} the html element
557      */
558     getDragEl: function() {
559         return Roo.getDom(this.dragElId);
560     },
561
562     /**
563      * Sets up the DragDrop object.  Must be called in the constructor of any
564      * Roo.dd.DragDrop subclass
565      * @method init
566      * @param id the id of the linked element
567      * @param {String} sGroup the group of related items
568      * @param {object} config configuration attributes
569      */
570     init: function(id, sGroup, config) {
571         this.initTarget(id, sGroup, config);
572         Event.on(this.id, "mousedown", this.handleMouseDown, this);
573         // Event.on(this.id, "selectstart", Event.preventDefault);
574     },
575
576     /**
577      * Initializes Targeting functionality only... the object does not
578      * get a mousedown handler.
579      * @method initTarget
580      * @param id the id of the linked element
581      * @param {String} sGroup the group of related items
582      * @param {object} config configuration attributes
583      */
584     initTarget: function(id, sGroup, config) {
585
586         // configuration attributes
587         this.config = config || {};
588
589         // create a local reference to the drag and drop manager
590         this.DDM = Roo.dd.DDM;
591         // initialize the groups array
592         this.groups = {};
593
594         // assume that we have an element reference instead of an id if the
595         // parameter is not a string
596         if (typeof id !== "string") {
597             id = Roo.id(id);
598         }
599
600         // set the id
601         this.id = id;
602
603         // add to an interaction group
604         this.addToGroup((sGroup) ? sGroup : "default");
605
606         // We don't want to register this as the handle with the manager
607         // so we just set the id rather than calling the setter.
608         this.handleElId = id;
609
610         // the linked element is the element that gets dragged by default
611         this.setDragElId(id);
612
613         // by default, clicked anchors will not start drag operations.
614         this.invalidHandleTypes = { A: "A" };
615         this.invalidHandleIds = {};
616         this.invalidHandleClasses = [];
617
618         this.applyConfig();
619
620         this.handleOnAvailable();
621     },
622
623     /**
624      * Applies the configuration parameters that were passed into the constructor.
625      * This is supposed to happen at each level through the inheritance chain.  So
626      * a DDProxy implentation will execute apply config on DDProxy, DD, and
627      * DragDrop in order to get all of the parameters that are available in
628      * each object.
629      * @method applyConfig
630      */
631     applyConfig: function() {
632
633         // configurable properties:
634         //    padding, isTarget, maintainOffset, primaryButtonOnly
635         this.padding           = this.config.padding || [0, 0, 0, 0];
636         this.isTarget          = (this.config.isTarget !== false);
637         this.maintainOffset    = (this.config.maintainOffset);
638         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
639
640     },
641
642     /**
643      * Executed when the linked element is available
644      * @method handleOnAvailable
645      * @private
646      */
647     handleOnAvailable: function() {
648         this.available = true;
649         this.resetConstraints();
650         this.onAvailable();
651     },
652
653      /**
654      * Configures the padding for the target zone in px.  Effectively expands
655      * (or reduces) the virtual object size for targeting calculations.
656      * Supports css-style shorthand; if only one parameter is passed, all sides
657      * will have that padding, and if only two are passed, the top and bottom
658      * will have the first param, the left and right the second.
659      * @method setPadding
660      * @param {int} iTop    Top pad
661      * @param {int} iRight  Right pad
662      * @param {int} iBot    Bot pad
663      * @param {int} iLeft   Left pad
664      */
665     setPadding: function(iTop, iRight, iBot, iLeft) {
666         // this.padding = [iLeft, iRight, iTop, iBot];
667         if (!iRight && 0 !== iRight) {
668             this.padding = [iTop, iTop, iTop, iTop];
669         } else if (!iBot && 0 !== iBot) {
670             this.padding = [iTop, iRight, iTop, iRight];
671         } else {
672             this.padding = [iTop, iRight, iBot, iLeft];
673         }
674     },
675
676     /**
677      * Stores the initial placement of the linked element.
678      * @method setInitialPosition
679      * @param {int} diffX   the X offset, default 0
680      * @param {int} diffY   the Y offset, default 0
681      */
682     setInitPosition: function(diffX, diffY) {
683         var el = this.getEl();
684
685         if (!this.DDM.verifyEl(el)) {
686             return;
687         }
688
689         var dx = diffX || 0;
690         var dy = diffY || 0;
691
692         var p = Dom.getXY( el );
693
694         this.initPageX = p[0] - dx;
695         this.initPageY = p[1] - dy;
696
697         this.lastPageX = p[0];
698         this.lastPageY = p[1];
699
700
701         this.setStartPosition(p);
702     },
703
704     /**
705      * Sets the start position of the element.  This is set when the obj
706      * is initialized, the reset when a drag is started.
707      * @method setStartPosition
708      * @param pos current position (from previous lookup)
709      * @private
710      */
711     setStartPosition: function(pos) {
712         var p = pos || Dom.getXY( this.getEl() );
713         this.deltaSetXY = null;
714
715         this.startPageX = p[0];
716         this.startPageY = p[1];
717     },
718
719     /**
720      * Add this instance to a group of related drag/drop objects.  All
721      * instances belong to at least one group, and can belong to as many
722      * groups as needed.
723      * @method addToGroup
724      * @param sGroup {string} the name of the group
725      */
726     addToGroup: function(sGroup) {
727         this.groups[sGroup] = true;
728         this.DDM.regDragDrop(this, sGroup);
729     },
730
731     /**
732      * Remove's this instance from the supplied interaction group
733      * @method removeFromGroup
734      * @param {string}  sGroup  The group to drop
735      */
736     removeFromGroup: function(sGroup) {
737         if (this.groups[sGroup]) {
738             delete this.groups[sGroup];
739         }
740
741         this.DDM.removeDDFromGroup(this, sGroup);
742     },
743
744     /**
745      * Allows you to specify that an element other than the linked element
746      * will be moved with the cursor during a drag
747      * @method setDragElId
748      * @param id {string} the id of the element that will be used to initiate the drag
749      */
750     setDragElId: function(id) {
751         this.dragElId = id;
752     },
753
754     /**
755      * Allows you to specify a child of the linked element that should be
756      * used to initiate the drag operation.  An example of this would be if
757      * you have a content div with text and links.  Clicking anywhere in the
758      * content area would normally start the drag operation.  Use this method
759      * to specify that an element inside of the content div is the element
760      * that starts the drag operation.
761      * @method setHandleElId
762      * @param id {string} the id of the element that will be used to
763      * initiate the drag.
764      */
765     setHandleElId: function(id) {
766         if (typeof id !== "string") {
767             id = Roo.id(id);
768         }
769         this.handleElId = id;
770         this.DDM.regHandle(this.id, id);
771     },
772
773     /**
774      * Allows you to set an element outside of the linked element as a drag
775      * handle
776      * @method setOuterHandleElId
777      * @param id the id of the element that will be used to initiate the drag
778      */
779     setOuterHandleElId: function(id) {
780         if (typeof id !== "string") {
781             id = Roo.id(id);
782         }
783         Event.on(id, "mousedown",
784                 this.handleMouseDown, this);
785         this.setHandleElId(id);
786
787         this.hasOuterHandles = true;
788     },
789
790     /**
791      * Remove all drag and drop hooks for this element
792      * @method unreg
793      */
794     unreg: function() {
795         Event.un(this.id, "mousedown",
796                 this.handleMouseDown);
797         this._domRef = null;
798         this.DDM._remove(this);
799     },
800
801     destroy : function(){
802         this.unreg();
803     },
804
805     /**
806      * Returns true if this instance is locked, or the drag drop mgr is locked
807      * (meaning that all drag/drop is disabled on the page.)
808      * @method isLocked
809      * @return {boolean} true if this obj or all drag/drop is locked, else
810      * false
811      */
812     isLocked: function() {
813         return (this.DDM.isLocked() || this.locked);
814     },
815
816     /**
817      * Fired when this object is clicked
818      * @method handleMouseDown
819      * @param {Event} e
820      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
821      * @private
822      */
823     handleMouseDown: function(e, oDD){
824         if (this.primaryButtonOnly && e.button != 0) {
825             return;
826         }
827
828         if (this.isLocked()) {
829             return;
830         }
831
832         this.DDM.refreshCache(this.groups);
833
834         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
835         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
836         } else {
837             if (this.clickValidator(e)) {
838
839                 // set the initial element position
840                 this.setStartPosition();
841
842
843                 this.b4MouseDown(e);
844                 this.onMouseDown(e);
845
846                 this.DDM.handleMouseDown(e, this);
847
848                 this.DDM.stopEvent(e);
849             } else {
850
851
852             }
853         }
854     },
855
856     clickValidator: function(e) {
857         var target = e.getTarget();
858         return ( this.isValidHandleChild(target) &&
859                     (this.id == this.handleElId ||
860                         this.DDM.handleWasClicked(target, this.id)) );
861     },
862
863     /**
864      * Allows you to specify a tag name that should not start a drag operation
865      * when clicked.  This is designed to facilitate embedding links within a
866      * drag handle that do something other than start the drag.
867      * @method addInvalidHandleType
868      * @param {string} tagName the type of element to exclude
869      */
870     addInvalidHandleType: function(tagName) {
871         var type = tagName.toUpperCase();
872         this.invalidHandleTypes[type] = type;
873     },
874
875     /**
876      * Lets you to specify an element id for a child of a drag handle
877      * that should not initiate a drag
878      * @method addInvalidHandleId
879      * @param {string} id the element id of the element you wish to ignore
880      */
881     addInvalidHandleId: function(id) {
882         if (typeof id !== "string") {
883             id = Roo.id(id);
884         }
885         this.invalidHandleIds[id] = id;
886     },
887
888     /**
889      * Lets you specify a css class of elements that will not initiate a drag
890      * @method addInvalidHandleClass
891      * @param {string} cssClass the class of the elements you wish to ignore
892      */
893     addInvalidHandleClass: function(cssClass) {
894         this.invalidHandleClasses.push(cssClass);
895     },
896
897     /**
898      * Unsets an excluded tag name set by addInvalidHandleType
899      * @method removeInvalidHandleType
900      * @param {string} tagName the type of element to unexclude
901      */
902     removeInvalidHandleType: function(tagName) {
903         var type = tagName.toUpperCase();
904         // this.invalidHandleTypes[type] = null;
905         delete this.invalidHandleTypes[type];
906     },
907
908     /**
909      * Unsets an invalid handle id
910      * @method removeInvalidHandleId
911      * @param {string} id the id of the element to re-enable
912      */
913     removeInvalidHandleId: function(id) {
914         if (typeof id !== "string") {
915             id = Roo.id(id);
916         }
917         delete this.invalidHandleIds[id];
918     },
919
920     /**
921      * Unsets an invalid css class
922      * @method removeInvalidHandleClass
923      * @param {string} cssClass the class of the element(s) you wish to
924      * re-enable
925      */
926     removeInvalidHandleClass: function(cssClass) {
927         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
928             if (this.invalidHandleClasses[i] == cssClass) {
929                 delete this.invalidHandleClasses[i];
930             }
931         }
932     },
933
934     /**
935      * Checks the tag exclusion list to see if this click should be ignored
936      * @method isValidHandleChild
937      * @param {HTMLElement} node the HTMLElement to evaluate
938      * @return {boolean} true if this is a valid tag type, false if not
939      */
940     isValidHandleChild: function(node) {
941
942         var valid = true;
943         // var n = (node.nodeName == "#text") ? node.parentNode : node;
944         var nodeName;
945         try {
946             nodeName = node.nodeName.toUpperCase();
947         } catch(e) {
948             nodeName = node.nodeName;
949         }
950         valid = valid && !this.invalidHandleTypes[nodeName];
951         valid = valid && !this.invalidHandleIds[node.id];
952
953         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
954             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
955         }
956
957
958         return valid;
959
960     },
961
962     /**
963      * Create the array of horizontal tick marks if an interval was specified
964      * in setXConstraint().
965      * @method setXTicks
966      * @private
967      */
968     setXTicks: function(iStartX, iTickSize) {
969         this.xTicks = [];
970         this.xTickSize = iTickSize;
971
972         var tickMap = {};
973
974         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
975             if (!tickMap[i]) {
976                 this.xTicks[this.xTicks.length] = i;
977                 tickMap[i] = true;
978             }
979         }
980
981         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
982             if (!tickMap[i]) {
983                 this.xTicks[this.xTicks.length] = i;
984                 tickMap[i] = true;
985             }
986         }
987
988         this.xTicks.sort(this.DDM.numericSort) ;
989     },
990
991     /**
992      * Create the array of vertical tick marks if an interval was specified in
993      * setYConstraint().
994      * @method setYTicks
995      * @private
996      */
997     setYTicks: function(iStartY, iTickSize) {
998         this.yTicks = [];
999         this.yTickSize = iTickSize;
1000
1001         var tickMap = {};
1002
1003         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1004             if (!tickMap[i]) {
1005                 this.yTicks[this.yTicks.length] = i;
1006                 tickMap[i] = true;
1007             }
1008         }
1009
1010         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1011             if (!tickMap[i]) {
1012                 this.yTicks[this.yTicks.length] = i;
1013                 tickMap[i] = true;
1014             }
1015         }
1016
1017         this.yTicks.sort(this.DDM.numericSort) ;
1018     },
1019
1020     /**
1021      * By default, the element can be dragged any place on the screen.  Use
1022      * this method to limit the horizontal travel of the element.  Pass in
1023      * 0,0 for the parameters if you want to lock the drag to the y axis.
1024      * @method setXConstraint
1025      * @param {int} iLeft the number of pixels the element can move to the left
1026      * @param {int} iRight the number of pixels the element can move to the
1027      * right
1028      * @param {int} iTickSize optional parameter for specifying that the
1029      * element
1030      * should move iTickSize pixels at a time.
1031      */
1032     setXConstraint: function(iLeft, iRight, iTickSize) {
1033         this.leftConstraint = iLeft;
1034         this.rightConstraint = iRight;
1035
1036         this.minX = this.initPageX - iLeft;
1037         this.maxX = this.initPageX + iRight;
1038         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1039
1040         this.constrainX = true;
1041     },
1042
1043     /**
1044      * Clears any constraints applied to this instance.  Also clears ticks
1045      * since they can't exist independent of a constraint at this time.
1046      * @method clearConstraints
1047      */
1048     clearConstraints: function() {
1049         this.constrainX = false;
1050         this.constrainY = false;
1051         this.clearTicks();
1052     },
1053
1054     /**
1055      * Clears any tick interval defined for this instance
1056      * @method clearTicks
1057      */
1058     clearTicks: function() {
1059         this.xTicks = null;
1060         this.yTicks = null;
1061         this.xTickSize = 0;
1062         this.yTickSize = 0;
1063     },
1064
1065     /**
1066      * By default, the element can be dragged any place on the screen.  Set
1067      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1068      * parameters if you want to lock the drag to the x axis.
1069      * @method setYConstraint
1070      * @param {int} iUp the number of pixels the element can move up
1071      * @param {int} iDown the number of pixels the element can move down
1072      * @param {int} iTickSize optional parameter for specifying that the
1073      * element should move iTickSize pixels at a time.
1074      */
1075     setYConstraint: function(iUp, iDown, iTickSize) {
1076         this.topConstraint = iUp;
1077         this.bottomConstraint = iDown;
1078
1079         this.minY = this.initPageY - iUp;
1080         this.maxY = this.initPageY + iDown;
1081         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1082
1083         this.constrainY = true;
1084
1085     },
1086
1087     /**
1088      * resetConstraints must be called if you manually reposition a dd element.
1089      * @method resetConstraints
1090      * @param {boolean} maintainOffset
1091      */
1092     resetConstraints: function() {
1093
1094
1095         // Maintain offsets if necessary
1096         if (this.initPageX || this.initPageX === 0) {
1097             // figure out how much this thing has moved
1098             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1099             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1100
1101             this.setInitPosition(dx, dy);
1102
1103         // This is the first time we have detected the element's position
1104         } else {
1105             this.setInitPosition();
1106         }
1107
1108         if (this.constrainX) {
1109             this.setXConstraint( this.leftConstraint,
1110                                  this.rightConstraint,
1111                                  this.xTickSize        );
1112         }
1113
1114         if (this.constrainY) {
1115             this.setYConstraint( this.topConstraint,
1116                                  this.bottomConstraint,
1117                                  this.yTickSize         );
1118         }
1119     },
1120
1121     /**
1122      * Normally the drag element is moved pixel by pixel, but we can specify
1123      * that it move a number of pixels at a time.  This method resolves the
1124      * location when we have it set up like this.
1125      * @method getTick
1126      * @param {int} val where we want to place the object
1127      * @param {int[]} tickArray sorted array of valid points
1128      * @return {int} the closest tick
1129      * @private
1130      */
1131     getTick: function(val, tickArray) {
1132
1133         if (!tickArray) {
1134             // If tick interval is not defined, it is effectively 1 pixel,
1135             // so we return the value passed to us.
1136             return val;
1137         } else if (tickArray[0] >= val) {
1138             // The value is lower than the first tick, so we return the first
1139             // tick.
1140             return tickArray[0];
1141         } else {
1142             for (var i=0, len=tickArray.length; i<len; ++i) {
1143                 var next = i + 1;
1144                 if (tickArray[next] && tickArray[next] >= val) {
1145                     var diff1 = val - tickArray[i];
1146                     var diff2 = tickArray[next] - val;
1147                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1148                 }
1149             }
1150
1151             // The value is larger than the last tick, so we return the last
1152             // tick.
1153             return tickArray[tickArray.length - 1];
1154         }
1155     },
1156
1157     /**
1158      * toString method
1159      * @method toString
1160      * @return {string} string representation of the dd obj
1161      */
1162     toString: function() {
1163         return ("DragDrop " + this.id);
1164     }
1165
1166 });
1167
1168 })();
1169 /*
1170  * Based on:
1171  * Ext JS Library 1.1.1
1172  * Copyright(c) 2006-2007, Ext JS, LLC.
1173  *
1174  * Originally Released Under LGPL - original licence link has changed is not relivant.
1175  *
1176  * Fork - LGPL
1177  * <script type="text/javascript">
1178  */
1179
1180
1181 /**
1182  * The drag and drop utility provides a framework for building drag and drop
1183  * applications.  In addition to enabling drag and drop for specific elements,
1184  * the drag and drop elements are tracked by the manager class, and the
1185  * interactions between the various elements are tracked during the drag and
1186  * the implementing code is notified about these important moments.
1187  */
1188
1189 // Only load the library once.  Rewriting the manager class would orphan
1190 // existing drag and drop instances.
1191 if (!Roo.dd.DragDropMgr) {
1192
1193 /**
1194  * @class Roo.dd.DragDropMgr
1195  * DragDropMgr is a singleton that tracks the element interaction for
1196  * all DragDrop items in the window.  Generally, you will not call
1197  * this class directly, but it does have helper methods that could
1198  * be useful in your DragDrop implementations.
1199  * @singleton
1200  */
1201 Roo.dd.DragDropMgr = function() {
1202
1203     var Event = Roo.EventManager;
1204
1205     return {
1206
1207         /**
1208          * Two dimensional Array of registered DragDrop objects.  The first
1209          * dimension is the DragDrop item group, the second the DragDrop
1210          * object.
1211          * @property ids
1212          * @type {string: string}
1213          * @private
1214          * @static
1215          */
1216         ids: {},
1217
1218         /**
1219          * Array of element ids defined as drag handles.  Used to determine
1220          * if the element that generated the mousedown event is actually the
1221          * handle and not the html element itself.
1222          * @property handleIds
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         handleIds: {},
1228
1229         /**
1230          * the DragDrop object that is currently being dragged
1231          * @property dragCurrent
1232          * @type DragDrop
1233          * @private
1234          * @static
1235          **/
1236         dragCurrent: null,
1237
1238         /**
1239          * the DragDrop object(s) that are being hovered over
1240          * @property dragOvers
1241          * @type Array
1242          * @private
1243          * @static
1244          */
1245         dragOvers: {},
1246
1247         /**
1248          * the X distance between the cursor and the object being dragged
1249          * @property deltaX
1250          * @type int
1251          * @private
1252          * @static
1253          */
1254         deltaX: 0,
1255
1256         /**
1257          * the Y distance between the cursor and the object being dragged
1258          * @property deltaY
1259          * @type int
1260          * @private
1261          * @static
1262          */
1263         deltaY: 0,
1264
1265         /**
1266          * Flag to determine if we should prevent the default behavior of the
1267          * events we define. By default this is true, but this can be set to
1268          * false if you need the default behavior (not recommended)
1269          * @property preventDefault
1270          * @type boolean
1271          * @static
1272          */
1273         preventDefault: true,
1274
1275         /**
1276          * Flag to determine if we should stop the propagation of the events
1277          * we generate. This is true by default but you may want to set it to
1278          * false if the html element contains other features that require the
1279          * mouse click.
1280          * @property stopPropagation
1281          * @type boolean
1282          * @static
1283          */
1284         stopPropagation: true,
1285
1286         /**
1287          * Internal flag that is set to true when drag and drop has been
1288          * intialized
1289          * @property initialized
1290          * @private
1291          * @static
1292          */
1293         initalized: false,
1294
1295         /**
1296          * All drag and drop can be disabled.
1297          * @property locked
1298          * @private
1299          * @static
1300          */
1301         locked: false,
1302
1303         /**
1304          * Called the first time an element is registered.
1305          * @method init
1306          * @private
1307          * @static
1308          */
1309         init: function() {
1310             this.initialized = true;
1311         },
1312
1313         /**
1314          * In point mode, drag and drop interaction is defined by the
1315          * location of the cursor during the drag/drop
1316          * @property POINT
1317          * @type int
1318          * @static
1319          */
1320         POINT: 0,
1321
1322         /**
1323          * In intersect mode, drag and drop interactio nis defined by the
1324          * overlap of two or more drag and drop objects.
1325          * @property INTERSECT
1326          * @type int
1327          * @static
1328          */
1329         INTERSECT: 1,
1330
1331         /**
1332          * The current drag and drop mode.  Default: POINT
1333          * @property mode
1334          * @type int
1335          * @static
1336          */
1337         mode: 0,
1338
1339         /**
1340          * Runs method on all drag and drop objects
1341          * @method _execOnAll
1342          * @private
1343          * @static
1344          */
1345         _execOnAll: function(sMethod, args) {
1346             for (var i in this.ids) {
1347                 for (var j in this.ids[i]) {
1348                     var oDD = this.ids[i][j];
1349                     if (! this.isTypeOfDD(oDD)) {
1350                         continue;
1351                     }
1352                     oDD[sMethod].apply(oDD, args);
1353                 }
1354             }
1355         },
1356
1357         /**
1358          * Drag and drop initialization.  Sets up the global event handlers
1359          * @method _onLoad
1360          * @private
1361          * @static
1362          */
1363         _onLoad: function() {
1364
1365             this.init();
1366
1367
1368             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1369             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1370             Event.on(window,   "unload",    this._onUnload, this, true);
1371             Event.on(window,   "resize",    this._onResize, this, true);
1372             // Event.on(window,   "mouseout",    this._test);
1373
1374         },
1375
1376         /**
1377          * Reset constraints on all drag and drop objs
1378          * @method _onResize
1379          * @private
1380          * @static
1381          */
1382         _onResize: function(e) {
1383             this._execOnAll("resetConstraints", []);
1384         },
1385
1386         /**
1387          * Lock all drag and drop functionality
1388          * @method lock
1389          * @static
1390          */
1391         lock: function() { this.locked = true; },
1392
1393         /**
1394          * Unlock all drag and drop functionality
1395          * @method unlock
1396          * @static
1397          */
1398         unlock: function() { this.locked = false; },
1399
1400         /**
1401          * Is drag and drop locked?
1402          * @method isLocked
1403          * @return {boolean} True if drag and drop is locked, false otherwise.
1404          * @static
1405          */
1406         isLocked: function() { return this.locked; },
1407
1408         /**
1409          * Location cache that is set for all drag drop objects when a drag is
1410          * initiated, cleared when the drag is finished.
1411          * @property locationCache
1412          * @private
1413          * @static
1414          */
1415         locationCache: {},
1416
1417         /**
1418          * Set useCache to false if you want to force object the lookup of each
1419          * drag and drop linked element constantly during a drag.
1420          * @property useCache
1421          * @type boolean
1422          * @static
1423          */
1424         useCache: true,
1425
1426         /**
1427          * The number of pixels that the mouse needs to move after the
1428          * mousedown before the drag is initiated.  Default=3;
1429          * @property clickPixelThresh
1430          * @type int
1431          * @static
1432          */
1433         clickPixelThresh: 3,
1434
1435         /**
1436          * The number of milliseconds after the mousedown event to initiate the
1437          * drag if we don't get a mouseup event. Default=1000
1438          * @property clickTimeThresh
1439          * @type int
1440          * @static
1441          */
1442         clickTimeThresh: 350,
1443
1444         /**
1445          * Flag that indicates that either the drag pixel threshold or the
1446          * mousdown time threshold has been met
1447          * @property dragThreshMet
1448          * @type boolean
1449          * @private
1450          * @static
1451          */
1452         dragThreshMet: false,
1453
1454         /**
1455          * Timeout used for the click time threshold
1456          * @property clickTimeout
1457          * @type Object
1458          * @private
1459          * @static
1460          */
1461         clickTimeout: null,
1462
1463         /**
1464          * The X position of the mousedown event stored for later use when a
1465          * drag threshold is met.
1466          * @property startX
1467          * @type int
1468          * @private
1469          * @static
1470          */
1471         startX: 0,
1472
1473         /**
1474          * The Y position of the mousedown event stored for later use when a
1475          * drag threshold is met.
1476          * @property startY
1477          * @type int
1478          * @private
1479          * @static
1480          */
1481         startY: 0,
1482
1483         /**
1484          * Each DragDrop instance must be registered with the DragDropMgr.
1485          * This is executed in DragDrop.init()
1486          * @method regDragDrop
1487          * @param {DragDrop} oDD the DragDrop object to register
1488          * @param {String} sGroup the name of the group this element belongs to
1489          * @static
1490          */
1491         regDragDrop: function(oDD, sGroup) {
1492             if (!this.initialized) { this.init(); }
1493
1494             if (!this.ids[sGroup]) {
1495                 this.ids[sGroup] = {};
1496             }
1497             this.ids[sGroup][oDD.id] = oDD;
1498         },
1499
1500         /**
1501          * Removes the supplied dd instance from the supplied group. Executed
1502          * by DragDrop.removeFromGroup, so don't call this function directly.
1503          * @method removeDDFromGroup
1504          * @private
1505          * @static
1506          */
1507         removeDDFromGroup: function(oDD, sGroup) {
1508             if (!this.ids[sGroup]) {
1509                 this.ids[sGroup] = {};
1510             }
1511
1512             var obj = this.ids[sGroup];
1513             if (obj && obj[oDD.id]) {
1514                 delete obj[oDD.id];
1515             }
1516         },
1517
1518         /**
1519          * Unregisters a drag and drop item.  This is executed in
1520          * DragDrop.unreg, use that method instead of calling this directly.
1521          * @method _remove
1522          * @private
1523          * @static
1524          */
1525         _remove: function(oDD) {
1526             for (var g in oDD.groups) {
1527                 if (g && this.ids[g][oDD.id]) {
1528                     delete this.ids[g][oDD.id];
1529                 }
1530             }
1531             delete this.handleIds[oDD.id];
1532         },
1533
1534         /**
1535          * Each DragDrop handle element must be registered.  This is done
1536          * automatically when executing DragDrop.setHandleElId()
1537          * @method regHandle
1538          * @param {String} sDDId the DragDrop id this element is a handle for
1539          * @param {String} sHandleId the id of the element that is the drag
1540          * handle
1541          * @static
1542          */
1543         regHandle: function(sDDId, sHandleId) {
1544             if (!this.handleIds[sDDId]) {
1545                 this.handleIds[sDDId] = {};
1546             }
1547             this.handleIds[sDDId][sHandleId] = sHandleId;
1548         },
1549
1550         /**
1551          * Utility function to determine if a given element has been
1552          * registered as a drag drop item.
1553          * @method isDragDrop
1554          * @param {String} id the element id to check
1555          * @return {boolean} true if this element is a DragDrop item,
1556          * false otherwise
1557          * @static
1558          */
1559         isDragDrop: function(id) {
1560             return ( this.getDDById(id) ) ? true : false;
1561         },
1562
1563         /**
1564          * Returns the drag and drop instances that are in all groups the
1565          * passed in instance belongs to.
1566          * @method getRelated
1567          * @param {DragDrop} p_oDD the obj to get related data for
1568          * @param {boolean} bTargetsOnly if true, only return targetable objs
1569          * @return {DragDrop[]} the related instances
1570          * @static
1571          */
1572         getRelated: function(p_oDD, bTargetsOnly) {
1573             var oDDs = [];
1574             for (var i in p_oDD.groups) {
1575                 for (j in this.ids[i]) {
1576                     var dd = this.ids[i][j];
1577                     if (! this.isTypeOfDD(dd)) {
1578                         continue;
1579                     }
1580                     if (!bTargetsOnly || dd.isTarget) {
1581                         oDDs[oDDs.length] = dd;
1582                     }
1583                 }
1584             }
1585
1586             return oDDs;
1587         },
1588
1589         /**
1590          * Returns true if the specified dd target is a legal target for
1591          * the specifice drag obj
1592          * @method isLegalTarget
1593          * @param {DragDrop} the drag obj
1594          * @param {DragDrop} the target
1595          * @return {boolean} true if the target is a legal target for the
1596          * dd obj
1597          * @static
1598          */
1599         isLegalTarget: function (oDD, oTargetDD) {
1600             var targets = this.getRelated(oDD, true);
1601             for (var i=0, len=targets.length;i<len;++i) {
1602                 if (targets[i].id == oTargetDD.id) {
1603                     return true;
1604                 }
1605             }
1606
1607             return false;
1608         },
1609
1610         /**
1611          * My goal is to be able to transparently determine if an object is
1612          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1613          * returns "object", oDD.constructor.toString() always returns
1614          * "DragDrop" and not the name of the subclass.  So for now it just
1615          * evaluates a well-known variable in DragDrop.
1616          * @method isTypeOfDD
1617          * @param {Object} the object to evaluate
1618          * @return {boolean} true if typeof oDD = DragDrop
1619          * @static
1620          */
1621         isTypeOfDD: function (oDD) {
1622             return (oDD && oDD.__ygDragDrop);
1623         },
1624
1625         /**
1626          * Utility function to determine if a given element has been
1627          * registered as a drag drop handle for the given Drag Drop object.
1628          * @method isHandle
1629          * @param {String} id the element id to check
1630          * @return {boolean} true if this element is a DragDrop handle, false
1631          * otherwise
1632          * @static
1633          */
1634         isHandle: function(sDDId, sHandleId) {
1635             return ( this.handleIds[sDDId] &&
1636                             this.handleIds[sDDId][sHandleId] );
1637         },
1638
1639         /**
1640          * Returns the DragDrop instance for a given id
1641          * @method getDDById
1642          * @param {String} id the id of the DragDrop object
1643          * @return {DragDrop} the drag drop object, null if it is not found
1644          * @static
1645          */
1646         getDDById: function(id) {
1647             for (var i in this.ids) {
1648                 if (this.ids[i][id]) {
1649                     return this.ids[i][id];
1650                 }
1651             }
1652             return null;
1653         },
1654
1655         /**
1656          * Fired after a registered DragDrop object gets the mousedown event.
1657          * Sets up the events required to track the object being dragged
1658          * @method handleMouseDown
1659          * @param {Event} e the event
1660          * @param oDD the DragDrop object being dragged
1661          * @private
1662          * @static
1663          */
1664         handleMouseDown: function(e, oDD) {
1665             if(Roo.QuickTips){
1666                 Roo.QuickTips.disable();
1667             }
1668             this.currentTarget = e.getTarget();
1669
1670             this.dragCurrent = oDD;
1671
1672             var el = oDD.getEl();
1673
1674             // track start position
1675             this.startX = e.getPageX();
1676             this.startY = e.getPageY();
1677
1678             this.deltaX = this.startX - el.offsetLeft;
1679             this.deltaY = this.startY - el.offsetTop;
1680
1681             this.dragThreshMet = false;
1682
1683             this.clickTimeout = setTimeout(
1684                     function() {
1685                         var DDM = Roo.dd.DDM;
1686                         DDM.startDrag(DDM.startX, DDM.startY);
1687                     },
1688                     this.clickTimeThresh );
1689         },
1690
1691         /**
1692          * Fired when either the drag pixel threshol or the mousedown hold
1693          * time threshold has been met.
1694          * @method startDrag
1695          * @param x {int} the X position of the original mousedown
1696          * @param y {int} the Y position of the original mousedown
1697          * @static
1698          */
1699         startDrag: function(x, y) {
1700             clearTimeout(this.clickTimeout);
1701             if (this.dragCurrent) {
1702                 this.dragCurrent.b4StartDrag(x, y);
1703                 this.dragCurrent.startDrag(x, y);
1704             }
1705             this.dragThreshMet = true;
1706         },
1707
1708         /**
1709          * Internal function to handle the mouseup event.  Will be invoked
1710          * from the context of the document.
1711          * @method handleMouseUp
1712          * @param {Event} e the event
1713          * @private
1714          * @static
1715          */
1716         handleMouseUp: function(e) {
1717
1718             if(Roo.QuickTips){
1719                 Roo.QuickTips.enable();
1720             }
1721             if (! this.dragCurrent) {
1722                 return;
1723             }
1724
1725             clearTimeout(this.clickTimeout);
1726
1727             if (this.dragThreshMet) {
1728                 this.fireEvents(e, true);
1729             } else {
1730             }
1731
1732             this.stopDrag(e);
1733
1734             this.stopEvent(e);
1735         },
1736
1737         /**
1738          * Utility to stop event propagation and event default, if these
1739          * features are turned on.
1740          * @method stopEvent
1741          * @param {Event} e the event as returned by this.getEvent()
1742          * @static
1743          */
1744         stopEvent: function(e){
1745             if(this.stopPropagation) {
1746                 e.stopPropagation();
1747             }
1748
1749             if (this.preventDefault) {
1750                 e.preventDefault();
1751             }
1752         },
1753
1754         /**
1755          * Internal function to clean up event handlers after the drag
1756          * operation is complete
1757          * @method stopDrag
1758          * @param {Event} e the event
1759          * @private
1760          * @static
1761          */
1762         stopDrag: function(e) {
1763             // Fire the drag end event for the item that was dragged
1764             if (this.dragCurrent) {
1765                 if (this.dragThreshMet) {
1766                     this.dragCurrent.b4EndDrag(e);
1767                     this.dragCurrent.endDrag(e);
1768                 }
1769
1770                 this.dragCurrent.onMouseUp(e);
1771             }
1772
1773             this.dragCurrent = null;
1774             this.dragOvers = {};
1775         },
1776
1777         /**
1778          * Internal function to handle the mousemove event.  Will be invoked
1779          * from the context of the html element.
1780          *
1781          * @TODO figure out what we can do about mouse events lost when the
1782          * user drags objects beyond the window boundary.  Currently we can
1783          * detect this in internet explorer by verifying that the mouse is
1784          * down during the mousemove event.  Firefox doesn't give us the
1785          * button state on the mousemove event.
1786          * @method handleMouseMove
1787          * @param {Event} e the event
1788          * @private
1789          * @static
1790          */
1791         handleMouseMove: function(e) {
1792             if (! this.dragCurrent) {
1793                 return true;
1794             }
1795
1796             // var button = e.which || e.button;
1797
1798             // check for IE mouseup outside of page boundary
1799             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1800                 this.stopEvent(e);
1801                 return this.handleMouseUp(e);
1802             }
1803
1804             if (!this.dragThreshMet) {
1805                 var diffX = Math.abs(this.startX - e.getPageX());
1806                 var diffY = Math.abs(this.startY - e.getPageY());
1807                 if (diffX > this.clickPixelThresh ||
1808                             diffY > this.clickPixelThresh) {
1809                     this.startDrag(this.startX, this.startY);
1810                 }
1811             }
1812
1813             if (this.dragThreshMet) {
1814                 this.dragCurrent.b4Drag(e);
1815                 this.dragCurrent.onDrag(e);
1816                 if(!this.dragCurrent.moveOnly){
1817                     this.fireEvents(e, false);
1818                 }
1819             }
1820
1821             this.stopEvent(e);
1822
1823             return true;
1824         },
1825
1826         /**
1827          * Iterates over all of the DragDrop elements to find ones we are
1828          * hovering over or dropping on
1829          * @method fireEvents
1830          * @param {Event} e the event
1831          * @param {boolean} isDrop is this a drop op or a mouseover op?
1832          * @private
1833          * @static
1834          */
1835         fireEvents: function(e, isDrop) {
1836             var dc = this.dragCurrent;
1837
1838             // If the user did the mouse up outside of the window, we could
1839             // get here even though we have ended the drag.
1840             if (!dc || dc.isLocked()) {
1841                 return;
1842             }
1843
1844             var pt = e.getPoint();
1845
1846             // cache the previous dragOver array
1847             var oldOvers = [];
1848
1849             var outEvts   = [];
1850             var overEvts  = [];
1851             var dropEvts  = [];
1852             var enterEvts = [];
1853
1854             // Check to see if the object(s) we were hovering over is no longer
1855             // being hovered over so we can fire the onDragOut event
1856             for (var i in this.dragOvers) {
1857
1858                 var ddo = this.dragOvers[i];
1859
1860                 if (! this.isTypeOfDD(ddo)) {
1861                     continue;
1862                 }
1863
1864                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1865                     outEvts.push( ddo );
1866                 }
1867
1868                 oldOvers[i] = true;
1869                 delete this.dragOvers[i];
1870             }
1871
1872             for (var sGroup in dc.groups) {
1873
1874                 if ("string" != typeof sGroup) {
1875                     continue;
1876                 }
1877
1878                 for (i in this.ids[sGroup]) {
1879                     var oDD = this.ids[sGroup][i];
1880                     if (! this.isTypeOfDD(oDD)) {
1881                         continue;
1882                     }
1883
1884                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1885                         if (this.isOverTarget(pt, oDD, this.mode)) {
1886                             // look for drop interactions
1887                             if (isDrop) {
1888                                 dropEvts.push( oDD );
1889                             // look for drag enter and drag over interactions
1890                             } else {
1891
1892                                 // initial drag over: dragEnter fires
1893                                 if (!oldOvers[oDD.id]) {
1894                                     enterEvts.push( oDD );
1895                                 // subsequent drag overs: dragOver fires
1896                                 } else {
1897                                     overEvts.push( oDD );
1898                                 }
1899
1900                                 this.dragOvers[oDD.id] = oDD;
1901                             }
1902                         }
1903                     }
1904                 }
1905             }
1906
1907             if (this.mode) {
1908                 if (outEvts.length) {
1909                     dc.b4DragOut(e, outEvts);
1910                     dc.onDragOut(e, outEvts);
1911                 }
1912
1913                 if (enterEvts.length) {
1914                     dc.onDragEnter(e, enterEvts);
1915                 }
1916
1917                 if (overEvts.length) {
1918                     dc.b4DragOver(e, overEvts);
1919                     dc.onDragOver(e, overEvts);
1920                 }
1921
1922                 if (dropEvts.length) {
1923                     dc.b4DragDrop(e, dropEvts);
1924                     dc.onDragDrop(e, dropEvts);
1925                 }
1926
1927             } else {
1928                 // fire dragout events
1929                 var len = 0;
1930                 for (i=0, len=outEvts.length; i<len; ++i) {
1931                     dc.b4DragOut(e, outEvts[i].id);
1932                     dc.onDragOut(e, outEvts[i].id);
1933                 }
1934
1935                 // fire enter events
1936                 for (i=0,len=enterEvts.length; i<len; ++i) {
1937                     // dc.b4DragEnter(e, oDD.id);
1938                     dc.onDragEnter(e, enterEvts[i].id);
1939                 }
1940
1941                 // fire over events
1942                 for (i=0,len=overEvts.length; i<len; ++i) {
1943                     dc.b4DragOver(e, overEvts[i].id);
1944                     dc.onDragOver(e, overEvts[i].id);
1945                 }
1946
1947                 // fire drop events
1948                 for (i=0, len=dropEvts.length; i<len; ++i) {
1949                     dc.b4DragDrop(e, dropEvts[i].id);
1950                     dc.onDragDrop(e, dropEvts[i].id);
1951                 }
1952
1953             }
1954
1955             // notify about a drop that did not find a target
1956             if (isDrop && !dropEvts.length) {
1957                 dc.onInvalidDrop(e);
1958             }
1959
1960         },
1961
1962         /**
1963          * Helper function for getting the best match from the list of drag
1964          * and drop objects returned by the drag and drop events when we are
1965          * in INTERSECT mode.  It returns either the first object that the
1966          * cursor is over, or the object that has the greatest overlap with
1967          * the dragged element.
1968          * @method getBestMatch
1969          * @param  {DragDrop[]} dds The array of drag and drop objects
1970          * targeted
1971          * @return {DragDrop}       The best single match
1972          * @static
1973          */
1974         getBestMatch: function(dds) {
1975             var winner = null;
1976             // Return null if the input is not what we expect
1977             //if (!dds || !dds.length || dds.length == 0) {
1978                // winner = null;
1979             // If there is only one item, it wins
1980             //} else if (dds.length == 1) {
1981
1982             var len = dds.length;
1983
1984             if (len == 1) {
1985                 winner = dds[0];
1986             } else {
1987                 // Loop through the targeted items
1988                 for (var i=0; i<len; ++i) {
1989                     var dd = dds[i];
1990                     // If the cursor is over the object, it wins.  If the
1991                     // cursor is over multiple matches, the first one we come
1992                     // to wins.
1993                     if (dd.cursorIsOver) {
1994                         winner = dd;
1995                         break;
1996                     // Otherwise the object with the most overlap wins
1997                     } else {
1998                         if (!winner ||
1999                             winner.overlap.getArea() < dd.overlap.getArea()) {
2000                             winner = dd;
2001                         }
2002                     }
2003                 }
2004             }
2005
2006             return winner;
2007         },
2008
2009         /**
2010          * Refreshes the cache of the top-left and bottom-right points of the
2011          * drag and drop objects in the specified group(s).  This is in the
2012          * format that is stored in the drag and drop instance, so typical
2013          * usage is:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2016          * </code>
2017          * Alternatively:
2018          * <code>
2019          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2020          * </code>
2021          * @TODO this really should be an indexed array.  Alternatively this
2022          * method could accept both.
2023          * @method refreshCache
2024          * @param {Object} groups an associative array of groups to refresh
2025          * @static
2026          */
2027         refreshCache: function(groups) {
2028             for (var sGroup in groups) {
2029                 if ("string" != typeof sGroup) {
2030                     continue;
2031                 }
2032                 for (var i in this.ids[sGroup]) {
2033                     var oDD = this.ids[sGroup][i];
2034
2035                     if (this.isTypeOfDD(oDD)) {
2036                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2037                         var loc = this.getLocation(oDD);
2038                         if (loc) {
2039                             this.locationCache[oDD.id] = loc;
2040                         } else {
2041                             delete this.locationCache[oDD.id];
2042                             // this will unregister the drag and drop object if
2043                             // the element is not in a usable state
2044                             // oDD.unreg();
2045                         }
2046                     }
2047                 }
2048             }
2049         },
2050
2051         /**
2052          * This checks to make sure an element exists and is in the DOM.  The
2053          * main purpose is to handle cases where innerHTML is used to remove
2054          * drag and drop objects from the DOM.  IE provides an 'unspecified
2055          * error' when trying to access the offsetParent of such an element
2056          * @method verifyEl
2057          * @param {HTMLElement} el the element to check
2058          * @return {boolean} true if the element looks usable
2059          * @static
2060          */
2061         verifyEl: function(el) {
2062             if (el) {
2063                 var parent;
2064                 if(Roo.isIE){
2065                     try{
2066                         parent = el.offsetParent;
2067                     }catch(e){}
2068                 }else{
2069                     parent = el.offsetParent;
2070                 }
2071                 if (parent) {
2072                     return true;
2073                 }
2074             }
2075
2076             return false;
2077         },
2078
2079         /**
2080          * Returns a Region object containing the drag and drop element's position
2081          * and size, including the padding configured for it
2082          * @method getLocation
2083          * @param {DragDrop} oDD the drag and drop object to get the
2084          *                       location for
2085          * @return {Roo.lib.Region} a Region object representing the total area
2086          *                             the element occupies, including any padding
2087          *                             the instance is configured for.
2088          * @static
2089          */
2090         getLocation: function(oDD) {
2091             if (! this.isTypeOfDD(oDD)) {
2092                 return null;
2093             }
2094
2095             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2096
2097             try {
2098                 pos= Roo.lib.Dom.getXY(el);
2099             } catch (e) { }
2100
2101             if (!pos) {
2102                 return null;
2103             }
2104
2105             x1 = pos[0];
2106             x2 = x1 + el.offsetWidth;
2107             y1 = pos[1];
2108             y2 = y1 + el.offsetHeight;
2109
2110             t = y1 - oDD.padding[0];
2111             r = x2 + oDD.padding[1];
2112             b = y2 + oDD.padding[2];
2113             l = x1 - oDD.padding[3];
2114
2115             return new Roo.lib.Region( t, r, b, l );
2116         },
2117
2118         /**
2119          * Checks the cursor location to see if it over the target
2120          * @method isOverTarget
2121          * @param {Roo.lib.Point} pt The point to evaluate
2122          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2123          * @return {boolean} true if the mouse is over the target
2124          * @private
2125          * @static
2126          */
2127         isOverTarget: function(pt, oTarget, intersect) {
2128             // use cache if available
2129             var loc = this.locationCache[oTarget.id];
2130             if (!loc || !this.useCache) {
2131                 loc = this.getLocation(oTarget);
2132                 this.locationCache[oTarget.id] = loc;
2133
2134             }
2135
2136             if (!loc) {
2137                 return false;
2138             }
2139
2140             oTarget.cursorIsOver = loc.contains( pt );
2141
2142             // DragDrop is using this as a sanity check for the initial mousedown
2143             // in this case we are done.  In POINT mode, if the drag obj has no
2144             // contraints, we are also done. Otherwise we need to evaluate the
2145             // location of the target as related to the actual location of the
2146             // dragged element.
2147             var dc = this.dragCurrent;
2148             if (!dc || !dc.getTargetCoord ||
2149                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2150                 return oTarget.cursorIsOver;
2151             }
2152
2153             oTarget.overlap = null;
2154
2155             // Get the current location of the drag element, this is the
2156             // location of the mouse event less the delta that represents
2157             // where the original mousedown happened on the element.  We
2158             // need to consider constraints and ticks as well.
2159             var pos = dc.getTargetCoord(pt.x, pt.y);
2160
2161             var el = dc.getDragEl();
2162             var curRegion = new Roo.lib.Region( pos.y,
2163                                                    pos.x + el.offsetWidth,
2164                                                    pos.y + el.offsetHeight,
2165                                                    pos.x );
2166
2167             var overlap = curRegion.intersect(loc);
2168
2169             if (overlap) {
2170                 oTarget.overlap = overlap;
2171                 return (intersect) ? true : oTarget.cursorIsOver;
2172             } else {
2173                 return false;
2174             }
2175         },
2176
2177         /**
2178          * unload event handler
2179          * @method _onUnload
2180          * @private
2181          * @static
2182          */
2183         _onUnload: function(e, me) {
2184             Roo.dd.DragDropMgr.unregAll();
2185         },
2186
2187         /**
2188          * Cleans up the drag and drop events and objects.
2189          * @method unregAll
2190          * @private
2191          * @static
2192          */
2193         unregAll: function() {
2194
2195             if (this.dragCurrent) {
2196                 this.stopDrag();
2197                 this.dragCurrent = null;
2198             }
2199
2200             this._execOnAll("unreg", []);
2201
2202             for (i in this.elementCache) {
2203                 delete this.elementCache[i];
2204             }
2205
2206             this.elementCache = {};
2207             this.ids = {};
2208         },
2209
2210         /**
2211          * A cache of DOM elements
2212          * @property elementCache
2213          * @private
2214          * @static
2215          */
2216         elementCache: {},
2217
2218         /**
2219          * Get the wrapper for the DOM element specified
2220          * @method getElWrapper
2221          * @param {String} id the id of the element to get
2222          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2223          * @private
2224          * @deprecated This wrapper isn't that useful
2225          * @static
2226          */
2227         getElWrapper: function(id) {
2228             var oWrapper = this.elementCache[id];
2229             if (!oWrapper || !oWrapper.el) {
2230                 oWrapper = this.elementCache[id] =
2231                     new this.ElementWrapper(Roo.getDom(id));
2232             }
2233             return oWrapper;
2234         },
2235
2236         /**
2237          * Returns the actual DOM element
2238          * @method getElement
2239          * @param {String} id the id of the elment to get
2240          * @return {Object} The element
2241          * @deprecated use Roo.getDom instead
2242          * @static
2243          */
2244         getElement: function(id) {
2245             return Roo.getDom(id);
2246         },
2247
2248         /**
2249          * Returns the style property for the DOM element (i.e.,
2250          * document.getElById(id).style)
2251          * @method getCss
2252          * @param {String} id the id of the elment to get
2253          * @return {Object} The style property of the element
2254          * @deprecated use Roo.getDom instead
2255          * @static
2256          */
2257         getCss: function(id) {
2258             var el = Roo.getDom(id);
2259             return (el) ? el.style : null;
2260         },
2261
2262         /**
2263          * Inner class for cached elements
2264          * @class DragDropMgr.ElementWrapper
2265          * @for DragDropMgr
2266          * @private
2267          * @deprecated
2268          */
2269         ElementWrapper: function(el) {
2270                 /**
2271                  * The element
2272                  * @property el
2273                  */
2274                 this.el = el || null;
2275                 /**
2276                  * The element id
2277                  * @property id
2278                  */
2279                 this.id = this.el && el.id;
2280                 /**
2281                  * A reference to the style property
2282                  * @property css
2283                  */
2284                 this.css = this.el && el.style;
2285             },
2286
2287         /**
2288          * Returns the X position of an html element
2289          * @method getPosX
2290          * @param el the element for which to get the position
2291          * @return {int} the X coordinate
2292          * @for DragDropMgr
2293          * @deprecated use Roo.lib.Dom.getX instead
2294          * @static
2295          */
2296         getPosX: function(el) {
2297             return Roo.lib.Dom.getX(el);
2298         },
2299
2300         /**
2301          * Returns the Y position of an html element
2302          * @method getPosY
2303          * @param el the element for which to get the position
2304          * @return {int} the Y coordinate
2305          * @deprecated use Roo.lib.Dom.getY instead
2306          * @static
2307          */
2308         getPosY: function(el) {
2309             return Roo.lib.Dom.getY(el);
2310         },
2311
2312         /**
2313          * Swap two nodes.  In IE, we use the native method, for others we
2314          * emulate the IE behavior
2315          * @method swapNode
2316          * @param n1 the first node to swap
2317          * @param n2 the other node to swap
2318          * @static
2319          */
2320         swapNode: function(n1, n2) {
2321             if (n1.swapNode) {
2322                 n1.swapNode(n2);
2323             } else {
2324                 var p = n2.parentNode;
2325                 var s = n2.nextSibling;
2326
2327                 if (s == n1) {
2328                     p.insertBefore(n1, n2);
2329                 } else if (n2 == n1.nextSibling) {
2330                     p.insertBefore(n2, n1);
2331                 } else {
2332                     n1.parentNode.replaceChild(n2, n1);
2333                     p.insertBefore(n1, s);
2334                 }
2335             }
2336         },
2337
2338         /**
2339          * Returns the current scroll position
2340          * @method getScroll
2341          * @private
2342          * @static
2343          */
2344         getScroll: function () {
2345             var t, l, dde=document.documentElement, db=document.body;
2346             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2347                 t = dde.scrollTop;
2348                 l = dde.scrollLeft;
2349             } else if (db) {
2350                 t = db.scrollTop;
2351                 l = db.scrollLeft;
2352             } else {
2353
2354             }
2355             return { top: t, left: l };
2356         },
2357
2358         /**
2359          * Returns the specified element style property
2360          * @method getStyle
2361          * @param {HTMLElement} el          the element
2362          * @param {string}      styleProp   the style property
2363          * @return {string} The value of the style property
2364          * @deprecated use Roo.lib.Dom.getStyle
2365          * @static
2366          */
2367         getStyle: function(el, styleProp) {
2368             return Roo.fly(el).getStyle(styleProp);
2369         },
2370
2371         /**
2372          * Gets the scrollTop
2373          * @method getScrollTop
2374          * @return {int} the document's scrollTop
2375          * @static
2376          */
2377         getScrollTop: function () { return this.getScroll().top; },
2378
2379         /**
2380          * Gets the scrollLeft
2381          * @method getScrollLeft
2382          * @return {int} the document's scrollTop
2383          * @static
2384          */
2385         getScrollLeft: function () { return this.getScroll().left; },
2386
2387         /**
2388          * Sets the x/y position of an element to the location of the
2389          * target element.
2390          * @method moveToEl
2391          * @param {HTMLElement} moveEl      The element to move
2392          * @param {HTMLElement} targetEl    The position reference element
2393          * @static
2394          */
2395         moveToEl: function (moveEl, targetEl) {
2396             var aCoord = Roo.lib.Dom.getXY(targetEl);
2397             Roo.lib.Dom.setXY(moveEl, aCoord);
2398         },
2399
2400         /**
2401          * Numeric array sort function
2402          * @method numericSort
2403          * @static
2404          */
2405         numericSort: function(a, b) { return (a - b); },
2406
2407         /**
2408          * Internal counter
2409          * @property _timeoutCount
2410          * @private
2411          * @static
2412          */
2413         _timeoutCount: 0,
2414
2415         /**
2416          * Trying to make the load order less important.  Without this we get
2417          * an error if this file is loaded before the Event Utility.
2418          * @method _addListeners
2419          * @private
2420          * @static
2421          */
2422         _addListeners: function() {
2423             var DDM = Roo.dd.DDM;
2424             if ( Roo.lib.Event && document ) {
2425                 DDM._onLoad();
2426             } else {
2427                 if (DDM._timeoutCount > 2000) {
2428                 } else {
2429                     setTimeout(DDM._addListeners, 10);
2430                     if (document && document.body) {
2431                         DDM._timeoutCount += 1;
2432                     }
2433                 }
2434             }
2435         },
2436
2437         /**
2438          * Recursively searches the immediate parent and all child nodes for
2439          * the handle element in order to determine wheter or not it was
2440          * clicked.
2441          * @method handleWasClicked
2442          * @param node the html element to inspect
2443          * @static
2444          */
2445         handleWasClicked: function(node, id) {
2446             if (this.isHandle(id, node.id)) {
2447                 return true;
2448             } else {
2449                 // check to see if this is a text node child of the one we want
2450                 var p = node.parentNode;
2451
2452                 while (p) {
2453                     if (this.isHandle(id, p.id)) {
2454                         return true;
2455                     } else {
2456                         p = p.parentNode;
2457                     }
2458                 }
2459             }
2460
2461             return false;
2462         }
2463
2464     };
2465
2466 }();
2467
2468 // shorter alias, save a few bytes
2469 Roo.dd.DDM = Roo.dd.DragDropMgr;
2470 Roo.dd.DDM._addListeners();
2471
2472 }/*
2473  * Based on:
2474  * Ext JS Library 1.1.1
2475  * Copyright(c) 2006-2007, Ext JS, LLC.
2476  *
2477  * Originally Released Under LGPL - original licence link has changed is not relivant.
2478  *
2479  * Fork - LGPL
2480  * <script type="text/javascript">
2481  */
2482
2483 /**
2484  * @class Roo.dd.DD
2485  * A DragDrop implementation where the linked element follows the
2486  * mouse cursor during a drag.
2487  * @extends Roo.dd.DragDrop
2488  * @constructor
2489  * @param {String} id the id of the linked element
2490  * @param {String} sGroup the group of related DragDrop items
2491  * @param {object} config an object containing configurable attributes
2492  *                Valid properties for DD:
2493  *                    scroll
2494  */
2495 Roo.dd.DD = function(id, sGroup, config) {
2496     if (id) {
2497         this.init(id, sGroup, config);
2498     }
2499 };
2500
2501 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2502
2503     /**
2504      * When set to true, the utility automatically tries to scroll the browser
2505      * window wehn a drag and drop element is dragged near the viewport boundary.
2506      * Defaults to true.
2507      * @property scroll
2508      * @type boolean
2509      */
2510     scroll: true,
2511
2512     /**
2513      * Sets the pointer offset to the distance between the linked element's top
2514      * left corner and the location the element was clicked
2515      * @method autoOffset
2516      * @param {int} iPageX the X coordinate of the click
2517      * @param {int} iPageY the Y coordinate of the click
2518      */
2519     autoOffset: function(iPageX, iPageY) {
2520         var x = iPageX - this.startPageX;
2521         var y = iPageY - this.startPageY;
2522         this.setDelta(x, y);
2523     },
2524
2525     /**
2526      * Sets the pointer offset.  You can call this directly to force the
2527      * offset to be in a particular location (e.g., pass in 0,0 to set it
2528      * to the center of the object)
2529      * @method setDelta
2530      * @param {int} iDeltaX the distance from the left
2531      * @param {int} iDeltaY the distance from the top
2532      */
2533     setDelta: function(iDeltaX, iDeltaY) {
2534         this.deltaX = iDeltaX;
2535         this.deltaY = iDeltaY;
2536     },
2537
2538     /**
2539      * Sets the drag element to the location of the mousedown or click event,
2540      * maintaining the cursor location relative to the location on the element
2541      * that was clicked.  Override this if you want to place the element in a
2542      * location other than where the cursor is.
2543      * @method setDragElPos
2544      * @param {int} iPageX the X coordinate of the mousedown or drag event
2545      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2546      */
2547     setDragElPos: function(iPageX, iPageY) {
2548         // the first time we do this, we are going to check to make sure
2549         // the element has css positioning
2550
2551         var el = this.getDragEl();
2552         this.alignElWithMouse(el, iPageX, iPageY);
2553     },
2554
2555     /**
2556      * Sets the element to the location of the mousedown or click event,
2557      * maintaining the cursor location relative to the location on the element
2558      * that was clicked.  Override this if you want to place the element in a
2559      * location other than where the cursor is.
2560      * @method alignElWithMouse
2561      * @param {HTMLElement} el the element to move
2562      * @param {int} iPageX the X coordinate of the mousedown or drag event
2563      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2564      */
2565     alignElWithMouse: function(el, iPageX, iPageY) {
2566         var oCoord = this.getTargetCoord(iPageX, iPageY);
2567         var fly = el.dom ? el : Roo.fly(el);
2568         if (!this.deltaSetXY) {
2569             var aCoord = [oCoord.x, oCoord.y];
2570             fly.setXY(aCoord);
2571             var newLeft = fly.getLeft(true);
2572             var newTop  = fly.getTop(true);
2573             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2574         } else {
2575             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2576         }
2577
2578         this.cachePosition(oCoord.x, oCoord.y);
2579         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2580         return oCoord;
2581     },
2582
2583     /**
2584      * Saves the most recent position so that we can reset the constraints and
2585      * tick marks on-demand.  We need to know this so that we can calculate the
2586      * number of pixels the element is offset from its original position.
2587      * @method cachePosition
2588      * @param iPageX the current x position (optional, this just makes it so we
2589      * don't have to look it up again)
2590      * @param iPageY the current y position (optional, this just makes it so we
2591      * don't have to look it up again)
2592      */
2593     cachePosition: function(iPageX, iPageY) {
2594         if (iPageX) {
2595             this.lastPageX = iPageX;
2596             this.lastPageY = iPageY;
2597         } else {
2598             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2599             this.lastPageX = aCoord[0];
2600             this.lastPageY = aCoord[1];
2601         }
2602     },
2603
2604     /**
2605      * Auto-scroll the window if the dragged object has been moved beyond the
2606      * visible window boundary.
2607      * @method autoScroll
2608      * @param {int} x the drag element's x position
2609      * @param {int} y the drag element's y position
2610      * @param {int} h the height of the drag element
2611      * @param {int} w the width of the drag element
2612      * @private
2613      */
2614     autoScroll: function(x, y, h, w) {
2615
2616         if (this.scroll) {
2617             // The client height
2618             var clientH = Roo.lib.Dom.getViewWidth();
2619
2620             // The client width
2621             var clientW = Roo.lib.Dom.getViewHeight();
2622
2623             // The amt scrolled down
2624             var st = this.DDM.getScrollTop();
2625
2626             // The amt scrolled right
2627             var sl = this.DDM.getScrollLeft();
2628
2629             // Location of the bottom of the element
2630             var bot = h + y;
2631
2632             // Location of the right of the element
2633             var right = w + x;
2634
2635             // The distance from the cursor to the bottom of the visible area,
2636             // adjusted so that we don't scroll if the cursor is beyond the
2637             // element drag constraints
2638             var toBot = (clientH + st - y - this.deltaY);
2639
2640             // The distance from the cursor to the right of the visible area
2641             var toRight = (clientW + sl - x - this.deltaX);
2642
2643
2644             // How close to the edge the cursor must be before we scroll
2645             // var thresh = (document.all) ? 100 : 40;
2646             var thresh = 40;
2647
2648             // How many pixels to scroll per autoscroll op.  This helps to reduce
2649             // clunky scrolling. IE is more sensitive about this ... it needs this
2650             // value to be higher.
2651             var scrAmt = (document.all) ? 80 : 30;
2652
2653             // Scroll down if we are near the bottom of the visible page and the
2654             // obj extends below the crease
2655             if ( bot > clientH && toBot < thresh ) {
2656                 window.scrollTo(sl, st + scrAmt);
2657             }
2658
2659             // Scroll up if the window is scrolled down and the top of the object
2660             // goes above the top border
2661             if ( y < st && st > 0 && y - st < thresh ) {
2662                 window.scrollTo(sl, st - scrAmt);
2663             }
2664
2665             // Scroll right if the obj is beyond the right border and the cursor is
2666             // near the border.
2667             if ( right > clientW && toRight < thresh ) {
2668                 window.scrollTo(sl + scrAmt, st);
2669             }
2670
2671             // Scroll left if the window has been scrolled to the right and the obj
2672             // extends past the left border
2673             if ( x < sl && sl > 0 && x - sl < thresh ) {
2674                 window.scrollTo(sl - scrAmt, st);
2675             }
2676         }
2677     },
2678
2679     /**
2680      * Finds the location the element should be placed if we want to move
2681      * it to where the mouse location less the click offset would place us.
2682      * @method getTargetCoord
2683      * @param {int} iPageX the X coordinate of the click
2684      * @param {int} iPageY the Y coordinate of the click
2685      * @return an object that contains the coordinates (Object.x and Object.y)
2686      * @private
2687      */
2688     getTargetCoord: function(iPageX, iPageY) {
2689
2690
2691         var x = iPageX - this.deltaX;
2692         var y = iPageY - this.deltaY;
2693
2694         if (this.constrainX) {
2695             if (x < this.minX) { x = this.minX; }
2696             if (x > this.maxX) { x = this.maxX; }
2697         }
2698
2699         if (this.constrainY) {
2700             if (y < this.minY) { y = this.minY; }
2701             if (y > this.maxY) { y = this.maxY; }
2702         }
2703
2704         x = this.getTick(x, this.xTicks);
2705         y = this.getTick(y, this.yTicks);
2706
2707
2708         return {x:x, y:y};
2709     },
2710
2711     /*
2712      * Sets up config options specific to this class. Overrides
2713      * Roo.dd.DragDrop, but all versions of this method through the
2714      * inheritance chain are called
2715      */
2716     applyConfig: function() {
2717         Roo.dd.DD.superclass.applyConfig.call(this);
2718         this.scroll = (this.config.scroll !== false);
2719     },
2720
2721     /*
2722      * Event that fires prior to the onMouseDown event.  Overrides
2723      * Roo.dd.DragDrop.
2724      */
2725     b4MouseDown: function(e) {
2726         // this.resetConstraints();
2727         this.autoOffset(e.getPageX(),
2728                             e.getPageY());
2729     },
2730
2731     /*
2732      * Event that fires prior to the onDrag event.  Overrides
2733      * Roo.dd.DragDrop.
2734      */
2735     b4Drag: function(e) {
2736         this.setDragElPos(e.getPageX(),
2737                             e.getPageY());
2738     },
2739
2740     toString: function() {
2741         return ("DD " + this.id);
2742     }
2743
2744     //////////////////////////////////////////////////////////////////////////
2745     // Debugging ygDragDrop events that can be overridden
2746     //////////////////////////////////////////////////////////////////////////
2747     /*
2748     startDrag: function(x, y) {
2749     },
2750
2751     onDrag: function(e) {
2752     },
2753
2754     onDragEnter: function(e, id) {
2755     },
2756
2757     onDragOver: function(e, id) {
2758     },
2759
2760     onDragOut: function(e, id) {
2761     },
2762
2763     onDragDrop: function(e, id) {
2764     },
2765
2766     endDrag: function(e) {
2767     }
2768
2769     */
2770
2771 });/*
2772  * Based on:
2773  * Ext JS Library 1.1.1
2774  * Copyright(c) 2006-2007, Ext JS, LLC.
2775  *
2776  * Originally Released Under LGPL - original licence link has changed is not relivant.
2777  *
2778  * Fork - LGPL
2779  * <script type="text/javascript">
2780  */
2781
2782 /**
2783  * @class Roo.dd.DDProxy
2784  * A DragDrop implementation that inserts an empty, bordered div into
2785  * the document that follows the cursor during drag operations.  At the time of
2786  * the click, the frame div is resized to the dimensions of the linked html
2787  * element, and moved to the exact location of the linked element.
2788  *
2789  * References to the "frame" element refer to the single proxy element that
2790  * was created to be dragged in place of all DDProxy elements on the
2791  * page.
2792  *
2793  * @extends Roo.dd.DD
2794  * @constructor
2795  * @param {String} id the id of the linked html element
2796  * @param {String} sGroup the group of related DragDrop objects
2797  * @param {object} config an object containing configurable attributes
2798  *                Valid properties for DDProxy in addition to those in DragDrop:
2799  *                   resizeFrame, centerFrame, dragElId
2800  */
2801 Roo.dd.DDProxy = function(id, sGroup, config) {
2802     if (id) {
2803         this.init(id, sGroup, config);
2804         this.initFrame();
2805     }
2806 };
2807
2808 /**
2809  * The default drag frame div id
2810  * @property Roo.dd.DDProxy.dragElId
2811  * @type String
2812  * @static
2813  */
2814 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2815
2816 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2817
2818     /**
2819      * By default we resize the drag frame to be the same size as the element
2820      * we want to drag (this is to get the frame effect).  We can turn it off
2821      * if we want a different behavior.
2822      * @property resizeFrame
2823      * @type boolean
2824      */
2825     resizeFrame: true,
2826
2827     /**
2828      * By default the frame is positioned exactly where the drag element is, so
2829      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2830      * you do not have constraints on the obj is to have the drag frame centered
2831      * around the cursor.  Set centerFrame to true for this effect.
2832      * @property centerFrame
2833      * @type boolean
2834      */
2835     centerFrame: false,
2836
2837     /**
2838      * Creates the proxy element if it does not yet exist
2839      * @method createFrame
2840      */
2841     createFrame: function() {
2842         var self = this;
2843         var body = document.body;
2844
2845         if (!body || !body.firstChild) {
2846             setTimeout( function() { self.createFrame(); }, 50 );
2847             return;
2848         }
2849
2850         var div = this.getDragEl();
2851
2852         if (!div) {
2853             div    = document.createElement("div");
2854             div.id = this.dragElId;
2855             var s  = div.style;
2856
2857             s.position   = "absolute";
2858             s.visibility = "hidden";
2859             s.cursor     = "move";
2860             s.border     = "2px solid #aaa";
2861             s.zIndex     = 999;
2862
2863             // appendChild can blow up IE if invoked prior to the window load event
2864             // while rendering a table.  It is possible there are other scenarios
2865             // that would cause this to happen as well.
2866             body.insertBefore(div, body.firstChild);
2867         }
2868     },
2869
2870     /**
2871      * Initialization for the drag frame element.  Must be called in the
2872      * constructor of all subclasses
2873      * @method initFrame
2874      */
2875     initFrame: function() {
2876         this.createFrame();
2877     },
2878
2879     applyConfig: function() {
2880         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2881
2882         this.resizeFrame = (this.config.resizeFrame !== false);
2883         this.centerFrame = (this.config.centerFrame);
2884         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2885     },
2886
2887     /**
2888      * Resizes the drag frame to the dimensions of the clicked object, positions
2889      * it over the object, and finally displays it
2890      * @method showFrame
2891      * @param {int} iPageX X click position
2892      * @param {int} iPageY Y click position
2893      * @private
2894      */
2895     showFrame: function(iPageX, iPageY) {
2896         var el = this.getEl();
2897         var dragEl = this.getDragEl();
2898         var s = dragEl.style;
2899
2900         this._resizeProxy();
2901
2902         if (this.centerFrame) {
2903             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2904                            Math.round(parseInt(s.height, 10)/2) );
2905         }
2906
2907         this.setDragElPos(iPageX, iPageY);
2908
2909         Roo.fly(dragEl).show();
2910     },
2911
2912     /**
2913      * The proxy is automatically resized to the dimensions of the linked
2914      * element when a drag is initiated, unless resizeFrame is set to false
2915      * @method _resizeProxy
2916      * @private
2917      */
2918     _resizeProxy: function() {
2919         if (this.resizeFrame) {
2920             var el = this.getEl();
2921             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2922         }
2923     },
2924
2925     // overrides Roo.dd.DragDrop
2926     b4MouseDown: function(e) {
2927         var x = e.getPageX();
2928         var y = e.getPageY();
2929         this.autoOffset(x, y);
2930         this.setDragElPos(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4StartDrag: function(x, y) {
2935         // show the drag frame
2936         this.showFrame(x, y);
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     b4EndDrag: function(e) {
2941         Roo.fly(this.getDragEl()).hide();
2942     },
2943
2944     // overrides Roo.dd.DragDrop
2945     // By default we try to move the element to the last location of the frame.
2946     // This is so that the default behavior mirrors that of Roo.dd.DD.
2947     endDrag: function(e) {
2948
2949         var lel = this.getEl();
2950         var del = this.getDragEl();
2951
2952         // Show the drag frame briefly so we can get its position
2953         del.style.visibility = "";
2954
2955         this.beforeMove();
2956         // Hide the linked element before the move to get around a Safari
2957         // rendering bug.
2958         lel.style.visibility = "hidden";
2959         Roo.dd.DDM.moveToEl(lel, del);
2960         del.style.visibility = "hidden";
2961         lel.style.visibility = "";
2962
2963         this.afterDrag();
2964     },
2965
2966     beforeMove : function(){
2967
2968     },
2969
2970     afterDrag : function(){
2971
2972     },
2973
2974     toString: function() {
2975         return ("DDProxy " + this.id);
2976     }
2977
2978 });
2979 /*
2980  * Based on:
2981  * Ext JS Library 1.1.1
2982  * Copyright(c) 2006-2007, Ext JS, LLC.
2983  *
2984  * Originally Released Under LGPL - original licence link has changed is not relivant.
2985  *
2986  * Fork - LGPL
2987  * <script type="text/javascript">
2988  */
2989
2990  /**
2991  * @class Roo.dd.DDTarget
2992  * A DragDrop implementation that does not move, but can be a drop
2993  * target.  You would get the same result by simply omitting implementation
2994  * for the event callbacks, but this way we reduce the processing cost of the
2995  * event listener and the callbacks.
2996  * @extends Roo.dd.DragDrop
2997  * @constructor
2998  * @param {String} id the id of the element that is a drop target
2999  * @param {String} sGroup the group of related DragDrop objects
3000  * @param {object} config an object containing configurable attributes
3001  *                 Valid properties for DDTarget in addition to those in
3002  *                 DragDrop:
3003  *                    none
3004  */
3005 Roo.dd.DDTarget = function(id, sGroup, config) {
3006     if (id) {
3007         this.initTarget(id, sGroup, config);
3008     }
3009 };
3010
3011 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3012 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3013     toString: function() {
3014         return ("DDTarget " + this.id);
3015     }
3016 });
3017 /*
3018  * Based on:
3019  * Ext JS Library 1.1.1
3020  * Copyright(c) 2006-2007, Ext JS, LLC.
3021  *
3022  * Originally Released Under LGPL - original licence link has changed is not relivant.
3023  *
3024  * Fork - LGPL
3025  * <script type="text/javascript">
3026  */
3027  
3028
3029 /**
3030  * @class Roo.dd.ScrollManager
3031  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3032  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3033  * @singleton
3034  */
3035 Roo.dd.ScrollManager = function(){
3036     var ddm = Roo.dd.DragDropMgr;
3037     var els = {};
3038     var dragEl = null;
3039     var proc = {};
3040     
3041     var onStop = function(e){
3042         dragEl = null;
3043         clearProc();
3044     };
3045     
3046     var triggerRefresh = function(){
3047         if(ddm.dragCurrent){
3048              ddm.refreshCache(ddm.dragCurrent.groups);
3049         }
3050     };
3051     
3052     var doScroll = function(){
3053         if(ddm.dragCurrent){
3054             var dds = Roo.dd.ScrollManager;
3055             if(!dds.animate){
3056                 if(proc.el.scroll(proc.dir, dds.increment)){
3057                     triggerRefresh();
3058                 }
3059             }else{
3060                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3061             }
3062         }
3063     };
3064     
3065     var clearProc = function(){
3066         if(proc.id){
3067             clearInterval(proc.id);
3068         }
3069         proc.id = 0;
3070         proc.el = null;
3071         proc.dir = "";
3072     };
3073     
3074     var startProc = function(el, dir){
3075         clearProc();
3076         proc.el = el;
3077         proc.dir = dir;
3078         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3079     };
3080     
3081     var onFire = function(e, isDrop){
3082         if(isDrop || !ddm.dragCurrent){ return; }
3083         var dds = Roo.dd.ScrollManager;
3084         if(!dragEl || dragEl != ddm.dragCurrent){
3085             dragEl = ddm.dragCurrent;
3086             // refresh regions on drag start
3087             dds.refreshCache();
3088         }
3089         
3090         var xy = Roo.lib.Event.getXY(e);
3091         var pt = new Roo.lib.Point(xy[0], xy[1]);
3092         for(var id in els){
3093             var el = els[id], r = el._region;
3094             if(r && r.contains(pt) && el.isScrollable()){
3095                 if(r.bottom - pt.y <= dds.thresh){
3096                     if(proc.el != el){
3097                         startProc(el, "down");
3098                     }
3099                     return;
3100                 }else if(r.right - pt.x <= dds.thresh){
3101                     if(proc.el != el){
3102                         startProc(el, "left");
3103                     }
3104                     return;
3105                 }else if(pt.y - r.top <= dds.thresh){
3106                     if(proc.el != el){
3107                         startProc(el, "up");
3108                     }
3109                     return;
3110                 }else if(pt.x - r.left <= dds.thresh){
3111                     if(proc.el != el){
3112                         startProc(el, "right");
3113                     }
3114                     return;
3115                 }
3116             }
3117         }
3118         clearProc();
3119     };
3120     
3121     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3122     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3123     
3124     return {
3125         /**
3126          * Registers new overflow element(s) to auto scroll
3127          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3128          */
3129         register : function(el){
3130             if(el instanceof Array){
3131                 for(var i = 0, len = el.length; i < len; i++) {
3132                         this.register(el[i]);
3133                 }
3134             }else{
3135                 el = Roo.get(el);
3136                 els[el.id] = el;
3137             }
3138         },
3139         
3140         /**
3141          * Unregisters overflow element(s) so they are no longer scrolled
3142          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3143          */
3144         unregister : function(el){
3145             if(el instanceof Array){
3146                 for(var i = 0, len = el.length; i < len; i++) {
3147                         this.unregister(el[i]);
3148                 }
3149             }else{
3150                 el = Roo.get(el);
3151                 delete els[el.id];
3152             }
3153         },
3154         
3155         /**
3156          * The number of pixels from the edge of a container the pointer needs to be to 
3157          * trigger scrolling (defaults to 25)
3158          * @type Number
3159          */
3160         thresh : 25,
3161         
3162         /**
3163          * The number of pixels to scroll in each scroll increment (defaults to 50)
3164          * @type Number
3165          */
3166         increment : 100,
3167         
3168         /**
3169          * The frequency of scrolls in milliseconds (defaults to 500)
3170          * @type Number
3171          */
3172         frequency : 500,
3173         
3174         /**
3175          * True to animate the scroll (defaults to true)
3176          * @type Boolean
3177          */
3178         animate: true,
3179         
3180         /**
3181          * The animation duration in seconds - 
3182          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3183          * @type Number
3184          */
3185         animDuration: .4,
3186         
3187         /**
3188          * Manually trigger a cache refresh.
3189          */
3190         refreshCache : function(){
3191             for(var id in els){
3192                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3193                     els[id]._region = els[id].getRegion();
3194                 }
3195             }
3196         }
3197     };
3198 }();/*
3199  * Based on:
3200  * Ext JS Library 1.1.1
3201  * Copyright(c) 2006-2007, Ext JS, LLC.
3202  *
3203  * Originally Released Under LGPL - original licence link has changed is not relivant.
3204  *
3205  * Fork - LGPL
3206  * <script type="text/javascript">
3207  */
3208  
3209
3210 /**
3211  * @class Roo.dd.Registry
3212  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3213  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3214  * @singleton
3215  */
3216 Roo.dd.Registry = function(){
3217     var elements = {}; 
3218     var handles = {}; 
3219     var autoIdSeed = 0;
3220
3221     var getId = function(el, autogen){
3222         if(typeof el == "string"){
3223             return el;
3224         }
3225         var id = el.id;
3226         if(!id && autogen !== false){
3227             id = "roodd-" + (++autoIdSeed);
3228             el.id = id;
3229         }
3230         return id;
3231     };
3232     
3233     return {
3234     /**
3235      * Register a drag drop element
3236      * @param {String|HTMLElement} element The id or DOM node to register
3237      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3238      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3239      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3240      * populated in the data object (if applicable):
3241      * <pre>
3242 Value      Description<br />
3243 ---------  ------------------------------------------<br />
3244 handles    Array of DOM nodes that trigger dragging<br />
3245            for the element being registered<br />
3246 isHandle   True if the element passed in triggers<br />
3247            dragging itself, else false
3248 </pre>
3249      */
3250         register : function(el, data){
3251             data = data || {};
3252             if(typeof el == "string"){
3253                 el = document.getElementById(el);
3254             }
3255             data.ddel = el;
3256             elements[getId(el)] = data;
3257             if(data.isHandle !== false){
3258                 handles[data.ddel.id] = data;
3259             }
3260             if(data.handles){
3261                 var hs = data.handles;
3262                 for(var i = 0, len = hs.length; i < len; i++){
3263                         handles[getId(hs[i])] = data;
3264                 }
3265             }
3266         },
3267
3268     /**
3269      * Unregister a drag drop element
3270      * @param {String|HTMLElement}  element The id or DOM node to unregister
3271      */
3272         unregister : function(el){
3273             var id = getId(el, false);
3274             var data = elements[id];
3275             if(data){
3276                 delete elements[id];
3277                 if(data.handles){
3278                     var hs = data.handles;
3279                     for(var i = 0, len = hs.length; i < len; i++){
3280                         delete handles[getId(hs[i], false)];
3281                     }
3282                 }
3283             }
3284         },
3285
3286     /**
3287      * Returns the handle registered for a DOM Node by id
3288      * @param {String|HTMLElement} id The DOM node or id to look up
3289      * @return {Object} handle The custom handle data
3290      */
3291         getHandle : function(id){
3292             if(typeof id != "string"){ // must be element?
3293                 id = id.id;
3294             }
3295             return handles[id];
3296         },
3297
3298     /**
3299      * Returns the handle that is registered for the DOM node that is the target of the event
3300      * @param {Event} e The event
3301      * @return {Object} handle The custom handle data
3302      */
3303         getHandleFromEvent : function(e){
3304             var t = Roo.lib.Event.getTarget(e);
3305             return t ? handles[t.id] : null;
3306         },
3307
3308     /**
3309      * Returns a custom data object that is registered for a DOM node by id
3310      * @param {String|HTMLElement} id The DOM node or id to look up
3311      * @return {Object} data The custom data
3312      */
3313         getTarget : function(id){
3314             if(typeof id != "string"){ // must be element?
3315                 id = id.id;
3316             }
3317             return elements[id];
3318         },
3319
3320     /**
3321      * Returns a custom data object that is registered for the DOM node that is the target of the event
3322      * @param {Event} e The event
3323      * @return {Object} data The custom data
3324      */
3325         getTargetFromEvent : function(e){
3326             var t = Roo.lib.Event.getTarget(e);
3327             return t ? elements[t.id] || handles[t.id] : null;
3328         }
3329     };
3330 }();/*
3331  * Based on:
3332  * Ext JS Library 1.1.1
3333  * Copyright(c) 2006-2007, Ext JS, LLC.
3334  *
3335  * Originally Released Under LGPL - original licence link has changed is not relivant.
3336  *
3337  * Fork - LGPL
3338  * <script type="text/javascript">
3339  */
3340  
3341
3342 /**
3343  * @class Roo.dd.StatusProxy
3344  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3345  * default drag proxy used by all Roo.dd components.
3346  * @constructor
3347  * @param {Object} config
3348  */
3349 Roo.dd.StatusProxy = function(config){
3350     Roo.apply(this, config);
3351     this.id = this.id || Roo.id();
3352     this.el = new Roo.Layer({
3353         dh: {
3354             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3355                 {tag: "div", cls: "x-dd-drop-icon"},
3356                 {tag: "div", cls: "x-dd-drag-ghost"}
3357             ]
3358         }, 
3359         shadow: !config || config.shadow !== false
3360     });
3361     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3362     this.dropStatus = this.dropNotAllowed;
3363 };
3364
3365 Roo.dd.StatusProxy.prototype = {
3366     /**
3367      * @cfg {String} dropAllowed
3368      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3369      */
3370     dropAllowed : "x-dd-drop-ok",
3371     /**
3372      * @cfg {String} dropNotAllowed
3373      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3374      */
3375     dropNotAllowed : "x-dd-drop-nodrop",
3376
3377     /**
3378      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3379      * over the current target element.
3380      * @param {String} cssClass The css class for the new drop status indicator image
3381      */
3382     setStatus : function(cssClass){
3383         cssClass = cssClass || this.dropNotAllowed;
3384         if(this.dropStatus != cssClass){
3385             this.el.replaceClass(this.dropStatus, cssClass);
3386             this.dropStatus = cssClass;
3387         }
3388     },
3389
3390     /**
3391      * Resets the status indicator to the default dropNotAllowed value
3392      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3393      */
3394     reset : function(clearGhost){
3395         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3396         this.dropStatus = this.dropNotAllowed;
3397         if(clearGhost){
3398             this.ghost.update("");
3399         }
3400     },
3401
3402     /**
3403      * Updates the contents of the ghost element
3404      * @param {String} html The html that will replace the current innerHTML of the ghost element
3405      */
3406     update : function(html){
3407         if(typeof html == "string"){
3408             this.ghost.update(html);
3409         }else{
3410             this.ghost.update("");
3411             html.style.margin = "0";
3412             this.ghost.dom.appendChild(html);
3413         }
3414         // ensure float = none set?? cant remember why though.
3415         var el = this.ghost.dom.firstChild;
3416                 if(el){
3417                         Roo.fly(el).setStyle('float', 'none');
3418                 }
3419     },
3420     
3421     /**
3422      * Returns the underlying proxy {@link Roo.Layer}
3423      * @return {Roo.Layer} el
3424     */
3425     getEl : function(){
3426         return this.el;
3427     },
3428
3429     /**
3430      * Returns the ghost element
3431      * @return {Roo.Element} el
3432      */
3433     getGhost : function(){
3434         return this.ghost;
3435     },
3436
3437     /**
3438      * Hides the proxy
3439      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3440      */
3441     hide : function(clear){
3442         this.el.hide();
3443         if(clear){
3444             this.reset(true);
3445         }
3446     },
3447
3448     /**
3449      * Stops the repair animation if it's currently running
3450      */
3451     stop : function(){
3452         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3453             this.anim.stop();
3454         }
3455     },
3456
3457     /**
3458      * Displays this proxy
3459      */
3460     show : function(){
3461         this.el.show();
3462     },
3463
3464     /**
3465      * Force the Layer to sync its shadow and shim positions to the element
3466      */
3467     sync : function(){
3468         this.el.sync();
3469     },
3470
3471     /**
3472      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3473      * invalid drop operation by the item being dragged.
3474      * @param {Array} xy The XY position of the element ([x, y])
3475      * @param {Function} callback The function to call after the repair is complete
3476      * @param {Object} scope The scope in which to execute the callback
3477      */
3478     repair : function(xy, callback, scope){
3479         this.callback = callback;
3480         this.scope = scope;
3481         if(xy && this.animRepair !== false){
3482             this.el.addClass("x-dd-drag-repair");
3483             this.el.hideUnders(true);
3484             this.anim = this.el.shift({
3485                 duration: this.repairDuration || .5,
3486                 easing: 'easeOut',
3487                 xy: xy,
3488                 stopFx: true,
3489                 callback: this.afterRepair,
3490                 scope: this
3491             });
3492         }else{
3493             this.afterRepair();
3494         }
3495     },
3496
3497     // private
3498     afterRepair : function(){
3499         this.hide(true);
3500         if(typeof this.callback == "function"){
3501             this.callback.call(this.scope || this);
3502         }
3503         this.callback = null;
3504         this.scope = null;
3505     }
3506 };/*
3507  * Based on:
3508  * Ext JS Library 1.1.1
3509  * Copyright(c) 2006-2007, Ext JS, LLC.
3510  *
3511  * Originally Released Under LGPL - original licence link has changed is not relivant.
3512  *
3513  * Fork - LGPL
3514  * <script type="text/javascript">
3515  */
3516
3517 /**
3518  * @class Roo.dd.DragSource
3519  * @extends Roo.dd.DDProxy
3520  * A simple class that provides the basic implementation needed to make any element draggable.
3521  * @constructor
3522  * @param {String/HTMLElement/Element} el The container element
3523  * @param {Object} config
3524  */
3525 Roo.dd.DragSource = function(el, config){
3526     this.el = Roo.get(el);
3527     this.dragData = {};
3528     
3529     Roo.apply(this, config);
3530     
3531     if(!this.proxy){
3532         this.proxy = new Roo.dd.StatusProxy();
3533     }
3534
3535     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3536           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3537     
3538     this.dragging = false;
3539 };
3540
3541 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3542     /**
3543      * @cfg {String} dropAllowed
3544      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3545      */
3546     dropAllowed : "x-dd-drop-ok",
3547     /**
3548      * @cfg {String} dropNotAllowed
3549      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3550      */
3551     dropNotAllowed : "x-dd-drop-nodrop",
3552
3553     /**
3554      * Returns the data object associated with this drag source
3555      * @return {Object} data An object containing arbitrary data
3556      */
3557     getDragData : function(e){
3558         return this.dragData;
3559     },
3560
3561     // private
3562     onDragEnter : function(e, id){
3563         var target = Roo.dd.DragDropMgr.getDDById(id);
3564         this.cachedTarget = target;
3565         if(this.beforeDragEnter(target, e, id) !== false){
3566             if(target.isNotifyTarget){
3567                 var status = target.notifyEnter(this, e, this.dragData);
3568                 this.proxy.setStatus(status);
3569             }else{
3570                 this.proxy.setStatus(this.dropAllowed);
3571             }
3572             
3573             if(this.afterDragEnter){
3574                 /**
3575                  * An empty function by default, but provided so that you can perform a custom action
3576                  * when the dragged item enters the drop target by providing an implementation.
3577                  * @param {Roo.dd.DragDrop} target The drop target
3578                  * @param {Event} e The event object
3579                  * @param {String} id The id of the dragged element
3580                  * @method afterDragEnter
3581                  */
3582                 this.afterDragEnter(target, e, id);
3583             }
3584         }
3585     },
3586
3587     /**
3588      * An empty function by default, but provided so that you can perform a custom action
3589      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3590      * @param {Roo.dd.DragDrop} target The drop target
3591      * @param {Event} e The event object
3592      * @param {String} id The id of the dragged element
3593      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3594      */
3595     beforeDragEnter : function(target, e, id){
3596         return true;
3597     },
3598
3599     // private
3600     alignElWithMouse: function() {
3601         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3602         this.proxy.sync();
3603     },
3604
3605     // private
3606     onDragOver : function(e, id){
3607         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3608         if(this.beforeDragOver(target, e, id) !== false){
3609             if(target.isNotifyTarget){
3610                 var status = target.notifyOver(this, e, this.dragData);
3611                 this.proxy.setStatus(status);
3612             }
3613
3614             if(this.afterDragOver){
3615                 /**
3616                  * An empty function by default, but provided so that you can perform a custom action
3617                  * while the dragged item is over the drop target by providing an implementation.
3618                  * @param {Roo.dd.DragDrop} target The drop target
3619                  * @param {Event} e The event object
3620                  * @param {String} id The id of the dragged element
3621                  * @method afterDragOver
3622                  */
3623                 this.afterDragOver(target, e, id);
3624             }
3625         }
3626     },
3627
3628     /**
3629      * An empty function by default, but provided so that you can perform a custom action
3630      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3631      * @param {Roo.dd.DragDrop} target The drop target
3632      * @param {Event} e The event object
3633      * @param {String} id The id of the dragged element
3634      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3635      */
3636     beforeDragOver : function(target, e, id){
3637         return true;
3638     },
3639
3640     // private
3641     onDragOut : function(e, id){
3642         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3643         if(this.beforeDragOut(target, e, id) !== false){
3644             if(target.isNotifyTarget){
3645                 target.notifyOut(this, e, this.dragData);
3646             }
3647             this.proxy.reset();
3648             if(this.afterDragOut){
3649                 /**
3650                  * An empty function by default, but provided so that you can perform a custom action
3651                  * after the dragged item is dragged out of the target without dropping.
3652                  * @param {Roo.dd.DragDrop} target The drop target
3653                  * @param {Event} e The event object
3654                  * @param {String} id The id of the dragged element
3655                  * @method afterDragOut
3656                  */
3657                 this.afterDragOut(target, e, id);
3658             }
3659         }
3660         this.cachedTarget = null;
3661     },
3662
3663     /**
3664      * An empty function by default, but provided so that you can perform a custom action before the dragged
3665      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3666      * @param {Roo.dd.DragDrop} target The drop target
3667      * @param {Event} e The event object
3668      * @param {String} id The id of the dragged element
3669      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3670      */
3671     beforeDragOut : function(target, e, id){
3672         return true;
3673     },
3674     
3675     // private
3676     onDragDrop : function(e, id){
3677         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3678         if(this.beforeDragDrop(target, e, id) !== false){
3679             if(target.isNotifyTarget){
3680                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3681                     this.onValidDrop(target, e, id);
3682                 }else{
3683                     this.onInvalidDrop(target, e, id);
3684                 }
3685             }else{
3686                 this.onValidDrop(target, e, id);
3687             }
3688             
3689             if(this.afterDragDrop){
3690                 /**
3691                  * An empty function by default, but provided so that you can perform a custom action
3692                  * after a valid drag drop has occurred by providing an implementation.
3693                  * @param {Roo.dd.DragDrop} target The drop target
3694                  * @param {Event} e The event object
3695                  * @param {String} id The id of the dropped element
3696                  * @method afterDragDrop
3697                  */
3698                 this.afterDragDrop(target, e, id);
3699             }
3700         }
3701         delete this.cachedTarget;
3702     },
3703
3704     /**
3705      * An empty function by default, but provided so that you can perform a custom action before the dragged
3706      * item is dropped onto the target and optionally cancel the onDragDrop.
3707      * @param {Roo.dd.DragDrop} target The drop target
3708      * @param {Event} e The event object
3709      * @param {String} id The id of the dragged element
3710      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3711      */
3712     beforeDragDrop : function(target, e, id){
3713         return true;
3714     },
3715
3716     // private
3717     onValidDrop : function(target, e, id){
3718         this.hideProxy();
3719         if(this.afterValidDrop){
3720             /**
3721              * An empty function by default, but provided so that you can perform a custom action
3722              * after a valid drop has occurred by providing an implementation.
3723              * @param {Object} target The target DD 
3724              * @param {Event} e The event object
3725              * @param {String} id The id of the dropped element
3726              * @method afterInvalidDrop
3727              */
3728             this.afterValidDrop(target, e, id);
3729         }
3730     },
3731
3732     // private
3733     getRepairXY : function(e, data){
3734         return this.el.getXY();  
3735     },
3736
3737     // private
3738     onInvalidDrop : function(target, e, id){
3739         this.beforeInvalidDrop(target, e, id);
3740         if(this.cachedTarget){
3741             if(this.cachedTarget.isNotifyTarget){
3742                 this.cachedTarget.notifyOut(this, e, this.dragData);
3743             }
3744             this.cacheTarget = null;
3745         }
3746         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3747
3748         if(this.afterInvalidDrop){
3749             /**
3750              * An empty function by default, but provided so that you can perform a custom action
3751              * after an invalid drop has occurred by providing an implementation.
3752              * @param {Event} e The event object
3753              * @param {String} id The id of the dropped element
3754              * @method afterInvalidDrop
3755              */
3756             this.afterInvalidDrop(e, id);
3757         }
3758     },
3759
3760     // private
3761     afterRepair : function(){
3762         if(Roo.enableFx){
3763             this.el.highlight(this.hlColor || "c3daf9");
3764         }
3765         this.dragging = false;
3766     },
3767
3768     /**
3769      * An empty function by default, but provided so that you can perform a custom action after an invalid
3770      * drop has occurred.
3771      * @param {Roo.dd.DragDrop} target The drop target
3772      * @param {Event} e The event object
3773      * @param {String} id The id of the dragged element
3774      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3775      */
3776     beforeInvalidDrop : function(target, e, id){
3777         return true;
3778     },
3779
3780     // private
3781     handleMouseDown : function(e){
3782         if(this.dragging) {
3783             return;
3784         }
3785         var data = this.getDragData(e);
3786         if(data && this.onBeforeDrag(data, e) !== false){
3787             this.dragData = data;
3788             this.proxy.stop();
3789             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3790         } 
3791     },
3792
3793     /**
3794      * An empty function by default, but provided so that you can perform a custom action before the initial
3795      * drag event begins and optionally cancel it.
3796      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3797      * @param {Event} e The event object
3798      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3799      */
3800     onBeforeDrag : function(data, e){
3801         return true;
3802     },
3803
3804     /**
3805      * An empty function by default, but provided so that you can perform a custom action once the initial
3806      * drag event has begun.  The drag cannot be canceled from this function.
3807      * @param {Number} x The x position of the click on the dragged object
3808      * @param {Number} y The y position of the click on the dragged object
3809      */
3810     onStartDrag : Roo.emptyFn,
3811
3812     // private - YUI override
3813     startDrag : function(x, y){
3814         this.proxy.reset();
3815         this.dragging = true;
3816         this.proxy.update("");
3817         this.onInitDrag(x, y);
3818         this.proxy.show();
3819     },
3820
3821     // private
3822     onInitDrag : function(x, y){
3823         var clone = this.el.dom.cloneNode(true);
3824         clone.id = Roo.id(); // prevent duplicate ids
3825         this.proxy.update(clone);
3826         this.onStartDrag(x, y);
3827         return true;
3828     },
3829
3830     /**
3831      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3832      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3833      */
3834     getProxy : function(){
3835         return this.proxy;  
3836     },
3837
3838     /**
3839      * Hides the drag source's {@link Roo.dd.StatusProxy}
3840      */
3841     hideProxy : function(){
3842         this.proxy.hide();  
3843         this.proxy.reset(true);
3844         this.dragging = false;
3845     },
3846
3847     // private
3848     triggerCacheRefresh : function(){
3849         Roo.dd.DDM.refreshCache(this.groups);
3850     },
3851
3852     // private - override to prevent hiding
3853     b4EndDrag: function(e) {
3854     },
3855
3856     // private - override to prevent moving
3857     endDrag : function(e){
3858         this.onEndDrag(this.dragData, e);
3859     },
3860
3861     // private
3862     onEndDrag : function(data, e){
3863     },
3864     
3865     // private - pin to cursor
3866     autoOffset : function(x, y) {
3867         this.setDelta(-12, -20);
3868     }    
3869 });/*
3870  * Based on:
3871  * Ext JS Library 1.1.1
3872  * Copyright(c) 2006-2007, Ext JS, LLC.
3873  *
3874  * Originally Released Under LGPL - original licence link has changed is not relivant.
3875  *
3876  * Fork - LGPL
3877  * <script type="text/javascript">
3878  */
3879
3880
3881 /**
3882  * @class Roo.dd.DropTarget
3883  * @extends Roo.dd.DDTarget
3884  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3885  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3886  * @constructor
3887  * @param {String/HTMLElement/Element} el The container element
3888  * @param {Object} config
3889  */
3890 Roo.dd.DropTarget = function(el, config){
3891     this.el = Roo.get(el);
3892     
3893     Roo.apply(this, config);
3894     
3895     if(this.containerScroll){
3896         Roo.dd.ScrollManager.register(this.el);
3897     }
3898     
3899      
3900     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3901         this.el.dom, 
3902         this.ddGroup || this.group,
3903         {
3904             isTarget: true,
3905             events : {
3906                  /**
3907                  * @scope Roo.dd.DropTarget
3908                  */
3909                  
3910                  /**
3911                  * @event enter
3912                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913                  * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914                  * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915                  * 
3916                  * IMPORTANT : it should set this.overClass and this.dropAllowed
3917                  * 
3918                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919                  * @param {Event} e The event
3920                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3921                  */
3922                 "enter" : true,
3923                 
3924                  /**
3925                  * @event over
3926                  * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927                  * This method will be called on every mouse movement while the drag source is over the drop target.
3928                  * This default implementation simply returns the dropAllowed config value.
3929                  * 
3930                  * IMPORTANT : it should set this.dropAllowed
3931                  * 
3932                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933                  * @param {Event} e The event
3934                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3935                  
3936                  */
3937                 "over" : true,
3938                 /**
3939                  * @event out
3940                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941                  * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942                  * overClass (if any) from the drop element.
3943                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944                  * @param {Event} e The event
3945                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3946                  */
3947                  "out" : true,
3948                  
3949                 /**
3950                  * @event drop
3951                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952                  * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953                  * implementation that does something to process the drop event and returns true so that the drag source's
3954                  * repair action does not run.
3955                  * 
3956                  * IMPORTANT : it should set this.success
3957                  * 
3958                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959                  * @param {Event} e The event
3960                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3961                 */
3962                  "drop" : true
3963             }
3964                 
3965         
3966         }
3967     );
3968
3969 };
3970
3971 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3972     /**
3973      * @cfg {String} overClass
3974      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3975      */
3976      /**
3977      * @cfg {String} ddGroup
3978      * The drag drop group to handle drop events for
3979      */
3980      
3981     /**
3982      * @cfg {String} dropAllowed
3983      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3984      */
3985     dropAllowed : "x-dd-drop-ok",
3986     /**
3987      * @cfg {String} dropNotAllowed
3988      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3989      */
3990     dropNotAllowed : "x-dd-drop-nodrop",
3991     /**
3992      * @cfg {boolean} success
3993      * set this after drop listener.. 
3994      */
3995     success : false,
3996     /**
3997      * @cfg {boolean} valid
3998      * if the drop point is valid for over/enter..
3999      */
4000     valid : false,
4001     // private
4002     isTarget : true,
4003
4004     // private
4005     isNotifyTarget : true,
4006     
4007     /**
4008      * @hide
4009      */
4010     notifyEnter : function(dd, e, data){
4011         this.valid = true;
4012         this.fireEvent('enter', this, dd, e, data);
4013         if(this.overClass){
4014             this.el.addClass(this.overClass);
4015         }
4016         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4017     },
4018
4019     /**
4020      * @hide
4021      */
4022     notifyOver : function(dd, e, data){
4023         this.valid = true;
4024         this.fireEvent('over', this, dd, e, data);
4025         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4026     },
4027
4028     /**
4029      * @hide
4030      */
4031     notifyOut : function(dd, e, data){
4032         this.fireEvent('out', this, dd, e, data);
4033         if(this.overClass){
4034             this.el.removeClass(this.overClass);
4035         }
4036     },
4037
4038     /**
4039      * @hide
4040      */
4041     notifyDrop : function(dd, e, data){
4042         this.success = false;
4043         this.fireEvent('drop', this, dd, e, data);
4044         return this.success;
4045     }
4046 });/*
4047  * Based on:
4048  * Ext JS Library 1.1.1
4049  * Copyright(c) 2006-2007, Ext JS, LLC.
4050  *
4051  * Originally Released Under LGPL - original licence link has changed is not relivant.
4052  *
4053  * Fork - LGPL
4054  * <script type="text/javascript">
4055  */
4056
4057
4058 /**
4059  * @class Roo.dd.DragZone
4060  * @extends Roo.dd.DragSource
4061  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4062  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4063  * @constructor
4064  * @param {String/HTMLElement/Element} el The container element
4065  * @param {Object} config
4066  */
4067 Roo.dd.DragZone = function(el, config){
4068     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4069     if(this.containerScroll){
4070         Roo.dd.ScrollManager.register(this.el);
4071     }
4072 };
4073
4074 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4075     /**
4076      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4077      * for auto scrolling during drag operations.
4078      */
4079     /**
4080      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4081      * method after a failed drop (defaults to "c3daf9" - light blue)
4082      */
4083
4084     /**
4085      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4086      * for a valid target to drag based on the mouse down. Override this method
4087      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4088      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4089      * @param {EventObject} e The mouse down event
4090      * @return {Object} The dragData
4091      */
4092     getDragData : function(e){
4093         return Roo.dd.Registry.getHandleFromEvent(e);
4094     },
4095     
4096     /**
4097      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4098      * this.dragData.ddel
4099      * @param {Number} x The x position of the click on the dragged object
4100      * @param {Number} y The y position of the click on the dragged object
4101      * @return {Boolean} true to continue the drag, false to cancel
4102      */
4103     onInitDrag : function(x, y){
4104         this.proxy.update(this.dragData.ddel.cloneNode(true));
4105         this.onStartDrag(x, y);
4106         return true;
4107     },
4108     
4109     /**
4110      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4111      */
4112     afterRepair : function(){
4113         if(Roo.enableFx){
4114             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4115         }
4116         this.dragging = false;
4117     },
4118
4119     /**
4120      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4121      * the XY of this.dragData.ddel
4122      * @param {EventObject} e The mouse up event
4123      * @return {Array} The xy location (e.g. [100, 200])
4124      */
4125     getRepairXY : function(e){
4126         return Roo.Element.fly(this.dragData.ddel).getXY();  
4127     }
4128 });/*
4129  * Based on:
4130  * Ext JS Library 1.1.1
4131  * Copyright(c) 2006-2007, Ext JS, LLC.
4132  *
4133  * Originally Released Under LGPL - original licence link has changed is not relivant.
4134  *
4135  * Fork - LGPL
4136  * <script type="text/javascript">
4137  */
4138 /**
4139  * @class Roo.dd.DropZone
4140  * @extends Roo.dd.DropTarget
4141  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4142  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4143  * @constructor
4144  * @param {String/HTMLElement/Element} el The container element
4145  * @param {Object} config
4146  */
4147 Roo.dd.DropZone = function(el, config){
4148     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4149 };
4150
4151 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4152     /**
4153      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4154      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4155      * provide your own custom lookup.
4156      * @param {Event} e The event
4157      * @return {Object} data The custom data
4158      */
4159     getTargetFromEvent : function(e){
4160         return Roo.dd.Registry.getTargetFromEvent(e);
4161     },
4162
4163     /**
4164      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4165      * that it has registered.  This method has no default implementation and should be overridden to provide
4166      * node-specific processing if necessary.
4167      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4168      * {@link #getTargetFromEvent} for this node)
4169      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4170      * @param {Event} e The event
4171      * @param {Object} data An object containing arbitrary data supplied by the drag source
4172      */
4173     onNodeEnter : function(n, dd, e, data){
4174         
4175     },
4176
4177     /**
4178      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4179      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4180      * overridden to provide the proper feedback.
4181      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4182      * {@link #getTargetFromEvent} for this node)
4183      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4184      * @param {Event} e The event
4185      * @param {Object} data An object containing arbitrary data supplied by the drag source
4186      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4187      * underlying {@link Roo.dd.StatusProxy} can be updated
4188      */
4189     onNodeOver : function(n, dd, e, data){
4190         return this.dropAllowed;
4191     },
4192
4193     /**
4194      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4195      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4196      * node-specific processing if necessary.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      */
4203     onNodeOut : function(n, dd, e, data){
4204         
4205     },
4206
4207     /**
4208      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4209      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4210      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4211      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4212      * {@link #getTargetFromEvent} for this node)
4213      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4214      * @param {Event} e The event
4215      * @param {Object} data An object containing arbitrary data supplied by the drag source
4216      * @return {Boolean} True if the drop was valid, else false
4217      */
4218     onNodeDrop : function(n, dd, e, data){
4219         return false;
4220     },
4221
4222     /**
4223      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4224      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4225      * it should be overridden to provide the proper feedback if necessary.
4226      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4227      * @param {Event} e The event
4228      * @param {Object} data An object containing arbitrary data supplied by the drag source
4229      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4230      * underlying {@link Roo.dd.StatusProxy} can be updated
4231      */
4232     onContainerOver : function(dd, e, data){
4233         return this.dropNotAllowed;
4234     },
4235
4236     /**
4237      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4238      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4239      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4240      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4241      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4242      * @param {Event} e The event
4243      * @param {Object} data An object containing arbitrary data supplied by the drag source
4244      * @return {Boolean} True if the drop was valid, else false
4245      */
4246     onContainerDrop : function(dd, e, data){
4247         return false;
4248     },
4249
4250     /**
4251      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4252      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4253      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4254      * you should override this method and provide a custom implementation.
4255      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4256      * @param {Event} e The event
4257      * @param {Object} data An object containing arbitrary data supplied by the drag source
4258      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4259      * underlying {@link Roo.dd.StatusProxy} can be updated
4260      */
4261     notifyEnter : function(dd, e, data){
4262         return this.dropNotAllowed;
4263     },
4264
4265     /**
4266      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4267      * This method will be called on every mouse movement while the drag source is over the drop zone.
4268      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4269      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4270      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4271      * registered node, it will call {@link #onContainerOver}.
4272      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4273      * @param {Event} e The event
4274      * @param {Object} data An object containing arbitrary data supplied by the drag source
4275      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4276      * underlying {@link Roo.dd.StatusProxy} can be updated
4277      */
4278     notifyOver : function(dd, e, data){
4279         var n = this.getTargetFromEvent(e);
4280         if(!n){ // not over valid drop target
4281             if(this.lastOverNode){
4282                 this.onNodeOut(this.lastOverNode, dd, e, data);
4283                 this.lastOverNode = null;
4284             }
4285             return this.onContainerOver(dd, e, data);
4286         }
4287         if(this.lastOverNode != n){
4288             if(this.lastOverNode){
4289                 this.onNodeOut(this.lastOverNode, dd, e, data);
4290             }
4291             this.onNodeEnter(n, dd, e, data);
4292             this.lastOverNode = n;
4293         }
4294         return this.onNodeOver(n, dd, e, data);
4295     },
4296
4297     /**
4298      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4299      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4300      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4301      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4302      * @param {Event} e The event
4303      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4304      */
4305     notifyOut : function(dd, e, data){
4306         if(this.lastOverNode){
4307             this.onNodeOut(this.lastOverNode, dd, e, data);
4308             this.lastOverNode = null;
4309         }
4310     },
4311
4312     /**
4313      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4314      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4315      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4316      * otherwise it will call {@link #onContainerDrop}.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag source
4320      * @return {Boolean} True if the drop was valid, else false
4321      */
4322     notifyDrop : function(dd, e, data){
4323         if(this.lastOverNode){
4324             this.onNodeOut(this.lastOverNode, dd, e, data);
4325             this.lastOverNode = null;
4326         }
4327         var n = this.getTargetFromEvent(e);
4328         return n ?
4329             this.onNodeDrop(n, dd, e, data) :
4330             this.onContainerDrop(dd, e, data);
4331     },
4332
4333     // private
4334     triggerCacheRefresh : function(){
4335         Roo.dd.DDM.refreshCache(this.groups);
4336     }  
4337 });/*
4338  * Based on:
4339  * Ext JS Library 1.1.1
4340  * Copyright(c) 2006-2007, Ext JS, LLC.
4341  *
4342  * Originally Released Under LGPL - original licence link has changed is not relivant.
4343  *
4344  * Fork - LGPL
4345  * <script type="text/javascript">
4346  */
4347
4348
4349 /**
4350  * @class Roo.data.SortTypes
4351  * @singleton
4352  * Defines the default sorting (casting?) comparison functions used when sorting data.
4353  */
4354 Roo.data.SortTypes = {
4355     /**
4356      * Default sort that does nothing
4357      * @param {Mixed} s The value being converted
4358      * @return {Mixed} The comparison value
4359      */
4360     none : function(s){
4361         return s;
4362     },
4363     
4364     /**
4365      * The regular expression used to strip tags
4366      * @type {RegExp}
4367      * @property
4368      */
4369     stripTagsRE : /<\/?[^>]+>/gi,
4370     
4371     /**
4372      * Strips all HTML tags to sort on text only
4373      * @param {Mixed} s The value being converted
4374      * @return {String} The comparison value
4375      */
4376     asText : function(s){
4377         return String(s).replace(this.stripTagsRE, "");
4378     },
4379     
4380     /**
4381      * Strips all HTML tags to sort on text only - Case insensitive
4382      * @param {Mixed} s The value being converted
4383      * @return {String} The comparison value
4384      */
4385     asUCText : function(s){
4386         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4387     },
4388     
4389     /**
4390      * Case insensitive string
4391      * @param {Mixed} s The value being converted
4392      * @return {String} The comparison value
4393      */
4394     asUCString : function(s) {
4395         return String(s).toUpperCase();
4396     },
4397     
4398     /**
4399      * Date sorting
4400      * @param {Mixed} s The value being converted
4401      * @return {Number} The comparison value
4402      */
4403     asDate : function(s) {
4404         if(!s){
4405             return 0;
4406         }
4407         if(s instanceof Date){
4408             return s.getTime();
4409         }
4410         return Date.parse(String(s));
4411     },
4412     
4413     /**
4414      * Float sorting
4415      * @param {Mixed} s The value being converted
4416      * @return {Float} The comparison value
4417      */
4418     asFloat : function(s) {
4419         var val = parseFloat(String(s).replace(/,/g, ""));
4420         if(isNaN(val)) val = 0;
4421         return val;
4422     },
4423     
4424     /**
4425      * Integer sorting
4426      * @param {Mixed} s The value being converted
4427      * @return {Number} The comparison value
4428      */
4429     asInt : function(s) {
4430         var val = parseInt(String(s).replace(/,/g, ""));
4431         if(isNaN(val)) val = 0;
4432         return val;
4433     }
4434 };/*
4435  * Based on:
4436  * Ext JS Library 1.1.1
4437  * Copyright(c) 2006-2007, Ext JS, LLC.
4438  *
4439  * Originally Released Under LGPL - original licence link has changed is not relivant.
4440  *
4441  * Fork - LGPL
4442  * <script type="text/javascript">
4443  */
4444
4445 /**
4446 * @class Roo.data.Record
4447  * Instances of this class encapsulate both record <em>definition</em> information, and record
4448  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4449  * to access Records cached in an {@link Roo.data.Store} object.<br>
4450  * <p>
4451  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4452  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4453  * objects.<br>
4454  * <p>
4455  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4456  * @constructor
4457  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4458  * {@link #create}. The parameters are the same.
4459  * @param {Array} data An associative Array of data values keyed by the field name.
4460  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4461  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4462  * not specified an integer id is generated.
4463  */
4464 Roo.data.Record = function(data, id){
4465     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4466     this.data = data;
4467 };
4468
4469 /**
4470  * Generate a constructor for a specific record layout.
4471  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4472  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4473  * Each field definition object may contain the following properties: <ul>
4474  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4475  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4476  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4477  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4478  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4479  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4480  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4481  * this may be omitted.</p></li>
4482  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4483  * <ul><li>auto (Default, implies no conversion)</li>
4484  * <li>string</li>
4485  * <li>int</li>
4486  * <li>float</li>
4487  * <li>boolean</li>
4488  * <li>date</li></ul></p></li>
4489  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4490  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4491  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4492  * by the Reader into an object that will be stored in the Record. It is passed the
4493  * following parameters:<ul>
4494  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4495  * </ul></p></li>
4496  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4497  * </ul>
4498  * <br>usage:<br><pre><code>
4499 var TopicRecord = Roo.data.Record.create(
4500     {name: 'title', mapping: 'topic_title'},
4501     {name: 'author', mapping: 'username'},
4502     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4503     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4504     {name: 'lastPoster', mapping: 'user2'},
4505     {name: 'excerpt', mapping: 'post_text'}
4506 );
4507
4508 var myNewRecord = new TopicRecord({
4509     title: 'Do my job please',
4510     author: 'noobie',
4511     totalPosts: 1,
4512     lastPost: new Date(),
4513     lastPoster: 'Animal',
4514     excerpt: 'No way dude!'
4515 });
4516 myStore.add(myNewRecord);
4517 </code></pre>
4518  * @method create
4519  * @static
4520  */
4521 Roo.data.Record.create = function(o){
4522     var f = function(){
4523         f.superclass.constructor.apply(this, arguments);
4524     };
4525     Roo.extend(f, Roo.data.Record);
4526     var p = f.prototype;
4527     p.fields = new Roo.util.MixedCollection(false, function(field){
4528         return field.name;
4529     });
4530     for(var i = 0, len = o.length; i < len; i++){
4531         p.fields.add(new Roo.data.Field(o[i]));
4532     }
4533     f.getField = function(name){
4534         return p.fields.get(name);  
4535     };
4536     return f;
4537 };
4538
4539 Roo.data.Record.AUTO_ID = 1000;
4540 Roo.data.Record.EDIT = 'edit';
4541 Roo.data.Record.REJECT = 'reject';
4542 Roo.data.Record.COMMIT = 'commit';
4543
4544 Roo.data.Record.prototype = {
4545     /**
4546      * Readonly flag - true if this record has been modified.
4547      * @type Boolean
4548      */
4549     dirty : false,
4550     editing : false,
4551     error: null,
4552     modified: null,
4553
4554     // private
4555     join : function(store){
4556         this.store = store;
4557     },
4558
4559     /**
4560      * Set the named field to the specified value.
4561      * @param {String} name The name of the field to set.
4562      * @param {Object} value The value to set the field to.
4563      */
4564     set : function(name, value){
4565         if(this.data[name] == value){
4566             return;
4567         }
4568         this.dirty = true;
4569         if(!this.modified){
4570             this.modified = {};
4571         }
4572         if(typeof this.modified[name] == 'undefined'){
4573             this.modified[name] = this.data[name];
4574         }
4575         this.data[name] = value;
4576         if(!this.editing){
4577             this.store.afterEdit(this);
4578         }       
4579     },
4580
4581     /**
4582      * Get the value of the named field.
4583      * @param {String} name The name of the field to get the value of.
4584      * @return {Object} The value of the field.
4585      */
4586     get : function(name){
4587         return this.data[name]; 
4588     },
4589
4590     // private
4591     beginEdit : function(){
4592         this.editing = true;
4593         this.modified = {}; 
4594     },
4595
4596     // private
4597     cancelEdit : function(){
4598         this.editing = false;
4599         delete this.modified;
4600     },
4601
4602     // private
4603     endEdit : function(){
4604         this.editing = false;
4605         if(this.dirty && this.store){
4606             this.store.afterEdit(this);
4607         }
4608     },
4609
4610     /**
4611      * Usually called by the {@link Roo.data.Store} which owns the Record.
4612      * Rejects all changes made to the Record since either creation, or the last commit operation.
4613      * Modified fields are reverted to their original values.
4614      * <p>
4615      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4616      * of reject operations.
4617      */
4618     reject : function(){
4619         var m = this.modified;
4620         for(var n in m){
4621             if(typeof m[n] != "function"){
4622                 this.data[n] = m[n];
4623             }
4624         }
4625         this.dirty = false;
4626         delete this.modified;
4627         this.editing = false;
4628         if(this.store){
4629             this.store.afterReject(this);
4630         }
4631     },
4632
4633     /**
4634      * Usually called by the {@link Roo.data.Store} which owns the Record.
4635      * Commits all changes made to the Record since either creation, or the last commit operation.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of commit operations.
4639      */
4640     commit : function(){
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterCommit(this);
4646         }
4647     },
4648
4649     // private
4650     hasError : function(){
4651         return this.error != null;
4652     },
4653
4654     // private
4655     clearError : function(){
4656         this.error = null;
4657     },
4658
4659     /**
4660      * Creates a copy of this record.
4661      * @param {String} id (optional) A new record id if you don't want to use this record's id
4662      * @return {Record}
4663      */
4664     copy : function(newId) {
4665         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4666     }
4667 };/*
4668  * Based on:
4669  * Ext JS Library 1.1.1
4670  * Copyright(c) 2006-2007, Ext JS, LLC.
4671  *
4672  * Originally Released Under LGPL - original licence link has changed is not relivant.
4673  *
4674  * Fork - LGPL
4675  * <script type="text/javascript">
4676  */
4677
4678
4679
4680 /**
4681  * @class Roo.data.Store
4682  * @extends Roo.util.Observable
4683  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4684  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4685  * <p>
4686  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4687  * has no knowledge of the format of the data returned by the Proxy.<br>
4688  * <p>
4689  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4690  * instances from the data object. These records are cached and made available through accessor functions.
4691  * @constructor
4692  * Creates a new Store.
4693  * @param {Object} config A config object containing the objects needed for the Store to access data,
4694  * and read the data into Records.
4695  */
4696 Roo.data.Store = function(config){
4697     this.data = new Roo.util.MixedCollection(false);
4698     this.data.getKey = function(o){
4699         return o.id;
4700     };
4701     this.baseParams = {};
4702     // private
4703     this.paramNames = {
4704         "start" : "start",
4705         "limit" : "limit",
4706         "sort" : "sort",
4707         "dir" : "dir"
4708     };
4709
4710     if(config && config.data){
4711         this.inlineData = config.data;
4712         delete config.data;
4713     }
4714
4715     Roo.apply(this, config);
4716     
4717     if(this.reader){ // reader passed
4718         this.reader = Roo.factory(this.reader, Roo.data);
4719         this.reader.xmodule = this.xmodule || false;
4720         if(!this.recordType){
4721             this.recordType = this.reader.recordType;
4722         }
4723         if(this.reader.onMetaChange){
4724             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4725         }
4726     }
4727
4728     if(this.recordType){
4729         this.fields = this.recordType.prototype.fields;
4730     }
4731     this.modified = [];
4732
4733     this.addEvents({
4734         /**
4735          * @event datachanged
4736          * Fires when the data cache has changed, and a widget which is using this Store
4737          * as a Record cache should refresh its view.
4738          * @param {Store} this
4739          */
4740         datachanged : true,
4741         /**
4742          * @event metachange
4743          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4744          * @param {Store} this
4745          * @param {Object} meta The JSON metadata
4746          */
4747         metachange : true,
4748         /**
4749          * @event add
4750          * Fires when Records have been added to the Store
4751          * @param {Store} this
4752          * @param {Roo.data.Record[]} records The array of Records added
4753          * @param {Number} index The index at which the record(s) were added
4754          */
4755         add : true,
4756         /**
4757          * @event remove
4758          * Fires when a Record has been removed from the Store
4759          * @param {Store} this
4760          * @param {Roo.data.Record} record The Record that was removed
4761          * @param {Number} index The index at which the record was removed
4762          */
4763         remove : true,
4764         /**
4765          * @event update
4766          * Fires when a Record has been updated
4767          * @param {Store} this
4768          * @param {Roo.data.Record} record The Record that was updated
4769          * @param {String} operation The update operation being performed.  Value may be one of:
4770          * <pre><code>
4771  Roo.data.Record.EDIT
4772  Roo.data.Record.REJECT
4773  Roo.data.Record.COMMIT
4774          * </code></pre>
4775          */
4776         update : true,
4777         /**
4778          * @event clear
4779          * Fires when the data cache has been cleared.
4780          * @param {Store} this
4781          */
4782         clear : true,
4783         /**
4784          * @event beforeload
4785          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4786          * the load action will be canceled.
4787          * @param {Store} this
4788          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4789          */
4790         beforeload : true,
4791         /**
4792          * @event load
4793          * Fires after a new set of Records has been loaded.
4794          * @param {Store} this
4795          * @param {Roo.data.Record[]} records The Records that were loaded
4796          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4797          */
4798         load : true,
4799         /**
4800          * @event loadexception
4801          * Fires if an exception occurs in the Proxy during loading.
4802          * Called with the signature of the Proxy's "loadexception" event.
4803          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4804          * 
4805          * @param {Proxy} 
4806          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4807          * @param {Object} load options 
4808          * @param {Object} jsonData from your request (normally this contains the Exception)
4809          */
4810         loadexception : true
4811     });
4812     
4813     if(this.proxy){
4814         this.proxy = Roo.factory(this.proxy, Roo.data);
4815         this.proxy.xmodule = this.xmodule || false;
4816         this.relayEvents(this.proxy,  ["loadexception"]);
4817     }
4818     this.sortToggle = {};
4819
4820     Roo.data.Store.superclass.constructor.call(this);
4821
4822     if(this.inlineData){
4823         this.loadData(this.inlineData);
4824         delete this.inlineData;
4825     }
4826 };
4827 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4828      /**
4829     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4830     * without a remote query - used by combo/forms at present.
4831     */
4832     
4833     /**
4834     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4835     */
4836     /**
4837     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4838     */
4839     /**
4840     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4841     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4842     */
4843     /**
4844     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4845     * on any HTTP request
4846     */
4847     /**
4848     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4849     */
4850     /**
4851     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4852     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4853     */
4854     remoteSort : false,
4855
4856     /**
4857     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4858      * loaded or when a record is removed. (defaults to false).
4859     */
4860     pruneModifiedRecords : false,
4861
4862     // private
4863     lastOptions : null,
4864
4865     /**
4866      * Add Records to the Store and fires the add event.
4867      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4868      */
4869     add : function(records){
4870         records = [].concat(records);
4871         for(var i = 0, len = records.length; i < len; i++){
4872             records[i].join(this);
4873         }
4874         var index = this.data.length;
4875         this.data.addAll(records);
4876         this.fireEvent("add", this, records, index);
4877     },
4878
4879     /**
4880      * Remove a Record from the Store and fires the remove event.
4881      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4882      */
4883     remove : function(record){
4884         var index = this.data.indexOf(record);
4885         this.data.removeAt(index);
4886         if(this.pruneModifiedRecords){
4887             this.modified.remove(record);
4888         }
4889         this.fireEvent("remove", this, record, index);
4890     },
4891
4892     /**
4893      * Remove all Records from the Store and fires the clear event.
4894      */
4895     removeAll : function(){
4896         this.data.clear();
4897         if(this.pruneModifiedRecords){
4898             this.modified = [];
4899         }
4900         this.fireEvent("clear", this);
4901     },
4902
4903     /**
4904      * Inserts Records to the Store at the given index and fires the add event.
4905      * @param {Number} index The start index at which to insert the passed Records.
4906      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4907      */
4908     insert : function(index, records){
4909         records = [].concat(records);
4910         for(var i = 0, len = records.length; i < len; i++){
4911             this.data.insert(index, records[i]);
4912             records[i].join(this);
4913         }
4914         this.fireEvent("add", this, records, index);
4915     },
4916
4917     /**
4918      * Get the index within the cache of the passed Record.
4919      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4920      * @return {Number} The index of the passed Record. Returns -1 if not found.
4921      */
4922     indexOf : function(record){
4923         return this.data.indexOf(record);
4924     },
4925
4926     /**
4927      * Get the index within the cache of the Record with the passed id.
4928      * @param {String} id The id of the Record to find.
4929      * @return {Number} The index of the Record. Returns -1 if not found.
4930      */
4931     indexOfId : function(id){
4932         return this.data.indexOfKey(id);
4933     },
4934
4935     /**
4936      * Get the Record with the specified id.
4937      * @param {String} id The id of the Record to find.
4938      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4939      */
4940     getById : function(id){
4941         return this.data.key(id);
4942     },
4943
4944     /**
4945      * Get the Record at the specified index.
4946      * @param {Number} index The index of the Record to find.
4947      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4948      */
4949     getAt : function(index){
4950         return this.data.itemAt(index);
4951     },
4952
4953     /**
4954      * Returns a range of Records between specified indices.
4955      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4956      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4957      * @return {Roo.data.Record[]} An array of Records
4958      */
4959     getRange : function(start, end){
4960         return this.data.getRange(start, end);
4961     },
4962
4963     // private
4964     storeOptions : function(o){
4965         o = Roo.apply({}, o);
4966         delete o.callback;
4967         delete o.scope;
4968         this.lastOptions = o;
4969     },
4970
4971     /**
4972      * Loads the Record cache from the configured Proxy using the configured Reader.
4973      * <p>
4974      * If using remote paging, then the first load call must specify the <em>start</em>
4975      * and <em>limit</em> properties in the options.params property to establish the initial
4976      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4977      * <p>
4978      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4979      * and this call will return before the new data has been loaded. Perform any post-processing
4980      * in a callback function, or in a "load" event handler.</strong>
4981      * <p>
4982      * @param {Object} options An object containing properties which control loading options:<ul>
4983      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4984      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4985      * passed the following arguments:<ul>
4986      * <li>r : Roo.data.Record[]</li>
4987      * <li>options: Options object from the load call</li>
4988      * <li>success: Boolean success indicator</li></ul></li>
4989      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4990      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4991      * </ul>
4992      */
4993     load : function(options){
4994         options = options || {};
4995         if(this.fireEvent("beforeload", this, options) !== false){
4996             this.storeOptions(options);
4997             var p = Roo.apply(options.params || {}, this.baseParams);
4998             // if meta was not loaded from remote source.. try requesting it.
4999             if (!this.reader.metaFromRemote) {
5000                 p._requestMeta = 1;
5001             }
5002             if(this.sortInfo && this.remoteSort){
5003                 var pn = this.paramNames;
5004                 p[pn["sort"]] = this.sortInfo.field;
5005                 p[pn["dir"]] = this.sortInfo.direction;
5006             }
5007             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5008         }
5009     },
5010
5011     /**
5012      * Reloads the Record cache from the configured Proxy using the configured Reader and
5013      * the options from the last load operation performed.
5014      * @param {Object} options (optional) An object containing properties which may override the options
5015      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5016      * the most recently used options are reused).
5017      */
5018     reload : function(options){
5019         this.load(Roo.applyIf(options||{}, this.lastOptions));
5020     },
5021
5022     // private
5023     // Called as a callback by the Reader during a load operation.
5024     loadRecords : function(o, options, success){
5025         if(!o || success === false){
5026             if(success !== false){
5027                 this.fireEvent("load", this, [], options);
5028             }
5029             if(options.callback){
5030                 options.callback.call(options.scope || this, [], options, false);
5031             }
5032             return;
5033         }
5034         // if data returned failure - throw an exception.
5035         if (o.success === false) {
5036             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5037             return;
5038         }
5039         var r = o.records, t = o.totalRecords || r.length;
5040         if(!options || options.add !== true){
5041             if(this.pruneModifiedRecords){
5042                 this.modified = [];
5043             }
5044             for(var i = 0, len = r.length; i < len; i++){
5045                 r[i].join(this);
5046             }
5047             if(this.snapshot){
5048                 this.data = this.snapshot;
5049                 delete this.snapshot;
5050             }
5051             this.data.clear();
5052             this.data.addAll(r);
5053             this.totalLength = t;
5054             this.applySort();
5055             this.fireEvent("datachanged", this);
5056         }else{
5057             this.totalLength = Math.max(t, this.data.length+r.length);
5058             this.add(r);
5059         }
5060         this.fireEvent("load", this, r, options);
5061         if(options.callback){
5062             options.callback.call(options.scope || this, r, options, true);
5063         }
5064     },
5065
5066     /**
5067      * Loads data from a passed data block. A Reader which understands the format of the data
5068      * must have been configured in the constructor.
5069      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5070      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5071      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5072      */
5073     loadData : function(o, append){
5074         var r = this.reader.readRecords(o);
5075         this.loadRecords(r, {add: append}, true);
5076     },
5077
5078     /**
5079      * Gets the number of cached records.
5080      * <p>
5081      * <em>If using paging, this may not be the total size of the dataset. If the data object
5082      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5083      * the data set size</em>
5084      */
5085     getCount : function(){
5086         return this.data.length || 0;
5087     },
5088
5089     /**
5090      * Gets the total number of records in the dataset as returned by the server.
5091      * <p>
5092      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5093      * the dataset size</em>
5094      */
5095     getTotalCount : function(){
5096         return this.totalLength || 0;
5097     },
5098
5099     /**
5100      * Returns the sort state of the Store as an object with two properties:
5101      * <pre><code>
5102  field {String} The name of the field by which the Records are sorted
5103  direction {String} The sort order, "ASC" or "DESC"
5104      * </code></pre>
5105      */
5106     getSortState : function(){
5107         return this.sortInfo;
5108     },
5109
5110     // private
5111     applySort : function(){
5112         if(this.sortInfo && !this.remoteSort){
5113             var s = this.sortInfo, f = s.field;
5114             var st = this.fields.get(f).sortType;
5115             var fn = function(r1, r2){
5116                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5117                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5118             };
5119             this.data.sort(s.direction, fn);
5120             if(this.snapshot && this.snapshot != this.data){
5121                 this.snapshot.sort(s.direction, fn);
5122             }
5123         }
5124     },
5125
5126     /**
5127      * Sets the default sort column and order to be used by the next load operation.
5128      * @param {String} fieldName The name of the field to sort by.
5129      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5130      */
5131     setDefaultSort : function(field, dir){
5132         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5133     },
5134
5135     /**
5136      * Sort the Records.
5137      * If remote sorting is used, the sort is performed on the server, and the cache is
5138      * reloaded. If local sorting is used, the cache is sorted internally.
5139      * @param {String} fieldName The name of the field to sort by.
5140      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5141      */
5142     sort : function(fieldName, dir){
5143         var f = this.fields.get(fieldName);
5144         if(!dir){
5145             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5146                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5147             }else{
5148                 dir = f.sortDir;
5149             }
5150         }
5151         this.sortToggle[f.name] = dir;
5152         this.sortInfo = {field: f.name, direction: dir};
5153         if(!this.remoteSort){
5154             this.applySort();
5155             this.fireEvent("datachanged", this);
5156         }else{
5157             this.load(this.lastOptions);
5158         }
5159     },
5160
5161     /**
5162      * Calls the specified function for each of the Records in the cache.
5163      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5164      * Returning <em>false</em> aborts and exits the iteration.
5165      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5166      */
5167     each : function(fn, scope){
5168         this.data.each(fn, scope);
5169     },
5170
5171     /**
5172      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5173      * (e.g., during paging).
5174      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5175      */
5176     getModifiedRecords : function(){
5177         return this.modified;
5178     },
5179
5180     // private
5181     createFilterFn : function(property, value, anyMatch){
5182         if(!value.exec){ // not a regex
5183             value = String(value);
5184             if(value.length == 0){
5185                 return false;
5186             }
5187             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5188         }
5189         return function(r){
5190             return value.test(r.data[property]);
5191         };
5192     },
5193
5194     /**
5195      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5196      * @param {String} property A field on your records
5197      * @param {Number} start The record index to start at (defaults to 0)
5198      * @param {Number} end The last record index to include (defaults to length - 1)
5199      * @return {Number} The sum
5200      */
5201     sum : function(property, start, end){
5202         var rs = this.data.items, v = 0;
5203         start = start || 0;
5204         end = (end || end === 0) ? end : rs.length-1;
5205
5206         for(var i = start; i <= end; i++){
5207             v += (rs[i].data[property] || 0);
5208         }
5209         return v;
5210     },
5211
5212     /**
5213      * Filter the records by a specified property.
5214      * @param {String} field A field on your records
5215      * @param {String/RegExp} value Either a string that the field
5216      * should start with or a RegExp to test against the field
5217      * @param {Boolean} anyMatch True to match any part not just the beginning
5218      */
5219     filter : function(property, value, anyMatch){
5220         var fn = this.createFilterFn(property, value, anyMatch);
5221         return fn ? this.filterBy(fn) : this.clearFilter();
5222     },
5223
5224     /**
5225      * Filter by a function. The specified function will be called with each
5226      * record in this data source. If the function returns true the record is included,
5227      * otherwise it is filtered.
5228      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5229      * @param {Object} scope (optional) The scope of the function (defaults to this)
5230      */
5231     filterBy : function(fn, scope){
5232         this.snapshot = this.snapshot || this.data;
5233         this.data = this.queryBy(fn, scope||this);
5234         this.fireEvent("datachanged", this);
5235     },
5236
5237     /**
5238      * Query the records by a specified property.
5239      * @param {String} field A field on your records
5240      * @param {String/RegExp} value Either a string that the field
5241      * should start with or a RegExp to test against the field
5242      * @param {Boolean} anyMatch True to match any part not just the beginning
5243      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5244      */
5245     query : function(property, value, anyMatch){
5246         var fn = this.createFilterFn(property, value, anyMatch);
5247         return fn ? this.queryBy(fn) : this.data.clone();
5248     },
5249
5250     /**
5251      * Query by a function. The specified function will be called with each
5252      * record in this data source. If the function returns true the record is included
5253      * in the results.
5254      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5255      * @param {Object} scope (optional) The scope of the function (defaults to this)
5256       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5257      **/
5258     queryBy : function(fn, scope){
5259         var data = this.snapshot || this.data;
5260         return data.filterBy(fn, scope||this);
5261     },
5262
5263     /**
5264      * Collects unique values for a particular dataIndex from this store.
5265      * @param {String} dataIndex The property to collect
5266      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5267      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5268      * @return {Array} An array of the unique values
5269      **/
5270     collect : function(dataIndex, allowNull, bypassFilter){
5271         var d = (bypassFilter === true && this.snapshot) ?
5272                 this.snapshot.items : this.data.items;
5273         var v, sv, r = [], l = {};
5274         for(var i = 0, len = d.length; i < len; i++){
5275             v = d[i].data[dataIndex];
5276             sv = String(v);
5277             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5278                 l[sv] = true;
5279                 r[r.length] = v;
5280             }
5281         }
5282         return r;
5283     },
5284
5285     /**
5286      * Revert to a view of the Record cache with no filtering applied.
5287      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5288      */
5289     clearFilter : function(suppressEvent){
5290         if(this.snapshot && this.snapshot != this.data){
5291             this.data = this.snapshot;
5292             delete this.snapshot;
5293             if(suppressEvent !== true){
5294                 this.fireEvent("datachanged", this);
5295             }
5296         }
5297     },
5298
5299     // private
5300     afterEdit : function(record){
5301         if(this.modified.indexOf(record) == -1){
5302             this.modified.push(record);
5303         }
5304         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5305     },
5306
5307     // private
5308     afterReject : function(record){
5309         this.modified.remove(record);
5310         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5311     },
5312
5313     // private
5314     afterCommit : function(record){
5315         this.modified.remove(record);
5316         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5317     },
5318
5319     /**
5320      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5321      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5322      */
5323     commitChanges : function(){
5324         var m = this.modified.slice(0);
5325         this.modified = [];
5326         for(var i = 0, len = m.length; i < len; i++){
5327             m[i].commit();
5328         }
5329     },
5330
5331     /**
5332      * Cancel outstanding changes on all changed records.
5333      */
5334     rejectChanges : function(){
5335         var m = this.modified.slice(0);
5336         this.modified = [];
5337         for(var i = 0, len = m.length; i < len; i++){
5338             m[i].reject();
5339         }
5340     },
5341
5342     onMetaChange : function(meta, rtype, o){
5343         this.recordType = rtype;
5344         this.fields = rtype.prototype.fields;
5345         delete this.snapshot;
5346         this.sortInfo = meta.sortInfo || this.sortInfo;
5347         this.modified = [];
5348         this.fireEvent('metachange', this, this.reader.meta);
5349     }
5350 });/*
5351  * Based on:
5352  * Ext JS Library 1.1.1
5353  * Copyright(c) 2006-2007, Ext JS, LLC.
5354  *
5355  * Originally Released Under LGPL - original licence link has changed is not relivant.
5356  *
5357  * Fork - LGPL
5358  * <script type="text/javascript">
5359  */
5360
5361 /**
5362  * @class Roo.data.SimpleStore
5363  * @extends Roo.data.Store
5364  * Small helper class to make creating Stores from Array data easier.
5365  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5366  * @cfg {Array} fields An array of field definition objects, or field name strings.
5367  * @cfg {Array} data The multi-dimensional array of data
5368  * @constructor
5369  * @param {Object} config
5370  */
5371 Roo.data.SimpleStore = function(config){
5372     Roo.data.SimpleStore.superclass.constructor.call(this, {
5373         isLocal : true,
5374         reader: new Roo.data.ArrayReader({
5375                 id: config.id
5376             },
5377             Roo.data.Record.create(config.fields)
5378         ),
5379         proxy : new Roo.data.MemoryProxy(config.data)
5380     });
5381     this.load();
5382 };
5383 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5384  * Based on:
5385  * Ext JS Library 1.1.1
5386  * Copyright(c) 2006-2007, Ext JS, LLC.
5387  *
5388  * Originally Released Under LGPL - original licence link has changed is not relivant.
5389  *
5390  * Fork - LGPL
5391  * <script type="text/javascript">
5392  */
5393
5394 /**
5395 /**
5396  * @extends Roo.data.Store
5397  * @class Roo.data.JsonStore
5398  * Small helper class to make creating Stores for JSON data easier. <br/>
5399 <pre><code>
5400 var store = new Roo.data.JsonStore({
5401     url: 'get-images.php',
5402     root: 'images',
5403     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5404 });
5405 </code></pre>
5406  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5407  * JsonReader and HttpProxy (unless inline data is provided).</b>
5408  * @cfg {Array} fields An array of field definition objects, or field name strings.
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.JsonStore = function(c){
5413     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5414         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5415         reader: new Roo.data.JsonReader(c, c.fields)
5416     }));
5417 };
5418 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429  
5430 Roo.data.Field = function(config){
5431     if(typeof config == "string"){
5432         config = {name: config};
5433     }
5434     Roo.apply(this, config);
5435     
5436     if(!this.type){
5437         this.type = "auto";
5438     }
5439     
5440     var st = Roo.data.SortTypes;
5441     // named sortTypes are supported, here we look them up
5442     if(typeof this.sortType == "string"){
5443         this.sortType = st[this.sortType];
5444     }
5445     
5446     // set default sortType for strings and dates
5447     if(!this.sortType){
5448         switch(this.type){
5449             case "string":
5450                 this.sortType = st.asUCString;
5451                 break;
5452             case "date":
5453                 this.sortType = st.asDate;
5454                 break;
5455             default:
5456                 this.sortType = st.none;
5457         }
5458     }
5459
5460     // define once
5461     var stripRe = /[\$,%]/g;
5462
5463     // prebuilt conversion function for this field, instead of
5464     // switching every time we're reading a value
5465     if(!this.convert){
5466         var cv, dateFormat = this.dateFormat;
5467         switch(this.type){
5468             case "":
5469             case "auto":
5470             case undefined:
5471                 cv = function(v){ return v; };
5472                 break;
5473             case "string":
5474                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5475                 break;
5476             case "int":
5477                 cv = function(v){
5478                     return v !== undefined && v !== null && v !== '' ?
5479                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5480                     };
5481                 break;
5482             case "float":
5483                 cv = function(v){
5484                     return v !== undefined && v !== null && v !== '' ?
5485                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5486                     };
5487                 break;
5488             case "bool":
5489             case "boolean":
5490                 cv = function(v){ return v === true || v === "true" || v == 1; };
5491                 break;
5492             case "date":
5493                 cv = function(v){
5494                     if(!v){
5495                         return '';
5496                     }
5497                     if(v instanceof Date){
5498                         return v;
5499                     }
5500                     if(dateFormat){
5501                         if(dateFormat == "timestamp"){
5502                             return new Date(v*1000);
5503                         }
5504                         return Date.parseDate(v, dateFormat);
5505                     }
5506                     var parsed = Date.parse(v);
5507                     return parsed ? new Date(parsed) : null;
5508                 };
5509              break;
5510             
5511         }
5512         this.convert = cv;
5513     }
5514 };
5515
5516 Roo.data.Field.prototype = {
5517     dateFormat: null,
5518     defaultValue: "",
5519     mapping: null,
5520     sortType : null,
5521     sortDir : "ASC"
5522 };/*
5523  * Based on:
5524  * Ext JS Library 1.1.1
5525  * Copyright(c) 2006-2007, Ext JS, LLC.
5526  *
5527  * Originally Released Under LGPL - original licence link has changed is not relivant.
5528  *
5529  * Fork - LGPL
5530  * <script type="text/javascript">
5531  */
5532  
5533 // Base class for reading structured data from a data source.  This class is intended to be
5534 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5535
5536 /**
5537  * @class Roo.data.DataReader
5538  * Base class for reading structured data from a data source.  This class is intended to be
5539  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5540  */
5541
5542 Roo.data.DataReader = function(meta, recordType){
5543     
5544     this.meta = meta;
5545     
5546     this.recordType = recordType instanceof Array ? 
5547         Roo.data.Record.create(recordType) : recordType;
5548 };
5549
5550 Roo.data.DataReader.prototype = {
5551      /**
5552      * Create an empty record
5553      * @param {Object} data (optional) - overlay some values
5554      * @return {Roo.data.Record} record created.
5555      */
5556     newRow :  function(d) {
5557         var da =  {};
5558         this.recordType.prototype.fields.each(function(c) {
5559             switch( c.type) {
5560                 case 'int' : da[c.name] = 0; break;
5561                 case 'date' : da[c.name] = new Date(); break;
5562                 case 'float' : da[c.name] = 0.0; break;
5563                 case 'boolean' : da[c.name] = false; break;
5564                 default : da[c.name] = ""; break;
5565             }
5566             
5567         });
5568         return new this.recordType(Roo.apply(da, d));
5569     }
5570     
5571 };/*
5572  * Based on:
5573  * Ext JS Library 1.1.1
5574  * Copyright(c) 2006-2007, Ext JS, LLC.
5575  *
5576  * Originally Released Under LGPL - original licence link has changed is not relivant.
5577  *
5578  * Fork - LGPL
5579  * <script type="text/javascript">
5580  */
5581
5582 /**
5583  * @class Roo.data.DataProxy
5584  * @extends Roo.data.Observable
5585  * This class is an abstract base class for implementations which provide retrieval of
5586  * unformatted data objects.<br>
5587  * <p>
5588  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5589  * (of the appropriate type which knows how to parse the data object) to provide a block of
5590  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5591  * <p>
5592  * Custom implementations must implement the load method as described in
5593  * {@link Roo.data.HttpProxy#load}.
5594  */
5595 Roo.data.DataProxy = function(){
5596     this.addEvents({
5597         /**
5598          * @event beforeload
5599          * Fires before a network request is made to retrieve a data object.
5600          * @param {Object} This DataProxy object.
5601          * @param {Object} params The params parameter to the load function.
5602          */
5603         beforeload : true,
5604         /**
5605          * @event load
5606          * Fires before the load method's callback is called.
5607          * @param {Object} This DataProxy object.
5608          * @param {Object} o The data object.
5609          * @param {Object} arg The callback argument object passed to the load function.
5610          */
5611         load : true,
5612         /**
5613          * @event loadexception
5614          * Fires if an Exception occurs during data retrieval.
5615          * @param {Object} This DataProxy object.
5616          * @param {Object} o The data object.
5617          * @param {Object} arg The callback argument object passed to the load function.
5618          * @param {Object} e The Exception.
5619          */
5620         loadexception : true
5621     });
5622     Roo.data.DataProxy.superclass.constructor.call(this);
5623 };
5624
5625 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5626
5627     /**
5628      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5629      */
5630 /*
5631  * Based on:
5632  * Ext JS Library 1.1.1
5633  * Copyright(c) 2006-2007, Ext JS, LLC.
5634  *
5635  * Originally Released Under LGPL - original licence link has changed is not relivant.
5636  *
5637  * Fork - LGPL
5638  * <script type="text/javascript">
5639  */
5640 /**
5641  * @class Roo.data.MemoryProxy
5642  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5643  * to the Reader when its load method is called.
5644  * @constructor
5645  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5646  */
5647 Roo.data.MemoryProxy = function(data){
5648     if (data.data) {
5649         data = data.data;
5650     }
5651     Roo.data.MemoryProxy.superclass.constructor.call(this);
5652     this.data = data;
5653 };
5654
5655 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5656     /**
5657      * Load data from the requested source (in this case an in-memory
5658      * data object passed to the constructor), read the data object into
5659      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5660      * process that block using the passed callback.
5661      * @param {Object} params This parameter is not used by the MemoryProxy class.
5662      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5663      * object into a block of Roo.data.Records.
5664      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5665      * The function must be passed <ul>
5666      * <li>The Record block object</li>
5667      * <li>The "arg" argument from the load function</li>
5668      * <li>A boolean success indicator</li>
5669      * </ul>
5670      * @param {Object} scope The scope in which to call the callback
5671      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5672      */
5673     load : function(params, reader, callback, scope, arg){
5674         params = params || {};
5675         var result;
5676         try {
5677             result = reader.readRecords(this.data);
5678         }catch(e){
5679             this.fireEvent("loadexception", this, arg, null, e);
5680             callback.call(scope, null, arg, false);
5681             return;
5682         }
5683         callback.call(scope, result, arg, true);
5684     },
5685     
5686     // private
5687     update : function(params, records){
5688         
5689     }
5690 });/*
5691  * Based on:
5692  * Ext JS Library 1.1.1
5693  * Copyright(c) 2006-2007, Ext JS, LLC.
5694  *
5695  * Originally Released Under LGPL - original licence link has changed is not relivant.
5696  *
5697  * Fork - LGPL
5698  * <script type="text/javascript">
5699  */
5700 /**
5701  * @class Roo.data.HttpProxy
5702  * @extends Roo.data.DataProxy
5703  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5704  * configured to reference a certain URL.<br><br>
5705  * <p>
5706  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5707  * from which the running page was served.<br><br>
5708  * <p>
5709  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5710  * <p>
5711  * Be aware that to enable the browser to parse an XML document, the server must set
5712  * the Content-Type header in the HTTP response to "text/xml".
5713  * @constructor
5714  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5715  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5716  * will be used to make the request.
5717  */
5718 Roo.data.HttpProxy = function(conn){
5719     Roo.data.HttpProxy.superclass.constructor.call(this);
5720     // is conn a conn config or a real conn?
5721     this.conn = conn;
5722     this.useAjax = !conn || !conn.events;
5723   
5724 };
5725
5726 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5727     // thse are take from connection...
5728     
5729     /**
5730      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5731      */
5732     /**
5733      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5734      * extra parameters to each request made by this object. (defaults to undefined)
5735      */
5736     /**
5737      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5738      *  to each request made by this object. (defaults to undefined)
5739      */
5740     /**
5741      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5742      */
5743     /**
5744      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5745      */
5746      /**
5747      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5748      * @type Boolean
5749      */
5750   
5751
5752     /**
5753      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5754      * @type Boolean
5755      */
5756     /**
5757      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5758      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5759      * a finer-grained basis than the DataProxy events.
5760      */
5761     getConnection : function(){
5762         return this.useAjax ? Roo.Ajax : this.conn;
5763     },
5764
5765     /**
5766      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5767      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5768      * process that block using the passed callback.
5769      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5770      * for the request to the remote server.
5771      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5772      * object into a block of Roo.data.Records.
5773      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5774      * The function must be passed <ul>
5775      * <li>The Record block object</li>
5776      * <li>The "arg" argument from the load function</li>
5777      * <li>A boolean success indicator</li>
5778      * </ul>
5779      * @param {Object} scope The scope in which to call the callback
5780      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5781      */
5782     load : function(params, reader, callback, scope, arg){
5783         if(this.fireEvent("beforeload", this, params) !== false){
5784             var  o = {
5785                 params : params || {},
5786                 request: {
5787                     callback : callback,
5788                     scope : scope,
5789                     arg : arg
5790                 },
5791                 reader: reader,
5792                 callback : this.loadResponse,
5793                 scope: this
5794             };
5795             if(this.useAjax){
5796                 Roo.applyIf(o, this.conn);
5797                 if(this.activeRequest){
5798                     Roo.Ajax.abort(this.activeRequest);
5799                 }
5800                 this.activeRequest = Roo.Ajax.request(o);
5801             }else{
5802                 this.conn.request(o);
5803             }
5804         }else{
5805             callback.call(scope||this, null, arg, false);
5806         }
5807     },
5808
5809     // private
5810     loadResponse : function(o, success, response){
5811         delete this.activeRequest;
5812         if(!success){
5813             this.fireEvent("loadexception", this, o, response);
5814             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5815             return;
5816         }
5817         var result;
5818         try {
5819             result = o.reader.read(response);
5820         }catch(e){
5821             this.fireEvent("loadexception", this, o, response, e);
5822             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5823             return;
5824         }
5825         
5826         this.fireEvent("load", this, o, o.request.arg);
5827         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5828     },
5829
5830     // private
5831     update : function(dataSet){
5832
5833     },
5834
5835     // private
5836     updateResponse : function(dataSet){
5837
5838     }
5839 });/*
5840  * Based on:
5841  * Ext JS Library 1.1.1
5842  * Copyright(c) 2006-2007, Ext JS, LLC.
5843  *
5844  * Originally Released Under LGPL - original licence link has changed is not relivant.
5845  *
5846  * Fork - LGPL
5847  * <script type="text/javascript">
5848  */
5849
5850 /**
5851  * @class Roo.data.ScriptTagProxy
5852  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5853  * other than the originating domain of the running page.<br><br>
5854  * <p>
5855  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5856  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5857  * <p>
5858  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5859  * source code that is used as the source inside a &lt;script> tag.<br><br>
5860  * <p>
5861  * In order for the browser to process the returned data, the server must wrap the data object
5862  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5863  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5864  * depending on whether the callback name was passed:
5865  * <p>
5866  * <pre><code>
5867 boolean scriptTag = false;
5868 String cb = request.getParameter("callback");
5869 if (cb != null) {
5870     scriptTag = true;
5871     response.setContentType("text/javascript");
5872 } else {
5873     response.setContentType("application/x-json");
5874 }
5875 Writer out = response.getWriter();
5876 if (scriptTag) {
5877     out.write(cb + "(");
5878 }
5879 out.print(dataBlock.toJsonString());
5880 if (scriptTag) {
5881     out.write(");");
5882 }
5883 </pre></code>
5884  *
5885  * @constructor
5886  * @param {Object} config A configuration object.
5887  */
5888 Roo.data.ScriptTagProxy = function(config){
5889     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5890     Roo.apply(this, config);
5891     this.head = document.getElementsByTagName("head")[0];
5892 };
5893
5894 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5895
5896 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5897     /**
5898      * @cfg {String} url The URL from which to request the data object.
5899      */
5900     /**
5901      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5902      */
5903     timeout : 30000,
5904     /**
5905      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5906      * the server the name of the callback function set up by the load call to process the returned data object.
5907      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5908      * javascript output which calls this named function passing the data object as its only parameter.
5909      */
5910     callbackParam : "callback",
5911     /**
5912      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5913      * name to the request.
5914      */
5915     nocache : true,
5916
5917     /**
5918      * Load data from the configured URL, read the data object into
5919      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5920      * process that block using the passed callback.
5921      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5922      * for the request to the remote server.
5923      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5924      * object into a block of Roo.data.Records.
5925      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5926      * The function must be passed <ul>
5927      * <li>The Record block object</li>
5928      * <li>The "arg" argument from the load function</li>
5929      * <li>A boolean success indicator</li>
5930      * </ul>
5931      * @param {Object} scope The scope in which to call the callback
5932      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5933      */
5934     load : function(params, reader, callback, scope, arg){
5935         if(this.fireEvent("beforeload", this, params) !== false){
5936
5937             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5938
5939             var url = this.url;
5940             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5941             if(this.nocache){
5942                 url += "&_dc=" + (new Date().getTime());
5943             }
5944             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5945             var trans = {
5946                 id : transId,
5947                 cb : "stcCallback"+transId,
5948                 scriptId : "stcScript"+transId,
5949                 params : params,
5950                 arg : arg,
5951                 url : url,
5952                 callback : callback,
5953                 scope : scope,
5954                 reader : reader
5955             };
5956             var conn = this;
5957
5958             window[trans.cb] = function(o){
5959                 conn.handleResponse(o, trans);
5960             };
5961
5962             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5963
5964             if(this.autoAbort !== false){
5965                 this.abort();
5966             }
5967
5968             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5969
5970             var script = document.createElement("script");
5971             script.setAttribute("src", url);
5972             script.setAttribute("type", "text/javascript");
5973             script.setAttribute("id", trans.scriptId);
5974             this.head.appendChild(script);
5975
5976             this.trans = trans;
5977         }else{
5978             callback.call(scope||this, null, arg, false);
5979         }
5980     },
5981
5982     // private
5983     isLoading : function(){
5984         return this.trans ? true : false;
5985     },
5986
5987     /**
5988      * Abort the current server request.
5989      */
5990     abort : function(){
5991         if(this.isLoading()){
5992             this.destroyTrans(this.trans);
5993         }
5994     },
5995
5996     // private
5997     destroyTrans : function(trans, isLoaded){
5998         this.head.removeChild(document.getElementById(trans.scriptId));
5999         clearTimeout(trans.timeoutId);
6000         if(isLoaded){
6001             window[trans.cb] = undefined;
6002             try{
6003                 delete window[trans.cb];
6004             }catch(e){}
6005         }else{
6006             // if hasn't been loaded, wait for load to remove it to prevent script error
6007             window[trans.cb] = function(){
6008                 window[trans.cb] = undefined;
6009                 try{
6010                     delete window[trans.cb];
6011                 }catch(e){}
6012             };
6013         }
6014     },
6015
6016     // private
6017     handleResponse : function(o, trans){
6018         this.trans = false;
6019         this.destroyTrans(trans, true);
6020         var result;
6021         try {
6022             result = trans.reader.readRecords(o);
6023         }catch(e){
6024             this.fireEvent("loadexception", this, o, trans.arg, e);
6025             trans.callback.call(trans.scope||window, null, trans.arg, false);
6026             return;
6027         }
6028         this.fireEvent("load", this, o, trans.arg);
6029         trans.callback.call(trans.scope||window, result, trans.arg, true);
6030     },
6031
6032     // private
6033     handleFailure : function(trans){
6034         this.trans = false;
6035         this.destroyTrans(trans, false);
6036         this.fireEvent("loadexception", this, null, trans.arg);
6037         trans.callback.call(trans.scope||window, null, trans.arg, false);
6038     }
6039 });/*
6040  * Based on:
6041  * Ext JS Library 1.1.1
6042  * Copyright(c) 2006-2007, Ext JS, LLC.
6043  *
6044  * Originally Released Under LGPL - original licence link has changed is not relivant.
6045  *
6046  * Fork - LGPL
6047  * <script type="text/javascript">
6048  */
6049
6050 /**
6051  * @class Roo.data.JsonReader
6052  * @extends Roo.data.DataReader
6053  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6054  * based on mappings in a provided Roo.data.Record constructor.
6055  * 
6056  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6057  * in the reply previously. 
6058  * 
6059  * <p>
6060  * Example code:
6061  * <pre><code>
6062 var RecordDef = Roo.data.Record.create([
6063     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6064     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6065 ]);
6066 var myReader = new Roo.data.JsonReader({
6067     totalProperty: "results",    // The property which contains the total dataset size (optional)
6068     root: "rows",                // The property which contains an Array of row objects
6069     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6070 }, RecordDef);
6071 </code></pre>
6072  * <p>
6073  * This would consume a JSON file like this:
6074  * <pre><code>
6075 { 'results': 2, 'rows': [
6076     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6077     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6078 }
6079 </code></pre>
6080  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6081  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6082  * paged from the remote server.
6083  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6084  * @cfg {String} root name of the property which contains the Array of row objects.
6085  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6086  * @constructor
6087  * Create a new JsonReader
6088  * @param {Object} meta Metadata configuration options
6089  * @param {Object} recordType Either an Array of field definition objects,
6090  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6091  */
6092 Roo.data.JsonReader = function(meta, recordType){
6093     
6094     meta = meta || {};
6095     // set some defaults:
6096     Roo.applyIf(meta, {
6097         totalProperty: 'total',
6098         successProperty : 'success',
6099         root : 'data',
6100         id : 'id'
6101     });
6102     
6103     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6104 };
6105 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6106     
6107     /**
6108      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6109      * Used by Store query builder to append _requestMeta to params.
6110      * 
6111      */
6112     metaFromRemote : false,
6113     /**
6114      * This method is only used by a DataProxy which has retrieved data from a remote server.
6115      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6116      * @return {Object} data A data block which is used by an Roo.data.Store object as
6117      * a cache of Roo.data.Records.
6118      */
6119     read : function(response){
6120         var json = response.responseText;
6121        
6122         var o = /* eval:var:o */ eval("("+json+")");
6123         if(!o) {
6124             throw {message: "JsonReader.read: Json object not found"};
6125         }
6126         
6127         if(o.metaData){
6128             
6129             delete this.ef;
6130             this.metaFromRemote = true;
6131             this.meta = o.metaData;
6132             this.recordType = Roo.data.Record.create(o.metaData.fields);
6133             this.onMetaChange(this.meta, this.recordType, o);
6134         }
6135         return this.readRecords(o);
6136     },
6137
6138     // private function a store will implement
6139     onMetaChange : function(meta, recordType, o){
6140
6141     },
6142
6143     /**
6144          * @ignore
6145          */
6146     simpleAccess: function(obj, subsc) {
6147         return obj[subsc];
6148     },
6149
6150         /**
6151          * @ignore
6152          */
6153     getJsonAccessor: function(){
6154         var re = /[\[\.]/;
6155         return function(expr) {
6156             try {
6157                 return(re.test(expr))
6158                     ? new Function("obj", "return obj." + expr)
6159                     : function(obj){
6160                         return obj[expr];
6161                     };
6162             } catch(e){}
6163             return Roo.emptyFn;
6164         };
6165     }(),
6166
6167     /**
6168      * Create a data block containing Roo.data.Records from an XML document.
6169      * @param {Object} o An object which contains an Array of row objects in the property specified
6170      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6171      * which contains the total size of the dataset.
6172      * @return {Object} data A data block which is used by an Roo.data.Store object as
6173      * a cache of Roo.data.Records.
6174      */
6175     readRecords : function(o){
6176         /**
6177          * After any data loads, the raw JSON data is available for further custom processing.
6178          * @type Object
6179          */
6180         this.jsonData = o;
6181         var s = this.meta, Record = this.recordType,
6182             f = Record.prototype.fields, fi = f.items, fl = f.length;
6183
6184 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6185         if (!this.ef) {
6186             if(s.totalProperty) {
6187                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6188                 }
6189                 if(s.successProperty) {
6190                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6191                 }
6192                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6193                 if (s.id) {
6194                         var g = this.getJsonAccessor(s.id);
6195                         this.getId = function(rec) {
6196                                 var r = g(rec);
6197                                 return (r === undefined || r === "") ? null : r;
6198                         };
6199                 } else {
6200                         this.getId = function(){return null;};
6201                 }
6202             this.ef = [];
6203             for(var jj = 0; jj < fl; jj++){
6204                 f = fi[jj];
6205                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6206                 this.ef[jj] = this.getJsonAccessor(map);
6207             }
6208         }
6209
6210         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6211         if(s.totalProperty){
6212             var vt = parseInt(this.getTotal(o), 10);
6213             if(!isNaN(vt)){
6214                 totalRecords = vt;
6215             }
6216         }
6217         if(s.successProperty){
6218             var vs = this.getSuccess(o);
6219             if(vs === false || vs === 'false'){
6220                 success = false;
6221             }
6222         }
6223         var records = [];
6224             for(var i = 0; i < c; i++){
6225                     var n = root[i];
6226                 var values = {};
6227                 var id = this.getId(n);
6228                 for(var j = 0; j < fl; j++){
6229                     f = fi[j];
6230                 var v = this.ef[j](n);
6231                 if (!f.convert) {
6232                     Roo.log('missing convert for ' + f.name);
6233                     Roo.log(f);
6234                     continue;
6235                 }
6236                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6237                 }
6238                 var record = new Record(values, id);
6239                 record.json = n;
6240                 records[i] = record;
6241             }
6242             return {
6243                 success : success,
6244                 records : records,
6245                 totalRecords : totalRecords
6246             };
6247     }
6248 });/*
6249  * Based on:
6250  * Ext JS Library 1.1.1
6251  * Copyright(c) 2006-2007, Ext JS, LLC.
6252  *
6253  * Originally Released Under LGPL - original licence link has changed is not relivant.
6254  *
6255  * Fork - LGPL
6256  * <script type="text/javascript">
6257  */
6258
6259 /**
6260  * @class Roo.data.XmlReader
6261  * @extends Roo.data.DataReader
6262  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6263  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6264  * <p>
6265  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6266  * header in the HTTP response must be set to "text/xml".</em>
6267  * <p>
6268  * Example code:
6269  * <pre><code>
6270 var RecordDef = Roo.data.Record.create([
6271    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6272    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6273 ]);
6274 var myReader = new Roo.data.XmlReader({
6275    totalRecords: "results", // The element which contains the total dataset size (optional)
6276    record: "row",           // The repeated element which contains row information
6277    id: "id"                 // The element within the row that provides an ID for the record (optional)
6278 }, RecordDef);
6279 </code></pre>
6280  * <p>
6281  * This would consume an XML file like this:
6282  * <pre><code>
6283 &lt;?xml?>
6284 &lt;dataset>
6285  &lt;results>2&lt;/results>
6286  &lt;row>
6287    &lt;id>1&lt;/id>
6288    &lt;name>Bill&lt;/name>
6289    &lt;occupation>Gardener&lt;/occupation>
6290  &lt;/row>
6291  &lt;row>
6292    &lt;id>2&lt;/id>
6293    &lt;name>Ben&lt;/name>
6294    &lt;occupation>Horticulturalist&lt;/occupation>
6295  &lt;/row>
6296 &lt;/dataset>
6297 </code></pre>
6298  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6299  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6300  * paged from the remote server.
6301  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6302  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6303  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6304  * a record identifier value.
6305  * @constructor
6306  * Create a new XmlReader
6307  * @param {Object} meta Metadata configuration options
6308  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6309  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6310  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6311  */
6312 Roo.data.XmlReader = function(meta, recordType){
6313     meta = meta || {};
6314     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6315 };
6316 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6317     /**
6318      * This method is only used by a DataProxy which has retrieved data from a remote server.
6319          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6320          * to contain a method called 'responseXML' that returns an XML document object.
6321      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6322      * a cache of Roo.data.Records.
6323      */
6324     read : function(response){
6325         var doc = response.responseXML;
6326         if(!doc) {
6327             throw {message: "XmlReader.read: XML Document not available"};
6328         }
6329         return this.readRecords(doc);
6330     },
6331
6332     /**
6333      * Create a data block containing Roo.data.Records from an XML document.
6334          * @param {Object} doc A parsed XML document.
6335      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6336      * a cache of Roo.data.Records.
6337      */
6338     readRecords : function(doc){
6339         /**
6340          * After any data loads/reads, the raw XML Document is available for further custom processing.
6341          * @type XMLDocument
6342          */
6343         this.xmlData = doc;
6344         var root = doc.documentElement || doc;
6345         var q = Roo.DomQuery;
6346         var recordType = this.recordType, fields = recordType.prototype.fields;
6347         var sid = this.meta.id;
6348         var totalRecords = 0, success = true;
6349         if(this.meta.totalRecords){
6350             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6351         }
6352         
6353         if(this.meta.success){
6354             var sv = q.selectValue(this.meta.success, root, true);
6355             success = sv !== false && sv !== 'false';
6356         }
6357         var records = [];
6358         var ns = q.select(this.meta.record, root);
6359         for(var i = 0, len = ns.length; i < len; i++) {
6360                 var n = ns[i];
6361                 var values = {};
6362                 var id = sid ? q.selectValue(sid, n) : undefined;
6363                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6364                     var f = fields.items[j];
6365                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6366                     v = f.convert(v);
6367                     values[f.name] = v;
6368                 }
6369                 var record = new recordType(values, id);
6370                 record.node = n;
6371                 records[records.length] = record;
6372             }
6373
6374             return {
6375                 success : success,
6376                 records : records,
6377                 totalRecords : totalRecords || records.length
6378             };
6379     }
6380 });/*
6381  * Based on:
6382  * Ext JS Library 1.1.1
6383  * Copyright(c) 2006-2007, Ext JS, LLC.
6384  *
6385  * Originally Released Under LGPL - original licence link has changed is not relivant.
6386  *
6387  * Fork - LGPL
6388  * <script type="text/javascript">
6389  */
6390
6391 /**
6392  * @class Roo.data.ArrayReader
6393  * @extends Roo.data.DataReader
6394  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6395  * Each element of that Array represents a row of data fields. The
6396  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6397  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6398  * <p>
6399  * Example code:.
6400  * <pre><code>
6401 var RecordDef = Roo.data.Record.create([
6402     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6403     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6404 ]);
6405 var myReader = new Roo.data.ArrayReader({
6406     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6407 }, RecordDef);
6408 </code></pre>
6409  * <p>
6410  * This would consume an Array like this:
6411  * <pre><code>
6412 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6413   </code></pre>
6414  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6415  * @constructor
6416  * Create a new JsonReader
6417  * @param {Object} meta Metadata configuration options.
6418  * @param {Object} recordType Either an Array of field definition objects
6419  * as specified to {@link Roo.data.Record#create},
6420  * or an {@link Roo.data.Record} object
6421  * created using {@link Roo.data.Record#create}.
6422  */
6423 Roo.data.ArrayReader = function(meta, recordType){
6424     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6425 };
6426
6427 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6428     /**
6429      * Create a data block containing Roo.data.Records from an XML document.
6430      * @param {Object} o An Array of row objects which represents the dataset.
6431      * @return {Object} data A data block which is used by an Roo.data.Store object as
6432      * a cache of Roo.data.Records.
6433      */
6434     readRecords : function(o){
6435         var sid = this.meta ? this.meta.id : null;
6436         var recordType = this.recordType, fields = recordType.prototype.fields;
6437         var records = [];
6438         var root = o;
6439             for(var i = 0; i < root.length; i++){
6440                     var n = root[i];
6441                 var values = {};
6442                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6443                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6444                 var f = fields.items[j];
6445                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6446                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6447                 v = f.convert(v);
6448                 values[f.name] = v;
6449             }
6450                 var record = new recordType(values, id);
6451                 record.json = n;
6452                 records[records.length] = record;
6453             }
6454             return {
6455                 records : records,
6456                 totalRecords : records.length
6457             };
6458     }
6459 });/*
6460  * Based on:
6461  * Ext JS Library 1.1.1
6462  * Copyright(c) 2006-2007, Ext JS, LLC.
6463  *
6464  * Originally Released Under LGPL - original licence link has changed is not relivant.
6465  *
6466  * Fork - LGPL
6467  * <script type="text/javascript">
6468  */
6469
6470
6471 /**
6472  * @class Roo.data.Tree
6473  * @extends Roo.util.Observable
6474  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6475  * in the tree have most standard DOM functionality.
6476  * @constructor
6477  * @param {Node} root (optional) The root node
6478  */
6479 Roo.data.Tree = function(root){
6480    this.nodeHash = {};
6481    /**
6482     * The root node for this tree
6483     * @type Node
6484     */
6485    this.root = null;
6486    if(root){
6487        this.setRootNode(root);
6488    }
6489    this.addEvents({
6490        /**
6491         * @event append
6492         * Fires when a new child node is appended to a node in this tree.
6493         * @param {Tree} tree The owner tree
6494         * @param {Node} parent The parent node
6495         * @param {Node} node The newly appended node
6496         * @param {Number} index The index of the newly appended node
6497         */
6498        "append" : true,
6499        /**
6500         * @event remove
6501         * Fires when a child node is removed from a node in this tree.
6502         * @param {Tree} tree The owner tree
6503         * @param {Node} parent The parent node
6504         * @param {Node} node The child node removed
6505         */
6506        "remove" : true,
6507        /**
6508         * @event move
6509         * Fires when a node is moved to a new location in the tree
6510         * @param {Tree} tree The owner tree
6511         * @param {Node} node The node moved
6512         * @param {Node} oldParent The old parent of this node
6513         * @param {Node} newParent The new parent of this node
6514         * @param {Number} index The index it was moved to
6515         */
6516        "move" : true,
6517        /**
6518         * @event insert
6519         * Fires when a new child node is inserted in a node in this tree.
6520         * @param {Tree} tree The owner tree
6521         * @param {Node} parent The parent node
6522         * @param {Node} node The child node inserted
6523         * @param {Node} refNode The child node the node was inserted before
6524         */
6525        "insert" : true,
6526        /**
6527         * @event beforeappend
6528         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6529         * @param {Tree} tree The owner tree
6530         * @param {Node} parent The parent node
6531         * @param {Node} node The child node to be appended
6532         */
6533        "beforeappend" : true,
6534        /**
6535         * @event beforeremove
6536         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node to be removed
6540         */
6541        "beforeremove" : true,
6542        /**
6543         * @event beforemove
6544         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node being moved
6547         * @param {Node} oldParent The parent of the node
6548         * @param {Node} newParent The new parent the node is moving to
6549         * @param {Number} index The index it is being moved to
6550         */
6551        "beforemove" : true,
6552        /**
6553         * @event beforeinsert
6554         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node to be inserted
6558         * @param {Node} refNode The child node the node is being inserted before
6559         */
6560        "beforeinsert" : true
6561    });
6562
6563     Roo.data.Tree.superclass.constructor.call(this);
6564 };
6565
6566 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6567     pathSeparator: "/",
6568
6569     proxyNodeEvent : function(){
6570         return this.fireEvent.apply(this, arguments);
6571     },
6572
6573     /**
6574      * Returns the root node for this tree.
6575      * @return {Node}
6576      */
6577     getRootNode : function(){
6578         return this.root;
6579     },
6580
6581     /**
6582      * Sets the root node for this tree.
6583      * @param {Node} node
6584      * @return {Node}
6585      */
6586     setRootNode : function(node){
6587         this.root = node;
6588         node.ownerTree = this;
6589         node.isRoot = true;
6590         this.registerNode(node);
6591         return node;
6592     },
6593
6594     /**
6595      * Gets a node in this tree by its id.
6596      * @param {String} id
6597      * @return {Node}
6598      */
6599     getNodeById : function(id){
6600         return this.nodeHash[id];
6601     },
6602
6603     registerNode : function(node){
6604         this.nodeHash[node.id] = node;
6605     },
6606
6607     unregisterNode : function(node){
6608         delete this.nodeHash[node.id];
6609     },
6610
6611     toString : function(){
6612         return "[Tree"+(this.id?" "+this.id:"")+"]";
6613     }
6614 });
6615
6616 /**
6617  * @class Roo.data.Node
6618  * @extends Roo.util.Observable
6619  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6620  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6621  * @constructor
6622  * @param {Object} attributes The attributes/config for the node
6623  */
6624 Roo.data.Node = function(attributes){
6625     /**
6626      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6627      * @type {Object}
6628      */
6629     this.attributes = attributes || {};
6630     this.leaf = this.attributes.leaf;
6631     /**
6632      * The node id. @type String
6633      */
6634     this.id = this.attributes.id;
6635     if(!this.id){
6636         this.id = Roo.id(null, "ynode-");
6637         this.attributes.id = this.id;
6638     }
6639     /**
6640      * All child nodes of this node. @type Array
6641      */
6642     this.childNodes = [];
6643     if(!this.childNodes.indexOf){ // indexOf is a must
6644         this.childNodes.indexOf = function(o){
6645             for(var i = 0, len = this.length; i < len; i++){
6646                 if(this[i] == o) {
6647                     return i;
6648                 }
6649             }
6650             return -1;
6651         };
6652     }
6653     /**
6654      * The parent node for this node. @type Node
6655      */
6656     this.parentNode = null;
6657     /**
6658      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6659      */
6660     this.firstChild = null;
6661     /**
6662      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6663      */
6664     this.lastChild = null;
6665     /**
6666      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6667      */
6668     this.previousSibling = null;
6669     /**
6670      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6671      */
6672     this.nextSibling = null;
6673
6674     this.addEvents({
6675        /**
6676         * @event append
6677         * Fires when a new child node is appended
6678         * @param {Tree} tree The owner tree
6679         * @param {Node} this This node
6680         * @param {Node} node The newly appended node
6681         * @param {Number} index The index of the newly appended node
6682         */
6683        "append" : true,
6684        /**
6685         * @event remove
6686         * Fires when a child node is removed
6687         * @param {Tree} tree The owner tree
6688         * @param {Node} this This node
6689         * @param {Node} node The removed node
6690         */
6691        "remove" : true,
6692        /**
6693         * @event move
6694         * Fires when this node is moved to a new location in the tree
6695         * @param {Tree} tree The owner tree
6696         * @param {Node} this This node
6697         * @param {Node} oldParent The old parent of this node
6698         * @param {Node} newParent The new parent of this node
6699         * @param {Number} index The index it was moved to
6700         */
6701        "move" : true,
6702        /**
6703         * @event insert
6704         * Fires when a new child node is inserted.
6705         * @param {Tree} tree The owner tree
6706         * @param {Node} this This node
6707         * @param {Node} node The child node inserted
6708         * @param {Node} refNode The child node the node was inserted before
6709         */
6710        "insert" : true,
6711        /**
6712         * @event beforeappend
6713         * Fires before a new child is appended, return false to cancel the append.
6714         * @param {Tree} tree The owner tree
6715         * @param {Node} this This node
6716         * @param {Node} node The child node to be appended
6717         */
6718        "beforeappend" : true,
6719        /**
6720         * @event beforeremove
6721         * Fires before a child is removed, return false to cancel the remove.
6722         * @param {Tree} tree The owner tree
6723         * @param {Node} this This node
6724         * @param {Node} node The child node to be removed
6725         */
6726        "beforeremove" : true,
6727        /**
6728         * @event beforemove
6729         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} oldParent The parent of this node
6733         * @param {Node} newParent The new parent this node is moving to
6734         * @param {Number} index The index it is being moved to
6735         */
6736        "beforemove" : true,
6737        /**
6738         * @event beforeinsert
6739         * Fires before a new child is inserted, return false to cancel the insert.
6740         * @param {Tree} tree The owner tree
6741         * @param {Node} this This node
6742         * @param {Node} node The child node to be inserted
6743         * @param {Node} refNode The child node the node is being inserted before
6744         */
6745        "beforeinsert" : true
6746    });
6747     this.listeners = this.attributes.listeners;
6748     Roo.data.Node.superclass.constructor.call(this);
6749 };
6750
6751 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6752     fireEvent : function(evtName){
6753         // first do standard event for this node
6754         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6755             return false;
6756         }
6757         // then bubble it up to the tree if the event wasn't cancelled
6758         var ot = this.getOwnerTree();
6759         if(ot){
6760             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6761                 return false;
6762             }
6763         }
6764         return true;
6765     },
6766
6767     /**
6768      * Returns true if this node is a leaf
6769      * @return {Boolean}
6770      */
6771     isLeaf : function(){
6772         return this.leaf === true;
6773     },
6774
6775     // private
6776     setFirstChild : function(node){
6777         this.firstChild = node;
6778     },
6779
6780     //private
6781     setLastChild : function(node){
6782         this.lastChild = node;
6783     },
6784
6785
6786     /**
6787      * Returns true if this node is the last child of its parent
6788      * @return {Boolean}
6789      */
6790     isLast : function(){
6791        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6792     },
6793
6794     /**
6795      * Returns true if this node is the first child of its parent
6796      * @return {Boolean}
6797      */
6798     isFirst : function(){
6799        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6800     },
6801
6802     hasChildNodes : function(){
6803         return !this.isLeaf() && this.childNodes.length > 0;
6804     },
6805
6806     /**
6807      * Insert node(s) as the last child node of this node.
6808      * @param {Node/Array} node The node or Array of nodes to append
6809      * @return {Node} The appended node if single append, or null if an array was passed
6810      */
6811     appendChild : function(node){
6812         var multi = false;
6813         if(node instanceof Array){
6814             multi = node;
6815         }else if(arguments.length > 1){
6816             multi = arguments;
6817         }
6818         // if passed an array or multiple args do them one by one
6819         if(multi){
6820             for(var i = 0, len = multi.length; i < len; i++) {
6821                 this.appendChild(multi[i]);
6822             }
6823         }else{
6824             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6825                 return false;
6826             }
6827             var index = this.childNodes.length;
6828             var oldParent = node.parentNode;
6829             // it's a move, make sure we move it cleanly
6830             if(oldParent){
6831                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6832                     return false;
6833                 }
6834                 oldParent.removeChild(node);
6835             }
6836             index = this.childNodes.length;
6837             if(index == 0){
6838                 this.setFirstChild(node);
6839             }
6840             this.childNodes.push(node);
6841             node.parentNode = this;
6842             var ps = this.childNodes[index-1];
6843             if(ps){
6844                 node.previousSibling = ps;
6845                 ps.nextSibling = node;
6846             }else{
6847                 node.previousSibling = null;
6848             }
6849             node.nextSibling = null;
6850             this.setLastChild(node);
6851             node.setOwnerTree(this.getOwnerTree());
6852             this.fireEvent("append", this.ownerTree, this, node, index);
6853             if(oldParent){
6854                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6855             }
6856             return node;
6857         }
6858     },
6859
6860     /**
6861      * Removes a child node from this node.
6862      * @param {Node} node The node to remove
6863      * @return {Node} The removed node
6864      */
6865     removeChild : function(node){
6866         var index = this.childNodes.indexOf(node);
6867         if(index == -1){
6868             return false;
6869         }
6870         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6871             return false;
6872         }
6873
6874         // remove it from childNodes collection
6875         this.childNodes.splice(index, 1);
6876
6877         // update siblings
6878         if(node.previousSibling){
6879             node.previousSibling.nextSibling = node.nextSibling;
6880         }
6881         if(node.nextSibling){
6882             node.nextSibling.previousSibling = node.previousSibling;
6883         }
6884
6885         // update child refs
6886         if(this.firstChild == node){
6887             this.setFirstChild(node.nextSibling);
6888         }
6889         if(this.lastChild == node){
6890             this.setLastChild(node.previousSibling);
6891         }
6892
6893         node.setOwnerTree(null);
6894         // clear any references from the node
6895         node.parentNode = null;
6896         node.previousSibling = null;
6897         node.nextSibling = null;
6898         this.fireEvent("remove", this.ownerTree, this, node);
6899         return node;
6900     },
6901
6902     /**
6903      * Inserts the first node before the second node in this nodes childNodes collection.
6904      * @param {Node} node The node to insert
6905      * @param {Node} refNode The node to insert before (if null the node is appended)
6906      * @return {Node} The inserted node
6907      */
6908     insertBefore : function(node, refNode){
6909         if(!refNode){ // like standard Dom, refNode can be null for append
6910             return this.appendChild(node);
6911         }
6912         // nothing to do
6913         if(node == refNode){
6914             return false;
6915         }
6916
6917         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6918             return false;
6919         }
6920         var index = this.childNodes.indexOf(refNode);
6921         var oldParent = node.parentNode;
6922         var refIndex = index;
6923
6924         // when moving internally, indexes will change after remove
6925         if(oldParent == this && this.childNodes.indexOf(node) < index){
6926             refIndex--;
6927         }
6928
6929         // it's a move, make sure we move it cleanly
6930         if(oldParent){
6931             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6932                 return false;
6933             }
6934             oldParent.removeChild(node);
6935         }
6936         if(refIndex == 0){
6937             this.setFirstChild(node);
6938         }
6939         this.childNodes.splice(refIndex, 0, node);
6940         node.parentNode = this;
6941         var ps = this.childNodes[refIndex-1];
6942         if(ps){
6943             node.previousSibling = ps;
6944             ps.nextSibling = node;
6945         }else{
6946             node.previousSibling = null;
6947         }
6948         node.nextSibling = refNode;
6949         refNode.previousSibling = node;
6950         node.setOwnerTree(this.getOwnerTree());
6951         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6952         if(oldParent){
6953             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6954         }
6955         return node;
6956     },
6957
6958     /**
6959      * Returns the child node at the specified index.
6960      * @param {Number} index
6961      * @return {Node}
6962      */
6963     item : function(index){
6964         return this.childNodes[index];
6965     },
6966
6967     /**
6968      * Replaces one child node in this node with another.
6969      * @param {Node} newChild The replacement node
6970      * @param {Node} oldChild The node to replace
6971      * @return {Node} The replaced node
6972      */
6973     replaceChild : function(newChild, oldChild){
6974         this.insertBefore(newChild, oldChild);
6975         this.removeChild(oldChild);
6976         return oldChild;
6977     },
6978
6979     /**
6980      * Returns the index of a child node
6981      * @param {Node} node
6982      * @return {Number} The index of the node or -1 if it was not found
6983      */
6984     indexOf : function(child){
6985         return this.childNodes.indexOf(child);
6986     },
6987
6988     /**
6989      * Returns the tree this node is in.
6990      * @return {Tree}
6991      */
6992     getOwnerTree : function(){
6993         // if it doesn't have one, look for one
6994         if(!this.ownerTree){
6995             var p = this;
6996             while(p){
6997                 if(p.ownerTree){
6998                     this.ownerTree = p.ownerTree;
6999                     break;
7000                 }
7001                 p = p.parentNode;
7002             }
7003         }
7004         return this.ownerTree;
7005     },
7006
7007     /**
7008      * Returns depth of this node (the root node has a depth of 0)
7009      * @return {Number}
7010      */
7011     getDepth : function(){
7012         var depth = 0;
7013         var p = this;
7014         while(p.parentNode){
7015             ++depth;
7016             p = p.parentNode;
7017         }
7018         return depth;
7019     },
7020
7021     // private
7022     setOwnerTree : function(tree){
7023         // if it's move, we need to update everyone
7024         if(tree != this.ownerTree){
7025             if(this.ownerTree){
7026                 this.ownerTree.unregisterNode(this);
7027             }
7028             this.ownerTree = tree;
7029             var cs = this.childNodes;
7030             for(var i = 0, len = cs.length; i < len; i++) {
7031                 cs[i].setOwnerTree(tree);
7032             }
7033             if(tree){
7034                 tree.registerNode(this);
7035             }
7036         }
7037     },
7038
7039     /**
7040      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7041      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7042      * @return {String} The path
7043      */
7044     getPath : function(attr){
7045         attr = attr || "id";
7046         var p = this.parentNode;
7047         var b = [this.attributes[attr]];
7048         while(p){
7049             b.unshift(p.attributes[attr]);
7050             p = p.parentNode;
7051         }
7052         var sep = this.getOwnerTree().pathSeparator;
7053         return sep + b.join(sep);
7054     },
7055
7056     /**
7057      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7058      * function call will be the scope provided or the current node. The arguments to the function
7059      * will be the args provided or the current node. If the function returns false at any point,
7060      * the bubble is stopped.
7061      * @param {Function} fn The function to call
7062      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7063      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7064      */
7065     bubble : function(fn, scope, args){
7066         var p = this;
7067         while(p){
7068             if(fn.call(scope || p, args || p) === false){
7069                 break;
7070             }
7071             p = p.parentNode;
7072         }
7073     },
7074
7075     /**
7076      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7077      * function call will be the scope provided or the current node. The arguments to the function
7078      * will be the args provided or the current node. If the function returns false at any point,
7079      * the cascade is stopped on that branch.
7080      * @param {Function} fn The function to call
7081      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7082      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7083      */
7084     cascade : function(fn, scope, args){
7085         if(fn.call(scope || this, args || this) !== false){
7086             var cs = this.childNodes;
7087             for(var i = 0, len = cs.length; i < len; i++) {
7088                 cs[i].cascade(fn, scope, args);
7089             }
7090         }
7091     },
7092
7093     /**
7094      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7095      * function call will be the scope provided or the current node. The arguments to the function
7096      * will be the args provided or the current node. If the function returns false at any point,
7097      * the iteration stops.
7098      * @param {Function} fn The function to call
7099      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7100      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7101      */
7102     eachChild : function(fn, scope, args){
7103         var cs = this.childNodes;
7104         for(var i = 0, len = cs.length; i < len; i++) {
7105                 if(fn.call(scope || this, args || cs[i]) === false){
7106                     break;
7107                 }
7108         }
7109     },
7110
7111     /**
7112      * Finds the first child that has the attribute with the specified value.
7113      * @param {String} attribute The attribute name
7114      * @param {Mixed} value The value to search for
7115      * @return {Node} The found child or null if none was found
7116      */
7117     findChild : function(attribute, value){
7118         var cs = this.childNodes;
7119         for(var i = 0, len = cs.length; i < len; i++) {
7120                 if(cs[i].attributes[attribute] == value){
7121                     return cs[i];
7122                 }
7123         }
7124         return null;
7125     },
7126
7127     /**
7128      * Finds the first child by a custom function. The child matches if the function passed
7129      * returns true.
7130      * @param {Function} fn
7131      * @param {Object} scope (optional)
7132      * @return {Node} The found child or null if none was found
7133      */
7134     findChildBy : function(fn, scope){
7135         var cs = this.childNodes;
7136         for(var i = 0, len = cs.length; i < len; i++) {
7137                 if(fn.call(scope||cs[i], cs[i]) === true){
7138                     return cs[i];
7139                 }
7140         }
7141         return null;
7142     },
7143
7144     /**
7145      * Sorts this nodes children using the supplied sort function
7146      * @param {Function} fn
7147      * @param {Object} scope (optional)
7148      */
7149     sort : function(fn, scope){
7150         var cs = this.childNodes;
7151         var len = cs.length;
7152         if(len > 0){
7153             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7154             cs.sort(sortFn);
7155             for(var i = 0; i < len; i++){
7156                 var n = cs[i];
7157                 n.previousSibling = cs[i-1];
7158                 n.nextSibling = cs[i+1];
7159                 if(i == 0){
7160                     this.setFirstChild(n);
7161                 }
7162                 if(i == len-1){
7163                     this.setLastChild(n);
7164                 }
7165             }
7166         }
7167     },
7168
7169     /**
7170      * Returns true if this node is an ancestor (at any point) of the passed node.
7171      * @param {Node} node
7172      * @return {Boolean}
7173      */
7174     contains : function(node){
7175         return node.isAncestor(this);
7176     },
7177
7178     /**
7179      * Returns true if the passed node is an ancestor (at any point) of this node.
7180      * @param {Node} node
7181      * @return {Boolean}
7182      */
7183     isAncestor : function(node){
7184         var p = this.parentNode;
7185         while(p){
7186             if(p == node){
7187                 return true;
7188             }
7189             p = p.parentNode;
7190         }
7191         return false;
7192     },
7193
7194     toString : function(){
7195         return "[Node"+(this.id?" "+this.id:"")+"]";
7196     }
7197 });/*
7198  * Based on:
7199  * Ext JS Library 1.1.1
7200  * Copyright(c) 2006-2007, Ext JS, LLC.
7201  *
7202  * Originally Released Under LGPL - original licence link has changed is not relivant.
7203  *
7204  * Fork - LGPL
7205  * <script type="text/javascript">
7206  */
7207  
7208
7209 /**
7210  * @class Roo.ComponentMgr
7211  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7212  * @singleton
7213  */
7214 Roo.ComponentMgr = function(){
7215     var all = new Roo.util.MixedCollection();
7216
7217     return {
7218         /**
7219          * Registers a component.
7220          * @param {Roo.Component} c The component
7221          */
7222         register : function(c){
7223             all.add(c);
7224         },
7225
7226         /**
7227          * Unregisters a component.
7228          * @param {Roo.Component} c The component
7229          */
7230         unregister : function(c){
7231             all.remove(c);
7232         },
7233
7234         /**
7235          * Returns a component by id
7236          * @param {String} id The component id
7237          */
7238         get : function(id){
7239             return all.get(id);
7240         },
7241
7242         /**
7243          * Registers a function that will be called when a specified component is added to ComponentMgr
7244          * @param {String} id The component id
7245          * @param {Funtction} fn The callback function
7246          * @param {Object} scope The scope of the callback
7247          */
7248         onAvailable : function(id, fn, scope){
7249             all.on("add", function(index, o){
7250                 if(o.id == id){
7251                     fn.call(scope || o, o);
7252                     all.un("add", fn, scope);
7253                 }
7254             });
7255         }
7256     };
7257 }();/*
7258  * Based on:
7259  * Ext JS Library 1.1.1
7260  * Copyright(c) 2006-2007, Ext JS, LLC.
7261  *
7262  * Originally Released Under LGPL - original licence link has changed is not relivant.
7263  *
7264  * Fork - LGPL
7265  * <script type="text/javascript">
7266  */
7267  
7268 /**
7269  * @class Roo.Component
7270  * @extends Roo.util.Observable
7271  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7272  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7273  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7274  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7275  * All visual components (widgets) that require rendering into a layout should subclass Component.
7276  * @constructor
7277  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7278  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7279  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7280  */
7281 Roo.Component = function(config){
7282     config = config || {};
7283     if(config.tagName || config.dom || typeof config == "string"){ // element object
7284         config = {el: config, id: config.id || config};
7285     }
7286     this.initialConfig = config;
7287
7288     Roo.apply(this, config);
7289     this.addEvents({
7290         /**
7291          * @event disable
7292          * Fires after the component is disabled.
7293              * @param {Roo.Component} this
7294              */
7295         disable : true,
7296         /**
7297          * @event enable
7298          * Fires after the component is enabled.
7299              * @param {Roo.Component} this
7300              */
7301         enable : true,
7302         /**
7303          * @event beforeshow
7304          * Fires before the component is shown.  Return false to stop the show.
7305              * @param {Roo.Component} this
7306              */
7307         beforeshow : true,
7308         /**
7309          * @event show
7310          * Fires after the component is shown.
7311              * @param {Roo.Component} this
7312              */
7313         show : true,
7314         /**
7315          * @event beforehide
7316          * Fires before the component is hidden. Return false to stop the hide.
7317              * @param {Roo.Component} this
7318              */
7319         beforehide : true,
7320         /**
7321          * @event hide
7322          * Fires after the component is hidden.
7323              * @param {Roo.Component} this
7324              */
7325         hide : true,
7326         /**
7327          * @event beforerender
7328          * Fires before the component is rendered. Return false to stop the render.
7329              * @param {Roo.Component} this
7330              */
7331         beforerender : true,
7332         /**
7333          * @event render
7334          * Fires after the component is rendered.
7335              * @param {Roo.Component} this
7336              */
7337         render : true,
7338         /**
7339          * @event beforedestroy
7340          * Fires before the component is destroyed. Return false to stop the destroy.
7341              * @param {Roo.Component} this
7342              */
7343         beforedestroy : true,
7344         /**
7345          * @event destroy
7346          * Fires after the component is destroyed.
7347              * @param {Roo.Component} this
7348              */
7349         destroy : true
7350     });
7351     if(!this.id){
7352         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7353     }
7354     Roo.ComponentMgr.register(this);
7355     Roo.Component.superclass.constructor.call(this);
7356     this.initComponent();
7357     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7358         this.render(this.renderTo);
7359         delete this.renderTo;
7360     }
7361 };
7362
7363 // private
7364 Roo.Component.AUTO_ID = 1000;
7365
7366 Roo.extend(Roo.Component, Roo.util.Observable, {
7367     /**
7368      * @property {Boolean} hidden
7369      * true if this component is hidden. Read-only.
7370      */
7371     hidden : false,
7372     /**
7373      * true if this component is disabled. Read-only.
7374      */
7375     disabled : false,
7376     /**
7377      * true if this component has been rendered. Read-only.
7378      */
7379     rendered : false,
7380     
7381     /** @cfg {String} disableClass
7382      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7383      */
7384     disabledClass : "x-item-disabled",
7385         /** @cfg {Boolean} allowDomMove
7386          * Whether the component can move the Dom node when rendering (defaults to true).
7387          */
7388     allowDomMove : true,
7389     /** @cfg {String} hideMode
7390      * How this component should hidden. Supported values are
7391      * "visibility" (css visibility), "offsets" (negative offset position) and
7392      * "display" (css display) - defaults to "display".
7393      */
7394     hideMode: 'display',
7395
7396     // private
7397     ctype : "Roo.Component",
7398
7399     /** @cfg {String} actionMode 
7400      * which property holds the element that used for  hide() / show() / disable() / enable()
7401      * default is 'el' 
7402      */
7403     actionMode : "el",
7404
7405     // private
7406     getActionEl : function(){
7407         return this[this.actionMode];
7408     },
7409
7410     initComponent : Roo.emptyFn,
7411     /**
7412      * If this is a lazy rendering component, render it to its container element.
7413      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7414      */
7415     render : function(container, position){
7416         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7417             if(!container && this.el){
7418                 this.el = Roo.get(this.el);
7419                 container = this.el.dom.parentNode;
7420                 this.allowDomMove = false;
7421             }
7422             this.container = Roo.get(container);
7423             this.rendered = true;
7424             if(position !== undefined){
7425                 if(typeof position == 'number'){
7426                     position = this.container.dom.childNodes[position];
7427                 }else{
7428                     position = Roo.getDom(position);
7429                 }
7430             }
7431             this.onRender(this.container, position || null);
7432             if(this.cls){
7433                 this.el.addClass(this.cls);
7434                 delete this.cls;
7435             }
7436             if(this.style){
7437                 this.el.applyStyles(this.style);
7438                 delete this.style;
7439             }
7440             this.fireEvent("render", this);
7441             this.afterRender(this.container);
7442             if(this.hidden){
7443                 this.hide();
7444             }
7445             if(this.disabled){
7446                 this.disable();
7447             }
7448         }
7449         return this;
7450     },
7451
7452     // private
7453     // default function is not really useful
7454     onRender : function(ct, position){
7455         if(this.el){
7456             this.el = Roo.get(this.el);
7457             if(this.allowDomMove !== false){
7458                 ct.dom.insertBefore(this.el.dom, position);
7459             }
7460         }
7461     },
7462
7463     // private
7464     getAutoCreate : function(){
7465         var cfg = typeof this.autoCreate == "object" ?
7466                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7467         if(this.id && !cfg.id){
7468             cfg.id = this.id;
7469         }
7470         return cfg;
7471     },
7472
7473     // private
7474     afterRender : Roo.emptyFn,
7475
7476     /**
7477      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7478      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7479      */
7480     destroy : function(){
7481         if(this.fireEvent("beforedestroy", this) !== false){
7482             this.purgeListeners();
7483             this.beforeDestroy();
7484             if(this.rendered){
7485                 this.el.removeAllListeners();
7486                 this.el.remove();
7487                 if(this.actionMode == "container"){
7488                     this.container.remove();
7489                 }
7490             }
7491             this.onDestroy();
7492             Roo.ComponentMgr.unregister(this);
7493             this.fireEvent("destroy", this);
7494         }
7495     },
7496
7497         // private
7498     beforeDestroy : function(){
7499
7500     },
7501
7502         // private
7503         onDestroy : function(){
7504
7505     },
7506
7507     /**
7508      * Returns the underlying {@link Roo.Element}.
7509      * @return {Roo.Element} The element
7510      */
7511     getEl : function(){
7512         return this.el;
7513     },
7514
7515     /**
7516      * Returns the id of this component.
7517      * @return {String}
7518      */
7519     getId : function(){
7520         return this.id;
7521     },
7522
7523     /**
7524      * Try to focus this component.
7525      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7526      * @return {Roo.Component} this
7527      */
7528     focus : function(selectText){
7529         if(this.rendered){
7530             this.el.focus();
7531             if(selectText === true){
7532                 this.el.dom.select();
7533             }
7534         }
7535         return this;
7536     },
7537
7538     // private
7539     blur : function(){
7540         if(this.rendered){
7541             this.el.blur();
7542         }
7543         return this;
7544     },
7545
7546     /**
7547      * Disable this component.
7548      * @return {Roo.Component} this
7549      */
7550     disable : function(){
7551         if(this.rendered){
7552             this.onDisable();
7553         }
7554         this.disabled = true;
7555         this.fireEvent("disable", this);
7556         return this;
7557     },
7558
7559         // private
7560     onDisable : function(){
7561         this.getActionEl().addClass(this.disabledClass);
7562         this.el.dom.disabled = true;
7563     },
7564
7565     /**
7566      * Enable this component.
7567      * @return {Roo.Component} this
7568      */
7569     enable : function(){
7570         if(this.rendered){
7571             this.onEnable();
7572         }
7573         this.disabled = false;
7574         this.fireEvent("enable", this);
7575         return this;
7576     },
7577
7578         // private
7579     onEnable : function(){
7580         this.getActionEl().removeClass(this.disabledClass);
7581         this.el.dom.disabled = false;
7582     },
7583
7584     /**
7585      * Convenience function for setting disabled/enabled by boolean.
7586      * @param {Boolean} disabled
7587      */
7588     setDisabled : function(disabled){
7589         this[disabled ? "disable" : "enable"]();
7590     },
7591
7592     /**
7593      * Show this component.
7594      * @return {Roo.Component} this
7595      */
7596     show: function(){
7597         if(this.fireEvent("beforeshow", this) !== false){
7598             this.hidden = false;
7599             if(this.rendered){
7600                 this.onShow();
7601             }
7602             this.fireEvent("show", this);
7603         }
7604         return this;
7605     },
7606
7607     // private
7608     onShow : function(){
7609         var ae = this.getActionEl();
7610         if(this.hideMode == 'visibility'){
7611             ae.dom.style.visibility = "visible";
7612         }else if(this.hideMode == 'offsets'){
7613             ae.removeClass('x-hidden');
7614         }else{
7615             ae.dom.style.display = "";
7616         }
7617     },
7618
7619     /**
7620      * Hide this component.
7621      * @return {Roo.Component} this
7622      */
7623     hide: function(){
7624         if(this.fireEvent("beforehide", this) !== false){
7625             this.hidden = true;
7626             if(this.rendered){
7627                 this.onHide();
7628             }
7629             this.fireEvent("hide", this);
7630         }
7631         return this;
7632     },
7633
7634     // private
7635     onHide : function(){
7636         var ae = this.getActionEl();
7637         if(this.hideMode == 'visibility'){
7638             ae.dom.style.visibility = "hidden";
7639         }else if(this.hideMode == 'offsets'){
7640             ae.addClass('x-hidden');
7641         }else{
7642             ae.dom.style.display = "none";
7643         }
7644     },
7645
7646     /**
7647      * Convenience function to hide or show this component by boolean.
7648      * @param {Boolean} visible True to show, false to hide
7649      * @return {Roo.Component} this
7650      */
7651     setVisible: function(visible){
7652         if(visible) {
7653             this.show();
7654         }else{
7655             this.hide();
7656         }
7657         return this;
7658     },
7659
7660     /**
7661      * Returns true if this component is visible.
7662      */
7663     isVisible : function(){
7664         return this.getActionEl().isVisible();
7665     },
7666
7667     cloneConfig : function(overrides){
7668         overrides = overrides || {};
7669         var id = overrides.id || Roo.id();
7670         var cfg = Roo.applyIf(overrides, this.initialConfig);
7671         cfg.id = id; // prevent dup id
7672         return new this.constructor(cfg);
7673     }
7674 });/*
7675  * Based on:
7676  * Ext JS Library 1.1.1
7677  * Copyright(c) 2006-2007, Ext JS, LLC.
7678  *
7679  * Originally Released Under LGPL - original licence link has changed is not relivant.
7680  *
7681  * Fork - LGPL
7682  * <script type="text/javascript">
7683  */
7684  (function(){ 
7685 /**
7686  * @class Roo.Layer
7687  * @extends Roo.Element
7688  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7689  * automatic maintaining of shadow/shim positions.
7690  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7691  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7692  * you can pass a string with a CSS class name. False turns off the shadow.
7693  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7694  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7695  * @cfg {String} cls CSS class to add to the element
7696  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7697  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7698  * @constructor
7699  * @param {Object} config An object with config options.
7700  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7701  */
7702
7703 Roo.Layer = function(config, existingEl){
7704     config = config || {};
7705     var dh = Roo.DomHelper;
7706     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7707     if(existingEl){
7708         this.dom = Roo.getDom(existingEl);
7709     }
7710     if(!this.dom){
7711         var o = config.dh || {tag: "div", cls: "x-layer"};
7712         this.dom = dh.append(pel, o);
7713     }
7714     if(config.cls){
7715         this.addClass(config.cls);
7716     }
7717     this.constrain = config.constrain !== false;
7718     this.visibilityMode = Roo.Element.VISIBILITY;
7719     if(config.id){
7720         this.id = this.dom.id = config.id;
7721     }else{
7722         this.id = Roo.id(this.dom);
7723     }
7724     this.zindex = config.zindex || this.getZIndex();
7725     this.position("absolute", this.zindex);
7726     if(config.shadow){
7727         this.shadowOffset = config.shadowOffset || 4;
7728         this.shadow = new Roo.Shadow({
7729             offset : this.shadowOffset,
7730             mode : config.shadow
7731         });
7732     }else{
7733         this.shadowOffset = 0;
7734     }
7735     this.useShim = config.shim !== false && Roo.useShims;
7736     this.useDisplay = config.useDisplay;
7737     this.hide();
7738 };
7739
7740 var supr = Roo.Element.prototype;
7741
7742 // shims are shared among layer to keep from having 100 iframes
7743 var shims = [];
7744
7745 Roo.extend(Roo.Layer, Roo.Element, {
7746
7747     getZIndex : function(){
7748         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7749     },
7750
7751     getShim : function(){
7752         if(!this.useShim){
7753             return null;
7754         }
7755         if(this.shim){
7756             return this.shim;
7757         }
7758         var shim = shims.shift();
7759         if(!shim){
7760             shim = this.createShim();
7761             shim.enableDisplayMode('block');
7762             shim.dom.style.display = 'none';
7763             shim.dom.style.visibility = 'visible';
7764         }
7765         var pn = this.dom.parentNode;
7766         if(shim.dom.parentNode != pn){
7767             pn.insertBefore(shim.dom, this.dom);
7768         }
7769         shim.setStyle('z-index', this.getZIndex()-2);
7770         this.shim = shim;
7771         return shim;
7772     },
7773
7774     hideShim : function(){
7775         if(this.shim){
7776             this.shim.setDisplayed(false);
7777             shims.push(this.shim);
7778             delete this.shim;
7779         }
7780     },
7781
7782     disableShadow : function(){
7783         if(this.shadow){
7784             this.shadowDisabled = true;
7785             this.shadow.hide();
7786             this.lastShadowOffset = this.shadowOffset;
7787             this.shadowOffset = 0;
7788         }
7789     },
7790
7791     enableShadow : function(show){
7792         if(this.shadow){
7793             this.shadowDisabled = false;
7794             this.shadowOffset = this.lastShadowOffset;
7795             delete this.lastShadowOffset;
7796             if(show){
7797                 this.sync(true);
7798             }
7799         }
7800     },
7801
7802     // private
7803     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7804     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7805     sync : function(doShow){
7806         var sw = this.shadow;
7807         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7808             var sh = this.getShim();
7809
7810             var w = this.getWidth(),
7811                 h = this.getHeight();
7812
7813             var l = this.getLeft(true),
7814                 t = this.getTop(true);
7815
7816             if(sw && !this.shadowDisabled){
7817                 if(doShow && !sw.isVisible()){
7818                     sw.show(this);
7819                 }else{
7820                     sw.realign(l, t, w, h);
7821                 }
7822                 if(sh){
7823                     if(doShow){
7824                        sh.show();
7825                     }
7826                     // fit the shim behind the shadow, so it is shimmed too
7827                     var a = sw.adjusts, s = sh.dom.style;
7828                     s.left = (Math.min(l, l+a.l))+"px";
7829                     s.top = (Math.min(t, t+a.t))+"px";
7830                     s.width = (w+a.w)+"px";
7831                     s.height = (h+a.h)+"px";
7832                 }
7833             }else if(sh){
7834                 if(doShow){
7835                    sh.show();
7836                 }
7837                 sh.setSize(w, h);
7838                 sh.setLeftTop(l, t);
7839             }
7840             
7841         }
7842     },
7843
7844     // private
7845     destroy : function(){
7846         this.hideShim();
7847         if(this.shadow){
7848             this.shadow.hide();
7849         }
7850         this.removeAllListeners();
7851         var pn = this.dom.parentNode;
7852         if(pn){
7853             pn.removeChild(this.dom);
7854         }
7855         Roo.Element.uncache(this.id);
7856     },
7857
7858     remove : function(){
7859         this.destroy();
7860     },
7861
7862     // private
7863     beginUpdate : function(){
7864         this.updating = true;
7865     },
7866
7867     // private
7868     endUpdate : function(){
7869         this.updating = false;
7870         this.sync(true);
7871     },
7872
7873     // private
7874     hideUnders : function(negOffset){
7875         if(this.shadow){
7876             this.shadow.hide();
7877         }
7878         this.hideShim();
7879     },
7880
7881     // private
7882     constrainXY : function(){
7883         if(this.constrain){
7884             var vw = Roo.lib.Dom.getViewWidth(),
7885                 vh = Roo.lib.Dom.getViewHeight();
7886             var s = Roo.get(document).getScroll();
7887
7888             var xy = this.getXY();
7889             var x = xy[0], y = xy[1];   
7890             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7891             // only move it if it needs it
7892             var moved = false;
7893             // first validate right/bottom
7894             if((x + w) > vw+s.left){
7895                 x = vw - w - this.shadowOffset;
7896                 moved = true;
7897             }
7898             if((y + h) > vh+s.top){
7899                 y = vh - h - this.shadowOffset;
7900                 moved = true;
7901             }
7902             // then make sure top/left isn't negative
7903             if(x < s.left){
7904                 x = s.left;
7905                 moved = true;
7906             }
7907             if(y < s.top){
7908                 y = s.top;
7909                 moved = true;
7910             }
7911             if(moved){
7912                 if(this.avoidY){
7913                     var ay = this.avoidY;
7914                     if(y <= ay && (y+h) >= ay){
7915                         y = ay-h-5;   
7916                     }
7917                 }
7918                 xy = [x, y];
7919                 this.storeXY(xy);
7920                 supr.setXY.call(this, xy);
7921                 this.sync();
7922             }
7923         }
7924     },
7925
7926     isVisible : function(){
7927         return this.visible;    
7928     },
7929
7930     // private
7931     showAction : function(){
7932         this.visible = true; // track visibility to prevent getStyle calls
7933         if(this.useDisplay === true){
7934             this.setDisplayed("");
7935         }else if(this.lastXY){
7936             supr.setXY.call(this, this.lastXY);
7937         }else if(this.lastLT){
7938             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7939         }
7940     },
7941
7942     // private
7943     hideAction : function(){
7944         this.visible = false;
7945         if(this.useDisplay === true){
7946             this.setDisplayed(false);
7947         }else{
7948             this.setLeftTop(-10000,-10000);
7949         }
7950     },
7951
7952     // overridden Element method
7953     setVisible : function(v, a, d, c, e){
7954         if(v){
7955             this.showAction();
7956         }
7957         if(a && v){
7958             var cb = function(){
7959                 this.sync(true);
7960                 if(c){
7961                     c();
7962                 }
7963             }.createDelegate(this);
7964             supr.setVisible.call(this, true, true, d, cb, e);
7965         }else{
7966             if(!v){
7967                 this.hideUnders(true);
7968             }
7969             var cb = c;
7970             if(a){
7971                 cb = function(){
7972                     this.hideAction();
7973                     if(c){
7974                         c();
7975                     }
7976                 }.createDelegate(this);
7977             }
7978             supr.setVisible.call(this, v, a, d, cb, e);
7979             if(v){
7980                 this.sync(true);
7981             }else if(!a){
7982                 this.hideAction();
7983             }
7984         }
7985     },
7986
7987     storeXY : function(xy){
7988         delete this.lastLT;
7989         this.lastXY = xy;
7990     },
7991
7992     storeLeftTop : function(left, top){
7993         delete this.lastXY;
7994         this.lastLT = [left, top];
7995     },
7996
7997     // private
7998     beforeFx : function(){
7999         this.beforeAction();
8000         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8001     },
8002
8003     // private
8004     afterFx : function(){
8005         Roo.Layer.superclass.afterFx.apply(this, arguments);
8006         this.sync(this.isVisible());
8007     },
8008
8009     // private
8010     beforeAction : function(){
8011         if(!this.updating && this.shadow){
8012             this.shadow.hide();
8013         }
8014     },
8015
8016     // overridden Element method
8017     setLeft : function(left){
8018         this.storeLeftTop(left, this.getTop(true));
8019         supr.setLeft.apply(this, arguments);
8020         this.sync();
8021     },
8022
8023     setTop : function(top){
8024         this.storeLeftTop(this.getLeft(true), top);
8025         supr.setTop.apply(this, arguments);
8026         this.sync();
8027     },
8028
8029     setLeftTop : function(left, top){
8030         this.storeLeftTop(left, top);
8031         supr.setLeftTop.apply(this, arguments);
8032         this.sync();
8033     },
8034
8035     setXY : function(xy, a, d, c, e){
8036         this.fixDisplay();
8037         this.beforeAction();
8038         this.storeXY(xy);
8039         var cb = this.createCB(c);
8040         supr.setXY.call(this, xy, a, d, cb, e);
8041         if(!a){
8042             cb();
8043         }
8044     },
8045
8046     // private
8047     createCB : function(c){
8048         var el = this;
8049         return function(){
8050             el.constrainXY();
8051             el.sync(true);
8052             if(c){
8053                 c();
8054             }
8055         };
8056     },
8057
8058     // overridden Element method
8059     setX : function(x, a, d, c, e){
8060         this.setXY([x, this.getY()], a, d, c, e);
8061     },
8062
8063     // overridden Element method
8064     setY : function(y, a, d, c, e){
8065         this.setXY([this.getX(), y], a, d, c, e);
8066     },
8067
8068     // overridden Element method
8069     setSize : function(w, h, a, d, c, e){
8070         this.beforeAction();
8071         var cb = this.createCB(c);
8072         supr.setSize.call(this, w, h, a, d, cb, e);
8073         if(!a){
8074             cb();
8075         }
8076     },
8077
8078     // overridden Element method
8079     setWidth : function(w, a, d, c, e){
8080         this.beforeAction();
8081         var cb = this.createCB(c);
8082         supr.setWidth.call(this, w, a, d, cb, e);
8083         if(!a){
8084             cb();
8085         }
8086     },
8087
8088     // overridden Element method
8089     setHeight : function(h, a, d, c, e){
8090         this.beforeAction();
8091         var cb = this.createCB(c);
8092         supr.setHeight.call(this, h, a, d, cb, e);
8093         if(!a){
8094             cb();
8095         }
8096     },
8097
8098     // overridden Element method
8099     setBounds : function(x, y, w, h, a, d, c, e){
8100         this.beforeAction();
8101         var cb = this.createCB(c);
8102         if(!a){
8103             this.storeXY([x, y]);
8104             supr.setXY.call(this, [x, y]);
8105             supr.setSize.call(this, w, h, a, d, cb, e);
8106             cb();
8107         }else{
8108             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8109         }
8110         return this;
8111     },
8112     
8113     /**
8114      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8115      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8116      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8117      * @param {Number} zindex The new z-index to set
8118      * @return {this} The Layer
8119      */
8120     setZIndex : function(zindex){
8121         this.zindex = zindex;
8122         this.setStyle("z-index", zindex + 2);
8123         if(this.shadow){
8124             this.shadow.setZIndex(zindex + 1);
8125         }
8126         if(this.shim){
8127             this.shim.setStyle("z-index", zindex);
8128         }
8129     }
8130 });
8131 })();/*
8132  * Based on:
8133  * Ext JS Library 1.1.1
8134  * Copyright(c) 2006-2007, Ext JS, LLC.
8135  *
8136  * Originally Released Under LGPL - original licence link has changed is not relivant.
8137  *
8138  * Fork - LGPL
8139  * <script type="text/javascript">
8140  */
8141
8142
8143 /**
8144  * @class Roo.Shadow
8145  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8146  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8147  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8148  * @constructor
8149  * Create a new Shadow
8150  * @param {Object} config The config object
8151  */
8152 Roo.Shadow = function(config){
8153     Roo.apply(this, config);
8154     if(typeof this.mode != "string"){
8155         this.mode = this.defaultMode;
8156     }
8157     var o = this.offset, a = {h: 0};
8158     var rad = Math.floor(this.offset/2);
8159     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8160         case "drop":
8161             a.w = 0;
8162             a.l = a.t = o;
8163             a.t -= 1;
8164             if(Roo.isIE){
8165                 a.l -= this.offset + rad;
8166                 a.t -= this.offset + rad;
8167                 a.w -= rad;
8168                 a.h -= rad;
8169                 a.t += 1;
8170             }
8171         break;
8172         case "sides":
8173             a.w = (o*2);
8174             a.l = -o;
8175             a.t = o-1;
8176             if(Roo.isIE){
8177                 a.l -= (this.offset - rad);
8178                 a.t -= this.offset + rad;
8179                 a.l += 1;
8180                 a.w -= (this.offset - rad)*2;
8181                 a.w -= rad + 1;
8182                 a.h -= 1;
8183             }
8184         break;
8185         case "frame":
8186             a.w = a.h = (o*2);
8187             a.l = a.t = -o;
8188             a.t += 1;
8189             a.h -= 2;
8190             if(Roo.isIE){
8191                 a.l -= (this.offset - rad);
8192                 a.t -= (this.offset - rad);
8193                 a.l += 1;
8194                 a.w -= (this.offset + rad + 1);
8195                 a.h -= (this.offset + rad);
8196                 a.h += 1;
8197             }
8198         break;
8199     };
8200
8201     this.adjusts = a;
8202 };
8203
8204 Roo.Shadow.prototype = {
8205     /**
8206      * @cfg {String} mode
8207      * The shadow display mode.  Supports the following options:<br />
8208      * sides: Shadow displays on both sides and bottom only<br />
8209      * frame: Shadow displays equally on all four sides<br />
8210      * drop: Traditional bottom-right drop shadow (default)
8211      */
8212     /**
8213      * @cfg {String} offset
8214      * The number of pixels to offset the shadow from the element (defaults to 4)
8215      */
8216     offset: 4,
8217
8218     // private
8219     defaultMode: "drop",
8220
8221     /**
8222      * Displays the shadow under the target element
8223      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8224      */
8225     show : function(target){
8226         target = Roo.get(target);
8227         if(!this.el){
8228             this.el = Roo.Shadow.Pool.pull();
8229             if(this.el.dom.nextSibling != target.dom){
8230                 this.el.insertBefore(target);
8231             }
8232         }
8233         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8234         if(Roo.isIE){
8235             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8236         }
8237         this.realign(
8238             target.getLeft(true),
8239             target.getTop(true),
8240             target.getWidth(),
8241             target.getHeight()
8242         );
8243         this.el.dom.style.display = "block";
8244     },
8245
8246     /**
8247      * Returns true if the shadow is visible, else false
8248      */
8249     isVisible : function(){
8250         return this.el ? true : false;  
8251     },
8252
8253     /**
8254      * Direct alignment when values are already available. Show must be called at least once before
8255      * calling this method to ensure it is initialized.
8256      * @param {Number} left The target element left position
8257      * @param {Number} top The target element top position
8258      * @param {Number} width The target element width
8259      * @param {Number} height The target element height
8260      */
8261     realign : function(l, t, w, h){
8262         if(!this.el){
8263             return;
8264         }
8265         var a = this.adjusts, d = this.el.dom, s = d.style;
8266         var iea = 0;
8267         s.left = (l+a.l)+"px";
8268         s.top = (t+a.t)+"px";
8269         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8270  
8271         if(s.width != sws || s.height != shs){
8272             s.width = sws;
8273             s.height = shs;
8274             if(!Roo.isIE){
8275                 var cn = d.childNodes;
8276                 var sww = Math.max(0, (sw-12))+"px";
8277                 cn[0].childNodes[1].style.width = sww;
8278                 cn[1].childNodes[1].style.width = sww;
8279                 cn[2].childNodes[1].style.width = sww;
8280                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8281             }
8282         }
8283     },
8284
8285     /**
8286      * Hides this shadow
8287      */
8288     hide : function(){
8289         if(this.el){
8290             this.el.dom.style.display = "none";
8291             Roo.Shadow.Pool.push(this.el);
8292             delete this.el;
8293         }
8294     },
8295
8296     /**
8297      * Adjust the z-index of this shadow
8298      * @param {Number} zindex The new z-index
8299      */
8300     setZIndex : function(z){
8301         this.zIndex = z;
8302         if(this.el){
8303             this.el.setStyle("z-index", z);
8304         }
8305     }
8306 };
8307
8308 // Private utility class that manages the internal Shadow cache
8309 Roo.Shadow.Pool = function(){
8310     var p = [];
8311     var markup = Roo.isIE ?
8312                  '<div class="x-ie-shadow"></div>' :
8313                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8314     return {
8315         pull : function(){
8316             var sh = p.shift();
8317             if(!sh){
8318                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8319                 sh.autoBoxAdjust = false;
8320             }
8321             return sh;
8322         },
8323
8324         push : function(sh){
8325             p.push(sh);
8326         }
8327     };
8328 }();/*
8329  * Based on:
8330  * Ext JS Library 1.1.1
8331  * Copyright(c) 2006-2007, Ext JS, LLC.
8332  *
8333  * Originally Released Under LGPL - original licence link has changed is not relivant.
8334  *
8335  * Fork - LGPL
8336  * <script type="text/javascript">
8337  */
8338
8339 /**
8340  * @class Roo.BoxComponent
8341  * @extends Roo.Component
8342  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8343  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8344  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8345  * layout containers.
8346  * @constructor
8347  * @param {Roo.Element/String/Object} config The configuration options.
8348  */
8349 Roo.BoxComponent = function(config){
8350     Roo.Component.call(this, config);
8351     this.addEvents({
8352         /**
8353          * @event resize
8354          * Fires after the component is resized.
8355              * @param {Roo.Component} this
8356              * @param {Number} adjWidth The box-adjusted width that was set
8357              * @param {Number} adjHeight The box-adjusted height that was set
8358              * @param {Number} rawWidth The width that was originally specified
8359              * @param {Number} rawHeight The height that was originally specified
8360              */
8361         resize : true,
8362         /**
8363          * @event move
8364          * Fires after the component is moved.
8365              * @param {Roo.Component} this
8366              * @param {Number} x The new x position
8367              * @param {Number} y The new y position
8368              */
8369         move : true
8370     });
8371 };
8372
8373 Roo.extend(Roo.BoxComponent, Roo.Component, {
8374     // private, set in afterRender to signify that the component has been rendered
8375     boxReady : false,
8376     // private, used to defer height settings to subclasses
8377     deferHeight: false,
8378     /** @cfg {Number} width
8379      * width (optional) size of component
8380      */
8381      /** @cfg {Number} height
8382      * height (optional) size of component
8383      */
8384      
8385     /**
8386      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8387      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8388      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8389      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8390      * @return {Roo.BoxComponent} this
8391      */
8392     setSize : function(w, h){
8393         // support for standard size objects
8394         if(typeof w == 'object'){
8395             h = w.height;
8396             w = w.width;
8397         }
8398         // not rendered
8399         if(!this.boxReady){
8400             this.width = w;
8401             this.height = h;
8402             return this;
8403         }
8404
8405         // prevent recalcs when not needed
8406         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8407             return this;
8408         }
8409         this.lastSize = {width: w, height: h};
8410
8411         var adj = this.adjustSize(w, h);
8412         var aw = adj.width, ah = adj.height;
8413         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8414             var rz = this.getResizeEl();
8415             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8416                 rz.setSize(aw, ah);
8417             }else if(!this.deferHeight && ah !== undefined){
8418                 rz.setHeight(ah);
8419             }else if(aw !== undefined){
8420                 rz.setWidth(aw);
8421             }
8422             this.onResize(aw, ah, w, h);
8423             this.fireEvent('resize', this, aw, ah, w, h);
8424         }
8425         return this;
8426     },
8427
8428     /**
8429      * Gets the current size of the component's underlying element.
8430      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8431      */
8432     getSize : function(){
8433         return this.el.getSize();
8434     },
8435
8436     /**
8437      * Gets the current XY position of the component's underlying element.
8438      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8439      * @return {Array} The XY position of the element (e.g., [100, 200])
8440      */
8441     getPosition : function(local){
8442         if(local === true){
8443             return [this.el.getLeft(true), this.el.getTop(true)];
8444         }
8445         return this.xy || this.el.getXY();
8446     },
8447
8448     /**
8449      * Gets the current box measurements of the component's underlying element.
8450      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8451      * @returns {Object} box An object in the format {x, y, width, height}
8452      */
8453     getBox : function(local){
8454         var s = this.el.getSize();
8455         if(local){
8456             s.x = this.el.getLeft(true);
8457             s.y = this.el.getTop(true);
8458         }else{
8459             var xy = this.xy || this.el.getXY();
8460             s.x = xy[0];
8461             s.y = xy[1];
8462         }
8463         return s;
8464     },
8465
8466     /**
8467      * Sets the current box measurements of the component's underlying element.
8468      * @param {Object} box An object in the format {x, y, width, height}
8469      * @returns {Roo.BoxComponent} this
8470      */
8471     updateBox : function(box){
8472         this.setSize(box.width, box.height);
8473         this.setPagePosition(box.x, box.y);
8474         return this;
8475     },
8476
8477     // protected
8478     getResizeEl : function(){
8479         return this.resizeEl || this.el;
8480     },
8481
8482     // protected
8483     getPositionEl : function(){
8484         return this.positionEl || this.el;
8485     },
8486
8487     /**
8488      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8489      * This method fires the move event.
8490      * @param {Number} left The new left
8491      * @param {Number} top The new top
8492      * @returns {Roo.BoxComponent} this
8493      */
8494     setPosition : function(x, y){
8495         this.x = x;
8496         this.y = y;
8497         if(!this.boxReady){
8498             return this;
8499         }
8500         var adj = this.adjustPosition(x, y);
8501         var ax = adj.x, ay = adj.y;
8502
8503         var el = this.getPositionEl();
8504         if(ax !== undefined || ay !== undefined){
8505             if(ax !== undefined && ay !== undefined){
8506                 el.setLeftTop(ax, ay);
8507             }else if(ax !== undefined){
8508                 el.setLeft(ax);
8509             }else if(ay !== undefined){
8510                 el.setTop(ay);
8511             }
8512             this.onPosition(ax, ay);
8513             this.fireEvent('move', this, ax, ay);
8514         }
8515         return this;
8516     },
8517
8518     /**
8519      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8520      * This method fires the move event.
8521      * @param {Number} x The new x position
8522      * @param {Number} y The new y position
8523      * @returns {Roo.BoxComponent} this
8524      */
8525     setPagePosition : function(x, y){
8526         this.pageX = x;
8527         this.pageY = y;
8528         if(!this.boxReady){
8529             return;
8530         }
8531         if(x === undefined || y === undefined){ // cannot translate undefined points
8532             return;
8533         }
8534         var p = this.el.translatePoints(x, y);
8535         this.setPosition(p.left, p.top);
8536         return this;
8537     },
8538
8539     // private
8540     onRender : function(ct, position){
8541         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8542         if(this.resizeEl){
8543             this.resizeEl = Roo.get(this.resizeEl);
8544         }
8545         if(this.positionEl){
8546             this.positionEl = Roo.get(this.positionEl);
8547         }
8548     },
8549
8550     // private
8551     afterRender : function(){
8552         Roo.BoxComponent.superclass.afterRender.call(this);
8553         this.boxReady = true;
8554         this.setSize(this.width, this.height);
8555         if(this.x || this.y){
8556             this.setPosition(this.x, this.y);
8557         }
8558         if(this.pageX || this.pageY){
8559             this.setPagePosition(this.pageX, this.pageY);
8560         }
8561     },
8562
8563     /**
8564      * Force the component's size to recalculate based on the underlying element's current height and width.
8565      * @returns {Roo.BoxComponent} this
8566      */
8567     syncSize : function(){
8568         delete this.lastSize;
8569         this.setSize(this.el.getWidth(), this.el.getHeight());
8570         return this;
8571     },
8572
8573     /**
8574      * Called after the component is resized, this method is empty by default but can be implemented by any
8575      * subclass that needs to perform custom logic after a resize occurs.
8576      * @param {Number} adjWidth The box-adjusted width that was set
8577      * @param {Number} adjHeight The box-adjusted height that was set
8578      * @param {Number} rawWidth The width that was originally specified
8579      * @param {Number} rawHeight The height that was originally specified
8580      */
8581     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8582
8583     },
8584
8585     /**
8586      * Called after the component is moved, this method is empty by default but can be implemented by any
8587      * subclass that needs to perform custom logic after a move occurs.
8588      * @param {Number} x The new x position
8589      * @param {Number} y The new y position
8590      */
8591     onPosition : function(x, y){
8592
8593     },
8594
8595     // private
8596     adjustSize : function(w, h){
8597         if(this.autoWidth){
8598             w = 'auto';
8599         }
8600         if(this.autoHeight){
8601             h = 'auto';
8602         }
8603         return {width : w, height: h};
8604     },
8605
8606     // private
8607     adjustPosition : function(x, y){
8608         return {x : x, y: y};
8609     }
8610 });/*
8611  * Based on:
8612  * Ext JS Library 1.1.1
8613  * Copyright(c) 2006-2007, Ext JS, LLC.
8614  *
8615  * Originally Released Under LGPL - original licence link has changed is not relivant.
8616  *
8617  * Fork - LGPL
8618  * <script type="text/javascript">
8619  */
8620
8621
8622 /**
8623  * @class Roo.SplitBar
8624  * @extends Roo.util.Observable
8625  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8626  * <br><br>
8627  * Usage:
8628  * <pre><code>
8629 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8630                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8631 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8632 split.minSize = 100;
8633 split.maxSize = 600;
8634 split.animate = true;
8635 split.on('moved', splitterMoved);
8636 </code></pre>
8637  * @constructor
8638  * Create a new SplitBar
8639  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8640  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8641  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8642  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8643                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8644                         position of the SplitBar).
8645  */
8646 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8647     
8648     /** @private */
8649     this.el = Roo.get(dragElement, true);
8650     this.el.dom.unselectable = "on";
8651     /** @private */
8652     this.resizingEl = Roo.get(resizingElement, true);
8653
8654     /**
8655      * @private
8656      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8657      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8658      * @type Number
8659      */
8660     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8661     
8662     /**
8663      * The minimum size of the resizing element. (Defaults to 0)
8664      * @type Number
8665      */
8666     this.minSize = 0;
8667     
8668     /**
8669      * The maximum size of the resizing element. (Defaults to 2000)
8670      * @type Number
8671      */
8672     this.maxSize = 2000;
8673     
8674     /**
8675      * Whether to animate the transition to the new size
8676      * @type Boolean
8677      */
8678     this.animate = false;
8679     
8680     /**
8681      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8682      * @type Boolean
8683      */
8684     this.useShim = false;
8685     
8686     /** @private */
8687     this.shim = null;
8688     
8689     if(!existingProxy){
8690         /** @private */
8691         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8692     }else{
8693         this.proxy = Roo.get(existingProxy).dom;
8694     }
8695     /** @private */
8696     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8697     
8698     /** @private */
8699     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8700     
8701     /** @private */
8702     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8703     
8704     /** @private */
8705     this.dragSpecs = {};
8706     
8707     /**
8708      * @private The adapter to use to positon and resize elements
8709      */
8710     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8711     this.adapter.init(this);
8712     
8713     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8714         /** @private */
8715         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8716         this.el.addClass("x-splitbar-h");
8717     }else{
8718         /** @private */
8719         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8720         this.el.addClass("x-splitbar-v");
8721     }
8722     
8723     this.addEvents({
8724         /**
8725          * @event resize
8726          * Fires when the splitter is moved (alias for {@link #event-moved})
8727          * @param {Roo.SplitBar} this
8728          * @param {Number} newSize the new width or height
8729          */
8730         "resize" : true,
8731         /**
8732          * @event moved
8733          * Fires when the splitter is moved
8734          * @param {Roo.SplitBar} this
8735          * @param {Number} newSize the new width or height
8736          */
8737         "moved" : true,
8738         /**
8739          * @event beforeresize
8740          * Fires before the splitter is dragged
8741          * @param {Roo.SplitBar} this
8742          */
8743         "beforeresize" : true,
8744
8745         "beforeapply" : true
8746     });
8747
8748     Roo.util.Observable.call(this);
8749 };
8750
8751 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8752     onStartProxyDrag : function(x, y){
8753         this.fireEvent("beforeresize", this);
8754         if(!this.overlay){
8755             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8756             o.unselectable();
8757             o.enableDisplayMode("block");
8758             // all splitbars share the same overlay
8759             Roo.SplitBar.prototype.overlay = o;
8760         }
8761         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8762         this.overlay.show();
8763         Roo.get(this.proxy).setDisplayed("block");
8764         var size = this.adapter.getElementSize(this);
8765         this.activeMinSize = this.getMinimumSize();;
8766         this.activeMaxSize = this.getMaximumSize();;
8767         var c1 = size - this.activeMinSize;
8768         var c2 = Math.max(this.activeMaxSize - size, 0);
8769         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8770             this.dd.resetConstraints();
8771             this.dd.setXConstraint(
8772                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8773                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8774             );
8775             this.dd.setYConstraint(0, 0);
8776         }else{
8777             this.dd.resetConstraints();
8778             this.dd.setXConstraint(0, 0);
8779             this.dd.setYConstraint(
8780                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8781                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8782             );
8783          }
8784         this.dragSpecs.startSize = size;
8785         this.dragSpecs.startPoint = [x, y];
8786         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8787     },
8788     
8789     /** 
8790      * @private Called after the drag operation by the DDProxy
8791      */
8792     onEndProxyDrag : function(e){
8793         Roo.get(this.proxy).setDisplayed(false);
8794         var endPoint = Roo.lib.Event.getXY(e);
8795         if(this.overlay){
8796             this.overlay.hide();
8797         }
8798         var newSize;
8799         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8800             newSize = this.dragSpecs.startSize + 
8801                 (this.placement == Roo.SplitBar.LEFT ?
8802                     endPoint[0] - this.dragSpecs.startPoint[0] :
8803                     this.dragSpecs.startPoint[0] - endPoint[0]
8804                 );
8805         }else{
8806             newSize = this.dragSpecs.startSize + 
8807                 (this.placement == Roo.SplitBar.TOP ?
8808                     endPoint[1] - this.dragSpecs.startPoint[1] :
8809                     this.dragSpecs.startPoint[1] - endPoint[1]
8810                 );
8811         }
8812         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8813         if(newSize != this.dragSpecs.startSize){
8814             if(this.fireEvent('beforeapply', this, newSize) !== false){
8815                 this.adapter.setElementSize(this, newSize);
8816                 this.fireEvent("moved", this, newSize);
8817                 this.fireEvent("resize", this, newSize);
8818             }
8819         }
8820     },
8821     
8822     /**
8823      * Get the adapter this SplitBar uses
8824      * @return The adapter object
8825      */
8826     getAdapter : function(){
8827         return this.adapter;
8828     },
8829     
8830     /**
8831      * Set the adapter this SplitBar uses
8832      * @param {Object} adapter A SplitBar adapter object
8833      */
8834     setAdapter : function(adapter){
8835         this.adapter = adapter;
8836         this.adapter.init(this);
8837     },
8838     
8839     /**
8840      * Gets the minimum size for the resizing element
8841      * @return {Number} The minimum size
8842      */
8843     getMinimumSize : function(){
8844         return this.minSize;
8845     },
8846     
8847     /**
8848      * Sets the minimum size for the resizing element
8849      * @param {Number} minSize The minimum size
8850      */
8851     setMinimumSize : function(minSize){
8852         this.minSize = minSize;
8853     },
8854     
8855     /**
8856      * Gets the maximum size for the resizing element
8857      * @return {Number} The maximum size
8858      */
8859     getMaximumSize : function(){
8860         return this.maxSize;
8861     },
8862     
8863     /**
8864      * Sets the maximum size for the resizing element
8865      * @param {Number} maxSize The maximum size
8866      */
8867     setMaximumSize : function(maxSize){
8868         this.maxSize = maxSize;
8869     },
8870     
8871     /**
8872      * Sets the initialize size for the resizing element
8873      * @param {Number} size The initial size
8874      */
8875     setCurrentSize : function(size){
8876         var oldAnimate = this.animate;
8877         this.animate = false;
8878         this.adapter.setElementSize(this, size);
8879         this.animate = oldAnimate;
8880     },
8881     
8882     /**
8883      * Destroy this splitbar. 
8884      * @param {Boolean} removeEl True to remove the element
8885      */
8886     destroy : function(removeEl){
8887         if(this.shim){
8888             this.shim.remove();
8889         }
8890         this.dd.unreg();
8891         this.proxy.parentNode.removeChild(this.proxy);
8892         if(removeEl){
8893             this.el.remove();
8894         }
8895     }
8896 });
8897
8898 /**
8899  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8900  */
8901 Roo.SplitBar.createProxy = function(dir){
8902     var proxy = new Roo.Element(document.createElement("div"));
8903     proxy.unselectable();
8904     var cls = 'x-splitbar-proxy';
8905     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8906     document.body.appendChild(proxy.dom);
8907     return proxy.dom;
8908 };
8909
8910 /** 
8911  * @class Roo.SplitBar.BasicLayoutAdapter
8912  * Default Adapter. It assumes the splitter and resizing element are not positioned
8913  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8914  */
8915 Roo.SplitBar.BasicLayoutAdapter = function(){
8916 };
8917
8918 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8919     // do nothing for now
8920     init : function(s){
8921     
8922     },
8923     /**
8924      * Called before drag operations to get the current size of the resizing element. 
8925      * @param {Roo.SplitBar} s The SplitBar using this adapter
8926      */
8927      getElementSize : function(s){
8928         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8929             return s.resizingEl.getWidth();
8930         }else{
8931             return s.resizingEl.getHeight();
8932         }
8933     },
8934     
8935     /**
8936      * Called after drag operations to set the size of the resizing element.
8937      * @param {Roo.SplitBar} s The SplitBar using this adapter
8938      * @param {Number} newSize The new size to set
8939      * @param {Function} onComplete A function to be invoked when resizing is complete
8940      */
8941     setElementSize : function(s, newSize, onComplete){
8942         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8943             if(!s.animate){
8944                 s.resizingEl.setWidth(newSize);
8945                 if(onComplete){
8946                     onComplete(s, newSize);
8947                 }
8948             }else{
8949                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8950             }
8951         }else{
8952             
8953             if(!s.animate){
8954                 s.resizingEl.setHeight(newSize);
8955                 if(onComplete){
8956                     onComplete(s, newSize);
8957                 }
8958             }else{
8959                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8960             }
8961         }
8962     }
8963 };
8964
8965 /** 
8966  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8967  * @extends Roo.SplitBar.BasicLayoutAdapter
8968  * Adapter that  moves the splitter element to align with the resized sizing element. 
8969  * Used with an absolute positioned SplitBar.
8970  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8971  * document.body, make sure you assign an id to the body element.
8972  */
8973 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8974     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8975     this.container = Roo.get(container);
8976 };
8977
8978 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8979     init : function(s){
8980         this.basic.init(s);
8981     },
8982     
8983     getElementSize : function(s){
8984         return this.basic.getElementSize(s);
8985     },
8986     
8987     setElementSize : function(s, newSize, onComplete){
8988         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8989     },
8990     
8991     moveSplitter : function(s){
8992         var yes = Roo.SplitBar;
8993         switch(s.placement){
8994             case yes.LEFT:
8995                 s.el.setX(s.resizingEl.getRight());
8996                 break;
8997             case yes.RIGHT:
8998                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8999                 break;
9000             case yes.TOP:
9001                 s.el.setY(s.resizingEl.getBottom());
9002                 break;
9003             case yes.BOTTOM:
9004                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9005                 break;
9006         }
9007     }
9008 };
9009
9010 /**
9011  * Orientation constant - Create a vertical SplitBar
9012  * @static
9013  * @type Number
9014  */
9015 Roo.SplitBar.VERTICAL = 1;
9016
9017 /**
9018  * Orientation constant - Create a horizontal SplitBar
9019  * @static
9020  * @type Number
9021  */
9022 Roo.SplitBar.HORIZONTAL = 2;
9023
9024 /**
9025  * Placement constant - The resizing element is to the left of the splitter element
9026  * @static
9027  * @type Number
9028  */
9029 Roo.SplitBar.LEFT = 1;
9030
9031 /**
9032  * Placement constant - The resizing element is to the right of the splitter element
9033  * @static
9034  * @type Number
9035  */
9036 Roo.SplitBar.RIGHT = 2;
9037
9038 /**
9039  * Placement constant - The resizing element is positioned above the splitter element
9040  * @static
9041  * @type Number
9042  */
9043 Roo.SplitBar.TOP = 3;
9044
9045 /**
9046  * Placement constant - The resizing element is positioned under splitter element
9047  * @static
9048  * @type Number
9049  */
9050 Roo.SplitBar.BOTTOM = 4;
9051 /*
9052  * Based on:
9053  * Ext JS Library 1.1.1
9054  * Copyright(c) 2006-2007, Ext JS, LLC.
9055  *
9056  * Originally Released Under LGPL - original licence link has changed is not relivant.
9057  *
9058  * Fork - LGPL
9059  * <script type="text/javascript">
9060  */
9061
9062 /**
9063  * @class Roo.View
9064  * @extends Roo.util.Observable
9065  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9066  * This class also supports single and multi selection modes. <br>
9067  * Create a data model bound view:
9068  <pre><code>
9069  var store = new Roo.data.Store(...);
9070
9071  var view = new Roo.View({
9072     el : "my-element",
9073     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9074  
9075     singleSelect: true,
9076     selectedClass: "ydataview-selected",
9077     store: store
9078  });
9079
9080  // listen for node click?
9081  view.on("click", function(vw, index, node, e){
9082  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9083  });
9084
9085  // load XML data
9086  dataModel.load("foobar.xml");
9087  </code></pre>
9088  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9089  * <br><br>
9090  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9091  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9092  * 
9093  * Note: old style constructor is still suported (container, template, config)
9094  * 
9095  * @constructor
9096  * Create a new View
9097  * @param {Object} config The config object
9098  * 
9099  */
9100 Roo.View = function(config, depreciated_tpl, depreciated_config){
9101     
9102     if (typeof(depreciated_tpl) == 'undefined') {
9103         // new way.. - universal constructor.
9104         Roo.apply(this, config);
9105         this.el  = Roo.get(this.el);
9106     } else {
9107         // old format..
9108         this.el  = Roo.get(config);
9109         this.tpl = depreciated_tpl;
9110         Roo.apply(this, depreciated_config);
9111     }
9112      
9113     
9114     if(typeof(this.tpl) == "string"){
9115         this.tpl = new Roo.Template(this.tpl);
9116     } else {
9117         // support xtype ctors..
9118         this.tpl = new Roo.factory(this.tpl, Roo);
9119     }
9120     
9121     
9122     this.tpl.compile();
9123    
9124
9125      
9126     /** @private */
9127     this.addEvents({
9128     /**
9129      * @event beforeclick
9130      * Fires before a click is processed. Returns false to cancel the default action.
9131      * @param {Roo.View} this
9132      * @param {Number} index The index of the target node
9133      * @param {HTMLElement} node The target node
9134      * @param {Roo.EventObject} e The raw event object
9135      */
9136         "beforeclick" : true,
9137     /**
9138      * @event click
9139      * Fires when a template node is clicked.
9140      * @param {Roo.View} this
9141      * @param {Number} index The index of the target node
9142      * @param {HTMLElement} node The target node
9143      * @param {Roo.EventObject} e The raw event object
9144      */
9145         "click" : true,
9146     /**
9147      * @event dblclick
9148      * Fires when a template node is double clicked.
9149      * @param {Roo.View} this
9150      * @param {Number} index The index of the target node
9151      * @param {HTMLElement} node The target node
9152      * @param {Roo.EventObject} e The raw event object
9153      */
9154         "dblclick" : true,
9155     /**
9156      * @event contextmenu
9157      * Fires when a template node is right clicked.
9158      * @param {Roo.View} this
9159      * @param {Number} index The index of the target node
9160      * @param {HTMLElement} node The target node
9161      * @param {Roo.EventObject} e The raw event object
9162      */
9163         "contextmenu" : true,
9164     /**
9165      * @event selectionchange
9166      * Fires when the selected nodes change.
9167      * @param {Roo.View} this
9168      * @param {Array} selections Array of the selected nodes
9169      */
9170         "selectionchange" : true,
9171
9172     /**
9173      * @event beforeselect
9174      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9175      * @param {Roo.View} this
9176      * @param {HTMLElement} node The node to be selected
9177      * @param {Array} selections Array of currently selected nodes
9178      */
9179         "beforeselect" : true
9180     });
9181
9182     this.el.on({
9183         "click": this.onClick,
9184         "dblclick": this.onDblClick,
9185         "contextmenu": this.onContextMenu,
9186         scope:this
9187     });
9188
9189     this.selections = [];
9190     this.nodes = [];
9191     this.cmp = new Roo.CompositeElementLite([]);
9192     if(this.store){
9193         this.store = Roo.factory(this.store, Roo.data);
9194         this.setStore(this.store, true);
9195     }
9196     Roo.View.superclass.constructor.call(this);
9197 };
9198
9199 Roo.extend(Roo.View, Roo.util.Observable, {
9200     
9201      /**
9202      * @cfg {Roo.data.Store} store Data store to load data from.
9203      */
9204     store : false,
9205     
9206     /**
9207      * @cfg {String|Roo.Element} el The container element.
9208      */
9209     el : '',
9210     
9211     /**
9212      * @cfg {String|Roo.Template} tpl The template used by this View 
9213      */
9214     tpl : false,
9215     
9216     /**
9217      * @cfg {String} selectedClass The css class to add to selected nodes
9218      */
9219     selectedClass : "x-view-selected",
9220      /**
9221      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9222      */
9223     emptyText : "",
9224     /**
9225      * @cfg {Boolean} multiSelect Allow multiple selection
9226      */
9227     
9228     multiSelect : false,
9229     /**
9230      * @cfg {Boolean} singleSelect Allow single selection
9231      */
9232     singleSelect:  false,
9233     
9234     /**
9235      * Returns the element this view is bound to.
9236      * @return {Roo.Element}
9237      */
9238     getEl : function(){
9239         return this.el;
9240     },
9241
9242     /**
9243      * Refreshes the view.
9244      */
9245     refresh : function(){
9246         var t = this.tpl;
9247         this.clearSelections();
9248         this.el.update("");
9249         var html = [];
9250         var records = this.store.getRange();
9251         if(records.length < 1){
9252             this.el.update(this.emptyText);
9253             return;
9254         }
9255         for(var i = 0, len = records.length; i < len; i++){
9256             var data = this.prepareData(records[i].data, i, records[i]);
9257             html[html.length] = t.apply(data);
9258         }
9259         this.el.update(html.join(""));
9260         this.nodes = this.el.dom.childNodes;
9261         this.updateIndexes(0);
9262     },
9263
9264     /**
9265      * Function to override to reformat the data that is sent to
9266      * the template for each node.
9267      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9268      * a JSON object for an UpdateManager bound view).
9269      */
9270     prepareData : function(data){
9271         return data;
9272     },
9273
9274     onUpdate : function(ds, record){
9275         this.clearSelections();
9276         var index = this.store.indexOf(record);
9277         var n = this.nodes[index];
9278         this.tpl.insertBefore(n, this.prepareData(record.data));
9279         n.parentNode.removeChild(n);
9280         this.updateIndexes(index, index);
9281     },
9282
9283     onAdd : function(ds, records, index){
9284         this.clearSelections();
9285         if(this.nodes.length == 0){
9286             this.refresh();
9287             return;
9288         }
9289         var n = this.nodes[index];
9290         for(var i = 0, len = records.length; i < len; i++){
9291             var d = this.prepareData(records[i].data);
9292             if(n){
9293                 this.tpl.insertBefore(n, d);
9294             }else{
9295                 this.tpl.append(this.el, d);
9296             }
9297         }
9298         this.updateIndexes(index);
9299     },
9300
9301     onRemove : function(ds, record, index){
9302         this.clearSelections();
9303         this.el.dom.removeChild(this.nodes[index]);
9304         this.updateIndexes(index);
9305     },
9306
9307     /**
9308      * Refresh an individual node.
9309      * @param {Number} index
9310      */
9311     refreshNode : function(index){
9312         this.onUpdate(this.store, this.store.getAt(index));
9313     },
9314
9315     updateIndexes : function(startIndex, endIndex){
9316         var ns = this.nodes;
9317         startIndex = startIndex || 0;
9318         endIndex = endIndex || ns.length - 1;
9319         for(var i = startIndex; i <= endIndex; i++){
9320             ns[i].nodeIndex = i;
9321         }
9322     },
9323
9324     /**
9325      * Changes the data store this view uses and refresh the view.
9326      * @param {Store} store
9327      */
9328     setStore : function(store, initial){
9329         if(!initial && this.store){
9330             this.store.un("datachanged", this.refresh);
9331             this.store.un("add", this.onAdd);
9332             this.store.un("remove", this.onRemove);
9333             this.store.un("update", this.onUpdate);
9334             this.store.un("clear", this.refresh);
9335         }
9336         if(store){
9337           
9338             store.on("datachanged", this.refresh, this);
9339             store.on("add", this.onAdd, this);
9340             store.on("remove", this.onRemove, this);
9341             store.on("update", this.onUpdate, this);
9342             store.on("clear", this.refresh, this);
9343         }
9344         
9345         if(store){
9346             this.refresh();
9347         }
9348     },
9349
9350     /**
9351      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9352      * @param {HTMLElement} node
9353      * @return {HTMLElement} The template node
9354      */
9355     findItemFromChild : function(node){
9356         var el = this.el.dom;
9357         if(!node || node.parentNode == el){
9358                     return node;
9359             }
9360             var p = node.parentNode;
9361             while(p && p != el){
9362             if(p.parentNode == el){
9363                 return p;
9364             }
9365             p = p.parentNode;
9366         }
9367             return null;
9368     },
9369
9370     /** @ignore */
9371     onClick : function(e){
9372         var item = this.findItemFromChild(e.getTarget());
9373         if(item){
9374             var index = this.indexOf(item);
9375             if(this.onItemClick(item, index, e) !== false){
9376                 this.fireEvent("click", this, index, item, e);
9377             }
9378         }else{
9379             this.clearSelections();
9380         }
9381     },
9382
9383     /** @ignore */
9384     onContextMenu : function(e){
9385         var item = this.findItemFromChild(e.getTarget());
9386         if(item){
9387             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9388         }
9389     },
9390
9391     /** @ignore */
9392     onDblClick : function(e){
9393         var item = this.findItemFromChild(e.getTarget());
9394         if(item){
9395             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9396         }
9397     },
9398
9399     onItemClick : function(item, index, e){
9400         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9401             return false;
9402         }
9403         if(this.multiSelect || this.singleSelect){
9404             if(this.multiSelect && e.shiftKey && this.lastSelection){
9405                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9406             }else{
9407                 this.select(item, this.multiSelect && e.ctrlKey);
9408                 this.lastSelection = item;
9409             }
9410             e.preventDefault();
9411         }
9412         return true;
9413     },
9414
9415     /**
9416      * Get the number of selected nodes.
9417      * @return {Number}
9418      */
9419     getSelectionCount : function(){
9420         return this.selections.length;
9421     },
9422
9423     /**
9424      * Get the currently selected nodes.
9425      * @return {Array} An array of HTMLElements
9426      */
9427     getSelectedNodes : function(){
9428         return this.selections;
9429     },
9430
9431     /**
9432      * Get the indexes of the selected nodes.
9433      * @return {Array}
9434      */
9435     getSelectedIndexes : function(){
9436         var indexes = [], s = this.selections;
9437         for(var i = 0, len = s.length; i < len; i++){
9438             indexes.push(s[i].nodeIndex);
9439         }
9440         return indexes;
9441     },
9442
9443     /**
9444      * Clear all selections
9445      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9446      */
9447     clearSelections : function(suppressEvent){
9448         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9449             this.cmp.elements = this.selections;
9450             this.cmp.removeClass(this.selectedClass);
9451             this.selections = [];
9452             if(!suppressEvent){
9453                 this.fireEvent("selectionchange", this, this.selections);
9454             }
9455         }
9456     },
9457
9458     /**
9459      * Returns true if the passed node is selected
9460      * @param {HTMLElement/Number} node The node or node index
9461      * @return {Boolean}
9462      */
9463     isSelected : function(node){
9464         var s = this.selections;
9465         if(s.length < 1){
9466             return false;
9467         }
9468         node = this.getNode(node);
9469         return s.indexOf(node) !== -1;
9470     },
9471
9472     /**
9473      * Selects nodes.
9474      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9475      * @param {Boolean} keepExisting (optional) true to keep existing selections
9476      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9477      */
9478     select : function(nodeInfo, keepExisting, suppressEvent){
9479         if(nodeInfo instanceof Array){
9480             if(!keepExisting){
9481                 this.clearSelections(true);
9482             }
9483             for(var i = 0, len = nodeInfo.length; i < len; i++){
9484                 this.select(nodeInfo[i], true, true);
9485             }
9486         } else{
9487             var node = this.getNode(nodeInfo);
9488             if(node && !this.isSelected(node)){
9489                 if(!keepExisting){
9490                     this.clearSelections(true);
9491                 }
9492                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9493                     Roo.fly(node).addClass(this.selectedClass);
9494                     this.selections.push(node);
9495                     if(!suppressEvent){
9496                         this.fireEvent("selectionchange", this, this.selections);
9497                     }
9498                 }
9499             }
9500         }
9501     },
9502
9503     /**
9504      * Gets a template node.
9505      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9506      * @return {HTMLElement} The node or null if it wasn't found
9507      */
9508     getNode : function(nodeInfo){
9509         if(typeof nodeInfo == "string"){
9510             return document.getElementById(nodeInfo);
9511         }else if(typeof nodeInfo == "number"){
9512             return this.nodes[nodeInfo];
9513         }
9514         return nodeInfo;
9515     },
9516
9517     /**
9518      * Gets a range template nodes.
9519      * @param {Number} startIndex
9520      * @param {Number} endIndex
9521      * @return {Array} An array of nodes
9522      */
9523     getNodes : function(start, end){
9524         var ns = this.nodes;
9525         start = start || 0;
9526         end = typeof end == "undefined" ? ns.length - 1 : end;
9527         var nodes = [];
9528         if(start <= end){
9529             for(var i = start; i <= end; i++){
9530                 nodes.push(ns[i]);
9531             }
9532         } else{
9533             for(var i = start; i >= end; i--){
9534                 nodes.push(ns[i]);
9535             }
9536         }
9537         return nodes;
9538     },
9539
9540     /**
9541      * Finds the index of the passed node
9542      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9543      * @return {Number} The index of the node or -1
9544      */
9545     indexOf : function(node){
9546         node = this.getNode(node);
9547         if(typeof node.nodeIndex == "number"){
9548             return node.nodeIndex;
9549         }
9550         var ns = this.nodes;
9551         for(var i = 0, len = ns.length; i < len; i++){
9552             if(ns[i] == node){
9553                 return i;
9554             }
9555         }
9556         return -1;
9557     }
9558 });
9559 /*
9560  * Based on:
9561  * Ext JS Library 1.1.1
9562  * Copyright(c) 2006-2007, Ext JS, LLC.
9563  *
9564  * Originally Released Under LGPL - original licence link has changed is not relivant.
9565  *
9566  * Fork - LGPL
9567  * <script type="text/javascript">
9568  */
9569
9570 /**
9571  * @class Roo.JsonView
9572  * @extends Roo.View
9573  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9574 <pre><code>
9575 var view = new Roo.JsonView({
9576     container: "my-element",
9577     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9578     multiSelect: true, 
9579     jsonRoot: "data" 
9580 });
9581
9582 // listen for node click?
9583 view.on("click", function(vw, index, node, e){
9584     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9585 });
9586
9587 // direct load of JSON data
9588 view.load("foobar.php");
9589
9590 // Example from my blog list
9591 var tpl = new Roo.Template(
9592     '&lt;div class="entry"&gt;' +
9593     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9594     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9595     "&lt;/div&gt;&lt;hr /&gt;"
9596 );
9597
9598 var moreView = new Roo.JsonView({
9599     container :  "entry-list", 
9600     template : tpl,
9601     jsonRoot: "posts"
9602 });
9603 moreView.on("beforerender", this.sortEntries, this);
9604 moreView.load({
9605     url: "/blog/get-posts.php",
9606     params: "allposts=true",
9607     text: "Loading Blog Entries..."
9608 });
9609 </code></pre>
9610
9611 * Note: old code is supported with arguments : (container, template, config)
9612
9613
9614  * @constructor
9615  * Create a new JsonView
9616  * 
9617  * @param {Object} config The config object
9618  * 
9619  */
9620 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9621     
9622     
9623     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9624
9625     var um = this.el.getUpdateManager();
9626     um.setRenderer(this);
9627     um.on("update", this.onLoad, this);
9628     um.on("failure", this.onLoadException, this);
9629
9630     /**
9631      * @event beforerender
9632      * Fires before rendering of the downloaded JSON data.
9633      * @param {Roo.JsonView} this
9634      * @param {Object} data The JSON data loaded
9635      */
9636     /**
9637      * @event load
9638      * Fires when data is loaded.
9639      * @param {Roo.JsonView} this
9640      * @param {Object} data The JSON data loaded
9641      * @param {Object} response The raw Connect response object
9642      */
9643     /**
9644      * @event loadexception
9645      * Fires when loading fails.
9646      * @param {Roo.JsonView} this
9647      * @param {Object} response The raw Connect response object
9648      */
9649     this.addEvents({
9650         'beforerender' : true,
9651         'load' : true,
9652         'loadexception' : true
9653     });
9654 };
9655 Roo.extend(Roo.JsonView, Roo.View, {
9656     /**
9657      * @type {String} The root property in the loaded JSON object that contains the data
9658      */
9659     jsonRoot : "",
9660
9661     /**
9662      * Refreshes the view.
9663      */
9664     refresh : function(){
9665         this.clearSelections();
9666         this.el.update("");
9667         var html = [];
9668         var o = this.jsonData;
9669         if(o && o.length > 0){
9670             for(var i = 0, len = o.length; i < len; i++){
9671                 var data = this.prepareData(o[i], i, o);
9672                 html[html.length] = this.tpl.apply(data);
9673             }
9674         }else{
9675             html.push(this.emptyText);
9676         }
9677         this.el.update(html.join(""));
9678         this.nodes = this.el.dom.childNodes;
9679         this.updateIndexes(0);
9680     },
9681
9682     /**
9683      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9684      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9685      <pre><code>
9686      view.load({
9687          url: "your-url.php",
9688          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9689          callback: yourFunction,
9690          scope: yourObject, //(optional scope)
9691          discardUrl: false,
9692          nocache: false,
9693          text: "Loading...",
9694          timeout: 30,
9695          scripts: false
9696      });
9697      </code></pre>
9698      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9699      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9700      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9701      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9702      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9703      */
9704     load : function(){
9705         var um = this.el.getUpdateManager();
9706         um.update.apply(um, arguments);
9707     },
9708
9709     render : function(el, response){
9710         this.clearSelections();
9711         this.el.update("");
9712         var o;
9713         try{
9714             o = Roo.util.JSON.decode(response.responseText);
9715             if(this.jsonRoot){
9716                 
9717                 o = o[this.jsonRoot];
9718             }
9719         } catch(e){
9720         }
9721         /**
9722          * The current JSON data or null
9723          */
9724         this.jsonData = o;
9725         this.beforeRender();
9726         this.refresh();
9727     },
9728
9729 /**
9730  * Get the number of records in the current JSON dataset
9731  * @return {Number}
9732  */
9733     getCount : function(){
9734         return this.jsonData ? this.jsonData.length : 0;
9735     },
9736
9737 /**
9738  * Returns the JSON object for the specified node(s)
9739  * @param {HTMLElement/Array} node The node or an array of nodes
9740  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9741  * you get the JSON object for the node
9742  */
9743     getNodeData : function(node){
9744         if(node instanceof Array){
9745             var data = [];
9746             for(var i = 0, len = node.length; i < len; i++){
9747                 data.push(this.getNodeData(node[i]));
9748             }
9749             return data;
9750         }
9751         return this.jsonData[this.indexOf(node)] || null;
9752     },
9753
9754     beforeRender : function(){
9755         this.snapshot = this.jsonData;
9756         if(this.sortInfo){
9757             this.sort.apply(this, this.sortInfo);
9758         }
9759         this.fireEvent("beforerender", this, this.jsonData);
9760     },
9761
9762     onLoad : function(el, o){
9763         this.fireEvent("load", this, this.jsonData, o);
9764     },
9765
9766     onLoadException : function(el, o){
9767         this.fireEvent("loadexception", this, o);
9768     },
9769
9770 /**
9771  * Filter the data by a specific property.
9772  * @param {String} property A property on your JSON objects
9773  * @param {String/RegExp} value Either string that the property values
9774  * should start with, or a RegExp to test against the property
9775  */
9776     filter : function(property, value){
9777         if(this.jsonData){
9778             var data = [];
9779             var ss = this.snapshot;
9780             if(typeof value == "string"){
9781                 var vlen = value.length;
9782                 if(vlen == 0){
9783                     this.clearFilter();
9784                     return;
9785                 }
9786                 value = value.toLowerCase();
9787                 for(var i = 0, len = ss.length; i < len; i++){
9788                     var o = ss[i];
9789                     if(o[property].substr(0, vlen).toLowerCase() == value){
9790                         data.push(o);
9791                     }
9792                 }
9793             } else if(value.exec){ // regex?
9794                 for(var i = 0, len = ss.length; i < len; i++){
9795                     var o = ss[i];
9796                     if(value.test(o[property])){
9797                         data.push(o);
9798                     }
9799                 }
9800             } else{
9801                 return;
9802             }
9803             this.jsonData = data;
9804             this.refresh();
9805         }
9806     },
9807
9808 /**
9809  * Filter by a function. The passed function will be called with each
9810  * object in the current dataset. If the function returns true the value is kept,
9811  * otherwise it is filtered.
9812  * @param {Function} fn
9813  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9814  */
9815     filterBy : function(fn, scope){
9816         if(this.jsonData){
9817             var data = [];
9818             var ss = this.snapshot;
9819             for(var i = 0, len = ss.length; i < len; i++){
9820                 var o = ss[i];
9821                 if(fn.call(scope || this, o)){
9822                     data.push(o);
9823                 }
9824             }
9825             this.jsonData = data;
9826             this.refresh();
9827         }
9828     },
9829
9830 /**
9831  * Clears the current filter.
9832  */
9833     clearFilter : function(){
9834         if(this.snapshot && this.jsonData != this.snapshot){
9835             this.jsonData = this.snapshot;
9836             this.refresh();
9837         }
9838     },
9839
9840
9841 /**
9842  * Sorts the data for this view and refreshes it.
9843  * @param {String} property A property on your JSON objects to sort on
9844  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9845  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9846  */
9847     sort : function(property, dir, sortType){
9848         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9849         if(this.jsonData){
9850             var p = property;
9851             var dsc = dir && dir.toLowerCase() == "desc";
9852             var f = function(o1, o2){
9853                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9854                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9855                 ;
9856                 if(v1 < v2){
9857                     return dsc ? +1 : -1;
9858                 } else if(v1 > v2){
9859                     return dsc ? -1 : +1;
9860                 } else{
9861                     return 0;
9862                 }
9863             };
9864             this.jsonData.sort(f);
9865             this.refresh();
9866             if(this.jsonData != this.snapshot){
9867                 this.snapshot.sort(f);
9868             }
9869         }
9870     }
9871 });/*
9872  * Based on:
9873  * Ext JS Library 1.1.1
9874  * Copyright(c) 2006-2007, Ext JS, LLC.
9875  *
9876  * Originally Released Under LGPL - original licence link has changed is not relivant.
9877  *
9878  * Fork - LGPL
9879  * <script type="text/javascript">
9880  */
9881  
9882
9883 /**
9884  * @class Roo.ColorPalette
9885  * @extends Roo.Component
9886  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9887  * Here's an example of typical usage:
9888  * <pre><code>
9889 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9890 cp.render('my-div');
9891
9892 cp.on('select', function(palette, selColor){
9893     // do something with selColor
9894 });
9895 </code></pre>
9896  * @constructor
9897  * Create a new ColorPalette
9898  * @param {Object} config The config object
9899  */
9900 Roo.ColorPalette = function(config){
9901     Roo.ColorPalette.superclass.constructor.call(this, config);
9902     this.addEvents({
9903         /**
9904              * @event select
9905              * Fires when a color is selected
9906              * @param {ColorPalette} this
9907              * @param {String} color The 6-digit color hex code (without the # symbol)
9908              */
9909         select: true
9910     });
9911
9912     if(this.handler){
9913         this.on("select", this.handler, this.scope, true);
9914     }
9915 };
9916 Roo.extend(Roo.ColorPalette, Roo.Component, {
9917     /**
9918      * @cfg {String} itemCls
9919      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9920      */
9921     itemCls : "x-color-palette",
9922     /**
9923      * @cfg {String} value
9924      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9925      * the hex codes are case-sensitive.
9926      */
9927     value : null,
9928     clickEvent:'click',
9929     // private
9930     ctype: "Roo.ColorPalette",
9931
9932     /**
9933      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9934      */
9935     allowReselect : false,
9936
9937     /**
9938      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9939      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9940      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9941      * of colors with the width setting until the box is symmetrical.</p>
9942      * <p>You can override individual colors if needed:</p>
9943      * <pre><code>
9944 var cp = new Roo.ColorPalette();
9945 cp.colors[0] = "FF0000";  // change the first box to red
9946 </code></pre>
9947
9948 Or you can provide a custom array of your own for complete control:
9949 <pre><code>
9950 var cp = new Roo.ColorPalette();
9951 cp.colors = ["000000", "993300", "333300"];
9952 </code></pre>
9953      * @type Array
9954      */
9955     colors : [
9956         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9957         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9958         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9959         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9960         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9961     ],
9962
9963     // private
9964     onRender : function(container, position){
9965         var t = new Roo.MasterTemplate(
9966             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9967         );
9968         var c = this.colors;
9969         for(var i = 0, len = c.length; i < len; i++){
9970             t.add([c[i]]);
9971         }
9972         var el = document.createElement("div");
9973         el.className = this.itemCls;
9974         t.overwrite(el);
9975         container.dom.insertBefore(el, position);
9976         this.el = Roo.get(el);
9977         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9978         if(this.clickEvent != 'click'){
9979             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9980         }
9981     },
9982
9983     // private
9984     afterRender : function(){
9985         Roo.ColorPalette.superclass.afterRender.call(this);
9986         if(this.value){
9987             var s = this.value;
9988             this.value = null;
9989             this.select(s);
9990         }
9991     },
9992
9993     // private
9994     handleClick : function(e, t){
9995         e.preventDefault();
9996         if(!this.disabled){
9997             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9998             this.select(c.toUpperCase());
9999         }
10000     },
10001
10002     /**
10003      * Selects the specified color in the palette (fires the select event)
10004      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10005      */
10006     select : function(color){
10007         color = color.replace("#", "");
10008         if(color != this.value || this.allowReselect){
10009             var el = this.el;
10010             if(this.value){
10011                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10012             }
10013             el.child("a.color-"+color).addClass("x-color-palette-sel");
10014             this.value = color;
10015             this.fireEvent("select", this, color);
10016         }
10017     }
10018 });/*
10019  * Based on:
10020  * Ext JS Library 1.1.1
10021  * Copyright(c) 2006-2007, Ext JS, LLC.
10022  *
10023  * Originally Released Under LGPL - original licence link has changed is not relivant.
10024  *
10025  * Fork - LGPL
10026  * <script type="text/javascript">
10027  */
10028  
10029 /**
10030  * @class Roo.DatePicker
10031  * @extends Roo.Component
10032  * Simple date picker class.
10033  * @constructor
10034  * Create a new DatePicker
10035  * @param {Object} config The config object
10036  */
10037 Roo.DatePicker = function(config){
10038     Roo.DatePicker.superclass.constructor.call(this, config);
10039
10040     this.value = config && config.value ?
10041                  config.value.clearTime() : new Date().clearTime();
10042
10043     this.addEvents({
10044         /**
10045              * @event select
10046              * Fires when a date is selected
10047              * @param {DatePicker} this
10048              * @param {Date} date The selected date
10049              */
10050         select: true
10051     });
10052
10053     if(this.handler){
10054         this.on("select", this.handler,  this.scope || this);
10055     }
10056     // build the disabledDatesRE
10057     if(!this.disabledDatesRE && this.disabledDates){
10058         var dd = this.disabledDates;
10059         var re = "(?:";
10060         for(var i = 0; i < dd.length; i++){
10061             re += dd[i];
10062             if(i != dd.length-1) re += "|";
10063         }
10064         this.disabledDatesRE = new RegExp(re + ")");
10065     }
10066 };
10067
10068 Roo.extend(Roo.DatePicker, Roo.Component, {
10069     /**
10070      * @cfg {String} todayText
10071      * The text to display on the button that selects the current date (defaults to "Today")
10072      */
10073     todayText : "Today",
10074     /**
10075      * @cfg {String} okText
10076      * The text to display on the ok button
10077      */
10078     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10079     /**
10080      * @cfg {String} cancelText
10081      * The text to display on the cancel button
10082      */
10083     cancelText : "Cancel",
10084     /**
10085      * @cfg {String} todayTip
10086      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10087      */
10088     todayTip : "{0} (Spacebar)",
10089     /**
10090      * @cfg {Date} minDate
10091      * Minimum allowable date (JavaScript date object, defaults to null)
10092      */
10093     minDate : null,
10094     /**
10095      * @cfg {Date} maxDate
10096      * Maximum allowable date (JavaScript date object, defaults to null)
10097      */
10098     maxDate : null,
10099     /**
10100      * @cfg {String} minText
10101      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10102      */
10103     minText : "This date is before the minimum date",
10104     /**
10105      * @cfg {String} maxText
10106      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10107      */
10108     maxText : "This date is after the maximum date",
10109     /**
10110      * @cfg {String} format
10111      * The default date format string which can be overriden for localization support.  The format must be
10112      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10113      */
10114     format : "m/d/y",
10115     /**
10116      * @cfg {Array} disabledDays
10117      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10118      */
10119     disabledDays : null,
10120     /**
10121      * @cfg {String} disabledDaysText
10122      * The tooltip to display when the date falls on a disabled day (defaults to "")
10123      */
10124     disabledDaysText : "",
10125     /**
10126      * @cfg {RegExp} disabledDatesRE
10127      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10128      */
10129     disabledDatesRE : null,
10130     /**
10131      * @cfg {String} disabledDatesText
10132      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10133      */
10134     disabledDatesText : "",
10135     /**
10136      * @cfg {Boolean} constrainToViewport
10137      * True to constrain the date picker to the viewport (defaults to true)
10138      */
10139     constrainToViewport : true,
10140     /**
10141      * @cfg {Array} monthNames
10142      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10143      */
10144     monthNames : Date.monthNames,
10145     /**
10146      * @cfg {Array} dayNames
10147      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10148      */
10149     dayNames : Date.dayNames,
10150     /**
10151      * @cfg {String} nextText
10152      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10153      */
10154     nextText: 'Next Month (Control+Right)',
10155     /**
10156      * @cfg {String} prevText
10157      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10158      */
10159     prevText: 'Previous Month (Control+Left)',
10160     /**
10161      * @cfg {String} monthYearText
10162      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10163      */
10164     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10165     /**
10166      * @cfg {Number} startDay
10167      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10168      */
10169     startDay : 0,
10170     /**
10171      * @cfg {Bool} showClear
10172      * Show a clear button (usefull for date form elements that can be blank.)
10173      */
10174     
10175     showClear: false,
10176     
10177     /**
10178      * Sets the value of the date field
10179      * @param {Date} value The date to set
10180      */
10181     setValue : function(value){
10182         var old = this.value;
10183         this.value = value.clearTime(true);
10184         if(this.el){
10185             this.update(this.value);
10186         }
10187     },
10188
10189     /**
10190      * Gets the current selected value of the date field
10191      * @return {Date} The selected date
10192      */
10193     getValue : function(){
10194         return this.value;
10195     },
10196
10197     // private
10198     focus : function(){
10199         if(this.el){
10200             this.update(this.activeDate);
10201         }
10202     },
10203
10204     // private
10205     onRender : function(container, position){
10206         var m = [
10207              '<table cellspacing="0">',
10208                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10209                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10210         var dn = this.dayNames;
10211         for(var i = 0; i < 7; i++){
10212             var d = this.startDay+i;
10213             if(d > 6){
10214                 d = d-7;
10215             }
10216             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10217         }
10218         m[m.length] = "</tr></thead><tbody><tr>";
10219         for(var i = 0; i < 42; i++) {
10220             if(i % 7 == 0 && i != 0){
10221                 m[m.length] = "</tr><tr>";
10222             }
10223             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10224         }
10225         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10226             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10227
10228         var el = document.createElement("div");
10229         el.className = "x-date-picker";
10230         el.innerHTML = m.join("");
10231
10232         container.dom.insertBefore(el, position);
10233
10234         this.el = Roo.get(el);
10235         this.eventEl = Roo.get(el.firstChild);
10236
10237         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10238             handler: this.showPrevMonth,
10239             scope: this,
10240             preventDefault:true,
10241             stopDefault:true
10242         });
10243
10244         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10245             handler: this.showNextMonth,
10246             scope: this,
10247             preventDefault:true,
10248             stopDefault:true
10249         });
10250
10251         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10252
10253         this.monthPicker = this.el.down('div.x-date-mp');
10254         this.monthPicker.enableDisplayMode('block');
10255         
10256         var kn = new Roo.KeyNav(this.eventEl, {
10257             "left" : function(e){
10258                 e.ctrlKey ?
10259                     this.showPrevMonth() :
10260                     this.update(this.activeDate.add("d", -1));
10261             },
10262
10263             "right" : function(e){
10264                 e.ctrlKey ?
10265                     this.showNextMonth() :
10266                     this.update(this.activeDate.add("d", 1));
10267             },
10268
10269             "up" : function(e){
10270                 e.ctrlKey ?
10271                     this.showNextYear() :
10272                     this.update(this.activeDate.add("d", -7));
10273             },
10274
10275             "down" : function(e){
10276                 e.ctrlKey ?
10277                     this.showPrevYear() :
10278                     this.update(this.activeDate.add("d", 7));
10279             },
10280
10281             "pageUp" : function(e){
10282                 this.showNextMonth();
10283             },
10284
10285             "pageDown" : function(e){
10286                 this.showPrevMonth();
10287             },
10288
10289             "enter" : function(e){
10290                 e.stopPropagation();
10291                 return true;
10292             },
10293
10294             scope : this
10295         });
10296
10297         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10298
10299         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10300
10301         this.el.unselectable();
10302         
10303         this.cells = this.el.select("table.x-date-inner tbody td");
10304         this.textNodes = this.el.query("table.x-date-inner tbody span");
10305
10306         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10307             text: "&#160;",
10308             tooltip: this.monthYearText
10309         });
10310
10311         this.mbtn.on('click', this.showMonthPicker, this);
10312         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10313
10314
10315         var today = (new Date()).dateFormat(this.format);
10316         
10317         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10318         if (this.showClear) {
10319             baseTb.add( new Roo.Toolbar.Fill());
10320         }
10321         baseTb.add({
10322             text: String.format(this.todayText, today),
10323             tooltip: String.format(this.todayTip, today),
10324             handler: this.selectToday,
10325             scope: this
10326         });
10327         
10328         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10329             
10330         //});
10331         if (this.showClear) {
10332             
10333             baseTb.add( new Roo.Toolbar.Fill());
10334             baseTb.add({
10335                 text: '&#160;',
10336                 cls: 'x-btn-icon x-btn-clear',
10337                 handler: function() {
10338                     //this.value = '';
10339                     this.fireEvent("select", this, '');
10340                 },
10341                 scope: this
10342             });
10343         }
10344         
10345         
10346         if(Roo.isIE){
10347             this.el.repaint();
10348         }
10349         this.update(this.value);
10350     },
10351
10352     createMonthPicker : function(){
10353         if(!this.monthPicker.dom.firstChild){
10354             var buf = ['<table border="0" cellspacing="0">'];
10355             for(var i = 0; i < 6; i++){
10356                 buf.push(
10357                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10358                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10359                     i == 0 ?
10360                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10361                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10362                 );
10363             }
10364             buf.push(
10365                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10366                     this.okText,
10367                     '</button><button type="button" class="x-date-mp-cancel">',
10368                     this.cancelText,
10369                     '</button></td></tr>',
10370                 '</table>'
10371             );
10372             this.monthPicker.update(buf.join(''));
10373             this.monthPicker.on('click', this.onMonthClick, this);
10374             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10375
10376             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10377             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10378
10379             this.mpMonths.each(function(m, a, i){
10380                 i += 1;
10381                 if((i%2) == 0){
10382                     m.dom.xmonth = 5 + Math.round(i * .5);
10383                 }else{
10384                     m.dom.xmonth = Math.round((i-1) * .5);
10385                 }
10386             });
10387         }
10388     },
10389
10390     showMonthPicker : function(){
10391         this.createMonthPicker();
10392         var size = this.el.getSize();
10393         this.monthPicker.setSize(size);
10394         this.monthPicker.child('table').setSize(size);
10395
10396         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10397         this.updateMPMonth(this.mpSelMonth);
10398         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10399         this.updateMPYear(this.mpSelYear);
10400
10401         this.monthPicker.slideIn('t', {duration:.2});
10402     },
10403
10404     updateMPYear : function(y){
10405         this.mpyear = y;
10406         var ys = this.mpYears.elements;
10407         for(var i = 1; i <= 10; i++){
10408             var td = ys[i-1], y2;
10409             if((i%2) == 0){
10410                 y2 = y + Math.round(i * .5);
10411                 td.firstChild.innerHTML = y2;
10412                 td.xyear = y2;
10413             }else{
10414                 y2 = y - (5-Math.round(i * .5));
10415                 td.firstChild.innerHTML = y2;
10416                 td.xyear = y2;
10417             }
10418             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10419         }
10420     },
10421
10422     updateMPMonth : function(sm){
10423         this.mpMonths.each(function(m, a, i){
10424             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10425         });
10426     },
10427
10428     selectMPMonth: function(m){
10429         
10430     },
10431
10432     onMonthClick : function(e, t){
10433         e.stopEvent();
10434         var el = new Roo.Element(t), pn;
10435         if(el.is('button.x-date-mp-cancel')){
10436             this.hideMonthPicker();
10437         }
10438         else if(el.is('button.x-date-mp-ok')){
10439             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10440             this.hideMonthPicker();
10441         }
10442         else if(pn = el.up('td.x-date-mp-month', 2)){
10443             this.mpMonths.removeClass('x-date-mp-sel');
10444             pn.addClass('x-date-mp-sel');
10445             this.mpSelMonth = pn.dom.xmonth;
10446         }
10447         else if(pn = el.up('td.x-date-mp-year', 2)){
10448             this.mpYears.removeClass('x-date-mp-sel');
10449             pn.addClass('x-date-mp-sel');
10450             this.mpSelYear = pn.dom.xyear;
10451         }
10452         else if(el.is('a.x-date-mp-prev')){
10453             this.updateMPYear(this.mpyear-10);
10454         }
10455         else if(el.is('a.x-date-mp-next')){
10456             this.updateMPYear(this.mpyear+10);
10457         }
10458     },
10459
10460     onMonthDblClick : function(e, t){
10461         e.stopEvent();
10462         var el = new Roo.Element(t), pn;
10463         if(pn = el.up('td.x-date-mp-month', 2)){
10464             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10465             this.hideMonthPicker();
10466         }
10467         else if(pn = el.up('td.x-date-mp-year', 2)){
10468             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10469             this.hideMonthPicker();
10470         }
10471     },
10472
10473     hideMonthPicker : function(disableAnim){
10474         if(this.monthPicker){
10475             if(disableAnim === true){
10476                 this.monthPicker.hide();
10477             }else{
10478                 this.monthPicker.slideOut('t', {duration:.2});
10479             }
10480         }
10481     },
10482
10483     // private
10484     showPrevMonth : function(e){
10485         this.update(this.activeDate.add("mo", -1));
10486     },
10487
10488     // private
10489     showNextMonth : function(e){
10490         this.update(this.activeDate.add("mo", 1));
10491     },
10492
10493     // private
10494     showPrevYear : function(){
10495         this.update(this.activeDate.add("y", -1));
10496     },
10497
10498     // private
10499     showNextYear : function(){
10500         this.update(this.activeDate.add("y", 1));
10501     },
10502
10503     // private
10504     handleMouseWheel : function(e){
10505         var delta = e.getWheelDelta();
10506         if(delta > 0){
10507             this.showPrevMonth();
10508             e.stopEvent();
10509         } else if(delta < 0){
10510             this.showNextMonth();
10511             e.stopEvent();
10512         }
10513     },
10514
10515     // private
10516     handleDateClick : function(e, t){
10517         e.stopEvent();
10518         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10519             this.setValue(new Date(t.dateValue));
10520             this.fireEvent("select", this, this.value);
10521         }
10522     },
10523
10524     // private
10525     selectToday : function(){
10526         this.setValue(new Date().clearTime());
10527         this.fireEvent("select", this, this.value);
10528     },
10529
10530     // private
10531     update : function(date){
10532         var vd = this.activeDate;
10533         this.activeDate = date;
10534         if(vd && this.el){
10535             var t = date.getTime();
10536             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10537                 this.cells.removeClass("x-date-selected");
10538                 this.cells.each(function(c){
10539                    if(c.dom.firstChild.dateValue == t){
10540                        c.addClass("x-date-selected");
10541                        setTimeout(function(){
10542                             try{c.dom.firstChild.focus();}catch(e){}
10543                        }, 50);
10544                        return false;
10545                    }
10546                 });
10547                 return;
10548             }
10549         }
10550         var days = date.getDaysInMonth();
10551         var firstOfMonth = date.getFirstDateOfMonth();
10552         var startingPos = firstOfMonth.getDay()-this.startDay;
10553
10554         if(startingPos <= this.startDay){
10555             startingPos += 7;
10556         }
10557
10558         var pm = date.add("mo", -1);
10559         var prevStart = pm.getDaysInMonth()-startingPos;
10560
10561         var cells = this.cells.elements;
10562         var textEls = this.textNodes;
10563         days += startingPos;
10564
10565         // convert everything to numbers so it's fast
10566         var day = 86400000;
10567         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10568         var today = new Date().clearTime().getTime();
10569         var sel = date.clearTime().getTime();
10570         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10571         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10572         var ddMatch = this.disabledDatesRE;
10573         var ddText = this.disabledDatesText;
10574         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10575         var ddaysText = this.disabledDaysText;
10576         var format = this.format;
10577
10578         var setCellClass = function(cal, cell){
10579             cell.title = "";
10580             var t = d.getTime();
10581             cell.firstChild.dateValue = t;
10582             if(t == today){
10583                 cell.className += " x-date-today";
10584                 cell.title = cal.todayText;
10585             }
10586             if(t == sel){
10587                 cell.className += " x-date-selected";
10588                 setTimeout(function(){
10589                     try{cell.firstChild.focus();}catch(e){}
10590                 }, 50);
10591             }
10592             // disabling
10593             if(t < min) {
10594                 cell.className = " x-date-disabled";
10595                 cell.title = cal.minText;
10596                 return;
10597             }
10598             if(t > max) {
10599                 cell.className = " x-date-disabled";
10600                 cell.title = cal.maxText;
10601                 return;
10602             }
10603             if(ddays){
10604                 if(ddays.indexOf(d.getDay()) != -1){
10605                     cell.title = ddaysText;
10606                     cell.className = " x-date-disabled";
10607                 }
10608             }
10609             if(ddMatch && format){
10610                 var fvalue = d.dateFormat(format);
10611                 if(ddMatch.test(fvalue)){
10612                     cell.title = ddText.replace("%0", fvalue);
10613                     cell.className = " x-date-disabled";
10614                 }
10615             }
10616         };
10617
10618         var i = 0;
10619         for(; i < startingPos; i++) {
10620             textEls[i].innerHTML = (++prevStart);
10621             d.setDate(d.getDate()+1);
10622             cells[i].className = "x-date-prevday";
10623             setCellClass(this, cells[i]);
10624         }
10625         for(; i < days; i++){
10626             intDay = i - startingPos + 1;
10627             textEls[i].innerHTML = (intDay);
10628             d.setDate(d.getDate()+1);
10629             cells[i].className = "x-date-active";
10630             setCellClass(this, cells[i]);
10631         }
10632         var extraDays = 0;
10633         for(; i < 42; i++) {
10634              textEls[i].innerHTML = (++extraDays);
10635              d.setDate(d.getDate()+1);
10636              cells[i].className = "x-date-nextday";
10637              setCellClass(this, cells[i]);
10638         }
10639
10640         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10641
10642         if(!this.internalRender){
10643             var main = this.el.dom.firstChild;
10644             var w = main.offsetWidth;
10645             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10646             Roo.fly(main).setWidth(w);
10647             this.internalRender = true;
10648             // opera does not respect the auto grow header center column
10649             // then, after it gets a width opera refuses to recalculate
10650             // without a second pass
10651             if(Roo.isOpera && !this.secondPass){
10652                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10653                 this.secondPass = true;
10654                 this.update.defer(10, this, [date]);
10655             }
10656         }
10657     }
10658 });/*
10659  * Based on:
10660  * Ext JS Library 1.1.1
10661  * Copyright(c) 2006-2007, Ext JS, LLC.
10662  *
10663  * Originally Released Under LGPL - original licence link has changed is not relivant.
10664  *
10665  * Fork - LGPL
10666  * <script type="text/javascript">
10667  */
10668 /**
10669  * @class Roo.TabPanel
10670  * @extends Roo.util.Observable
10671  * A lightweight tab container.
10672  * <br><br>
10673  * Usage:
10674  * <pre><code>
10675 // basic tabs 1, built from existing content
10676 var tabs = new Roo.TabPanel("tabs1");
10677 tabs.addTab("script", "View Script");
10678 tabs.addTab("markup", "View Markup");
10679 tabs.activate("script");
10680
10681 // more advanced tabs, built from javascript
10682 var jtabs = new Roo.TabPanel("jtabs");
10683 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10684
10685 // set up the UpdateManager
10686 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10687 var updater = tab2.getUpdateManager();
10688 updater.setDefaultUrl("ajax1.htm");
10689 tab2.on('activate', updater.refresh, updater, true);
10690
10691 // Use setUrl for Ajax loading
10692 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10693 tab3.setUrl("ajax2.htm", null, true);
10694
10695 // Disabled tab
10696 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10697 tab4.disable();
10698
10699 jtabs.activate("jtabs-1");
10700  * </code></pre>
10701  * @constructor
10702  * Create a new TabPanel.
10703  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10704  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10705  */
10706 Roo.TabPanel = function(container, config){
10707     /**
10708     * The container element for this TabPanel.
10709     * @type Roo.Element
10710     */
10711     this.el = Roo.get(container, true);
10712     if(config){
10713         if(typeof config == "boolean"){
10714             this.tabPosition = config ? "bottom" : "top";
10715         }else{
10716             Roo.apply(this, config);
10717         }
10718     }
10719     if(this.tabPosition == "bottom"){
10720         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10721         this.el.addClass("x-tabs-bottom");
10722     }
10723     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10724     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10725     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10726     if(Roo.isIE){
10727         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10728     }
10729     if(this.tabPosition != "bottom"){
10730     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10731      * @type Roo.Element
10732      */
10733       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10734       this.el.addClass("x-tabs-top");
10735     }
10736     this.items = [];
10737
10738     this.bodyEl.setStyle("position", "relative");
10739
10740     this.active = null;
10741     this.activateDelegate = this.activate.createDelegate(this);
10742
10743     this.addEvents({
10744         /**
10745          * @event tabchange
10746          * Fires when the active tab changes
10747          * @param {Roo.TabPanel} this
10748          * @param {Roo.TabPanelItem} activePanel The new active tab
10749          */
10750         "tabchange": true,
10751         /**
10752          * @event beforetabchange
10753          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10754          * @param {Roo.TabPanel} this
10755          * @param {Object} e Set cancel to true on this object to cancel the tab change
10756          * @param {Roo.TabPanelItem} tab The tab being changed to
10757          */
10758         "beforetabchange" : true
10759     });
10760
10761     Roo.EventManager.onWindowResize(this.onResize, this);
10762     this.cpad = this.el.getPadding("lr");
10763     this.hiddenCount = 0;
10764
10765     Roo.TabPanel.superclass.constructor.call(this);
10766 };
10767
10768 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10769         /*
10770          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10771          */
10772     tabPosition : "top",
10773         /*
10774          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10775          */
10776     currentTabWidth : 0,
10777         /*
10778          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10779          */
10780     minTabWidth : 40,
10781         /*
10782          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10783          */
10784     maxTabWidth : 250,
10785         /*
10786          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10787          */
10788     preferredTabWidth : 175,
10789         /*
10790          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10791          */
10792     resizeTabs : false,
10793         /*
10794          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10795          */
10796     monitorResize : true,
10797
10798     /**
10799      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10800      * @param {String} id The id of the div to use <b>or create</b>
10801      * @param {String} text The text for the tab
10802      * @param {String} content (optional) Content to put in the TabPanelItem body
10803      * @param {Boolean} closable (optional) True to create a close icon on the tab
10804      * @return {Roo.TabPanelItem} The created TabPanelItem
10805      */
10806     addTab : function(id, text, content, closable){
10807         var item = new Roo.TabPanelItem(this, id, text, closable);
10808         this.addTabItem(item);
10809         if(content){
10810             item.setContent(content);
10811         }
10812         return item;
10813     },
10814
10815     /**
10816      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10817      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10818      * @return {Roo.TabPanelItem}
10819      */
10820     getTab : function(id){
10821         return this.items[id];
10822     },
10823
10824     /**
10825      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10826      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10827      */
10828     hideTab : function(id){
10829         var t = this.items[id];
10830         if(!t.isHidden()){
10831            t.setHidden(true);
10832            this.hiddenCount++;
10833            this.autoSizeTabs();
10834         }
10835     },
10836
10837     /**
10838      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10839      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10840      */
10841     unhideTab : function(id){
10842         var t = this.items[id];
10843         if(t.isHidden()){
10844            t.setHidden(false);
10845            this.hiddenCount--;
10846            this.autoSizeTabs();
10847         }
10848     },
10849
10850     /**
10851      * Adds an existing {@link Roo.TabPanelItem}.
10852      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10853      */
10854     addTabItem : function(item){
10855         this.items[item.id] = item;
10856         this.items.push(item);
10857         if(this.resizeTabs){
10858            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10859            this.autoSizeTabs();
10860         }else{
10861             item.autoSize();
10862         }
10863     },
10864
10865     /**
10866      * Removes a {@link Roo.TabPanelItem}.
10867      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10868      */
10869     removeTab : function(id){
10870         var items = this.items;
10871         var tab = items[id];
10872         if(!tab) { return; }
10873         var index = items.indexOf(tab);
10874         if(this.active == tab && items.length > 1){
10875             var newTab = this.getNextAvailable(index);
10876             if(newTab) {
10877                 newTab.activate();
10878             }
10879         }
10880         this.stripEl.dom.removeChild(tab.pnode.dom);
10881         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10882             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10883         }
10884         items.splice(index, 1);
10885         delete this.items[tab.id];
10886         tab.fireEvent("close", tab);
10887         tab.purgeListeners();
10888         this.autoSizeTabs();
10889     },
10890
10891     getNextAvailable : function(start){
10892         var items = this.items;
10893         var index = start;
10894         // look for a next tab that will slide over to
10895         // replace the one being removed
10896         while(index < items.length){
10897             var item = items[++index];
10898             if(item && !item.isHidden()){
10899                 return item;
10900             }
10901         }
10902         // if one isn't found select the previous tab (on the left)
10903         index = start;
10904         while(index >= 0){
10905             var item = items[--index];
10906             if(item && !item.isHidden()){
10907                 return item;
10908             }
10909         }
10910         return null;
10911     },
10912
10913     /**
10914      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10915      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10916      */
10917     disableTab : function(id){
10918         var tab = this.items[id];
10919         if(tab && this.active != tab){
10920             tab.disable();
10921         }
10922     },
10923
10924     /**
10925      * Enables a {@link Roo.TabPanelItem} that is disabled.
10926      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10927      */
10928     enableTab : function(id){
10929         var tab = this.items[id];
10930         tab.enable();
10931     },
10932
10933     /**
10934      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10935      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10936      * @return {Roo.TabPanelItem} The TabPanelItem.
10937      */
10938     activate : function(id){
10939         var tab = this.items[id];
10940         if(!tab){
10941             return null;
10942         }
10943         if(tab == this.active || tab.disabled){
10944             return tab;
10945         }
10946         var e = {};
10947         this.fireEvent("beforetabchange", this, e, tab);
10948         if(e.cancel !== true && !tab.disabled){
10949             if(this.active){
10950                 this.active.hide();
10951             }
10952             this.active = this.items[id];
10953             this.active.show();
10954             this.fireEvent("tabchange", this, this.active);
10955         }
10956         return tab;
10957     },
10958
10959     /**
10960      * Gets the active {@link Roo.TabPanelItem}.
10961      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10962      */
10963     getActiveTab : function(){
10964         return this.active;
10965     },
10966
10967     /**
10968      * Updates the tab body element to fit the height of the container element
10969      * for overflow scrolling
10970      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10971      */
10972     syncHeight : function(targetHeight){
10973         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10974         var bm = this.bodyEl.getMargins();
10975         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10976         this.bodyEl.setHeight(newHeight);
10977         return newHeight;
10978     },
10979
10980     onResize : function(){
10981         if(this.monitorResize){
10982             this.autoSizeTabs();
10983         }
10984     },
10985
10986     /**
10987      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10988      */
10989     beginUpdate : function(){
10990         this.updating = true;
10991     },
10992
10993     /**
10994      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10995      */
10996     endUpdate : function(){
10997         this.updating = false;
10998         this.autoSizeTabs();
10999     },
11000
11001     /**
11002      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11003      */
11004     autoSizeTabs : function(){
11005         var count = this.items.length;
11006         var vcount = count - this.hiddenCount;
11007         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11008         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11009         var availWidth = Math.floor(w / vcount);
11010         var b = this.stripBody;
11011         if(b.getWidth() > w){
11012             var tabs = this.items;
11013             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11014             if(availWidth < this.minTabWidth){
11015                 /*if(!this.sleft){    // incomplete scrolling code
11016                     this.createScrollButtons();
11017                 }
11018                 this.showScroll();
11019                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11020             }
11021         }else{
11022             if(this.currentTabWidth < this.preferredTabWidth){
11023                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11024             }
11025         }
11026     },
11027
11028     /**
11029      * Returns the number of tabs in this TabPanel.
11030      * @return {Number}
11031      */
11032      getCount : function(){
11033          return this.items.length;
11034      },
11035
11036     /**
11037      * Resizes all the tabs to the passed width
11038      * @param {Number} The new width
11039      */
11040     setTabWidth : function(width){
11041         this.currentTabWidth = width;
11042         for(var i = 0, len = this.items.length; i < len; i++) {
11043                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11044         }
11045     },
11046
11047     /**
11048      * Destroys this TabPanel
11049      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11050      */
11051     destroy : function(removeEl){
11052         Roo.EventManager.removeResizeListener(this.onResize, this);
11053         for(var i = 0, len = this.items.length; i < len; i++){
11054             this.items[i].purgeListeners();
11055         }
11056         if(removeEl === true){
11057             this.el.update("");
11058             this.el.remove();
11059         }
11060     }
11061 });
11062
11063 /**
11064  * @class Roo.TabPanelItem
11065  * @extends Roo.util.Observable
11066  * Represents an individual item (tab plus body) in a TabPanel.
11067  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11068  * @param {String} id The id of this TabPanelItem
11069  * @param {String} text The text for the tab of this TabPanelItem
11070  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11071  */
11072 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11073     /**
11074      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11075      * @type Roo.TabPanel
11076      */
11077     this.tabPanel = tabPanel;
11078     /**
11079      * The id for this TabPanelItem
11080      * @type String
11081      */
11082     this.id = id;
11083     /** @private */
11084     this.disabled = false;
11085     /** @private */
11086     this.text = text;
11087     /** @private */
11088     this.loaded = false;
11089     this.closable = closable;
11090
11091     /**
11092      * The body element for this TabPanelItem.
11093      * @type Roo.Element
11094      */
11095     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11096     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11097     this.bodyEl.setStyle("display", "block");
11098     this.bodyEl.setStyle("zoom", "1");
11099     this.hideAction();
11100
11101     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11102     /** @private */
11103     this.el = Roo.get(els.el, true);
11104     this.inner = Roo.get(els.inner, true);
11105     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11106     this.pnode = Roo.get(els.el.parentNode, true);
11107     this.el.on("mousedown", this.onTabMouseDown, this);
11108     this.el.on("click", this.onTabClick, this);
11109     /** @private */
11110     if(closable){
11111         var c = Roo.get(els.close, true);
11112         c.dom.title = this.closeText;
11113         c.addClassOnOver("close-over");
11114         c.on("click", this.closeClick, this);
11115      }
11116
11117     this.addEvents({
11118          /**
11119          * @event activate
11120          * Fires when this tab becomes the active tab.
11121          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11122          * @param {Roo.TabPanelItem} this
11123          */
11124         "activate": true,
11125         /**
11126          * @event beforeclose
11127          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11128          * @param {Roo.TabPanelItem} this
11129          * @param {Object} e Set cancel to true on this object to cancel the close.
11130          */
11131         "beforeclose": true,
11132         /**
11133          * @event close
11134          * Fires when this tab is closed.
11135          * @param {Roo.TabPanelItem} this
11136          */
11137          "close": true,
11138         /**
11139          * @event deactivate
11140          * Fires when this tab is no longer the active tab.
11141          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11142          * @param {Roo.TabPanelItem} this
11143          */
11144          "deactivate" : true
11145     });
11146     this.hidden = false;
11147
11148     Roo.TabPanelItem.superclass.constructor.call(this);
11149 };
11150
11151 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11152     purgeListeners : function(){
11153        Roo.util.Observable.prototype.purgeListeners.call(this);
11154        this.el.removeAllListeners();
11155     },
11156     /**
11157      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11158      */
11159     show : function(){
11160         this.pnode.addClass("on");
11161         this.showAction();
11162         if(Roo.isOpera){
11163             this.tabPanel.stripWrap.repaint();
11164         }
11165         this.fireEvent("activate", this.tabPanel, this);
11166     },
11167
11168     /**
11169      * Returns true if this tab is the active tab.
11170      * @return {Boolean}
11171      */
11172     isActive : function(){
11173         return this.tabPanel.getActiveTab() == this;
11174     },
11175
11176     /**
11177      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11178      */
11179     hide : function(){
11180         this.pnode.removeClass("on");
11181         this.hideAction();
11182         this.fireEvent("deactivate", this.tabPanel, this);
11183     },
11184
11185     hideAction : function(){
11186         this.bodyEl.hide();
11187         this.bodyEl.setStyle("position", "absolute");
11188         this.bodyEl.setLeft("-20000px");
11189         this.bodyEl.setTop("-20000px");
11190     },
11191
11192     showAction : function(){
11193         this.bodyEl.setStyle("position", "relative");
11194         this.bodyEl.setTop("");
11195         this.bodyEl.setLeft("");
11196         this.bodyEl.show();
11197     },
11198
11199     /**
11200      * Set the tooltip for the tab.
11201      * @param {String} tooltip The tab's tooltip
11202      */
11203     setTooltip : function(text){
11204         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11205             this.textEl.dom.qtip = text;
11206             this.textEl.dom.removeAttribute('title');
11207         }else{
11208             this.textEl.dom.title = text;
11209         }
11210     },
11211
11212     onTabClick : function(e){
11213         e.preventDefault();
11214         this.tabPanel.activate(this.id);
11215     },
11216
11217     onTabMouseDown : function(e){
11218         e.preventDefault();
11219         this.tabPanel.activate(this.id);
11220     },
11221
11222     getWidth : function(){
11223         return this.inner.getWidth();
11224     },
11225
11226     setWidth : function(width){
11227         var iwidth = width - this.pnode.getPadding("lr");
11228         this.inner.setWidth(iwidth);
11229         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11230         this.pnode.setWidth(width);
11231     },
11232
11233     /**
11234      * Show or hide the tab
11235      * @param {Boolean} hidden True to hide or false to show.
11236      */
11237     setHidden : function(hidden){
11238         this.hidden = hidden;
11239         this.pnode.setStyle("display", hidden ? "none" : "");
11240     },
11241
11242     /**
11243      * Returns true if this tab is "hidden"
11244      * @return {Boolean}
11245      */
11246     isHidden : function(){
11247         return this.hidden;
11248     },
11249
11250     /**
11251      * Returns the text for this tab
11252      * @return {String}
11253      */
11254     getText : function(){
11255         return this.text;
11256     },
11257
11258     autoSize : function(){
11259         //this.el.beginMeasure();
11260         this.textEl.setWidth(1);
11261         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11262         //this.el.endMeasure();
11263     },
11264
11265     /**
11266      * Sets the text for the tab (Note: this also sets the tooltip text)
11267      * @param {String} text The tab's text and tooltip
11268      */
11269     setText : function(text){
11270         this.text = text;
11271         this.textEl.update(text);
11272         this.setTooltip(text);
11273         if(!this.tabPanel.resizeTabs){
11274             this.autoSize();
11275         }
11276     },
11277     /**
11278      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11279      */
11280     activate : function(){
11281         this.tabPanel.activate(this.id);
11282     },
11283
11284     /**
11285      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11286      */
11287     disable : function(){
11288         if(this.tabPanel.active != this){
11289             this.disabled = true;
11290             this.pnode.addClass("disabled");
11291         }
11292     },
11293
11294     /**
11295      * Enables this TabPanelItem if it was previously disabled.
11296      */
11297     enable : function(){
11298         this.disabled = false;
11299         this.pnode.removeClass("disabled");
11300     },
11301
11302     /**
11303      * Sets the content for this TabPanelItem.
11304      * @param {String} content The content
11305      * @param {Boolean} loadScripts true to look for and load scripts
11306      */
11307     setContent : function(content, loadScripts){
11308         this.bodyEl.update(content, loadScripts);
11309     },
11310
11311     /**
11312      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11313      * @return {Roo.UpdateManager} The UpdateManager
11314      */
11315     getUpdateManager : function(){
11316         return this.bodyEl.getUpdateManager();
11317     },
11318
11319     /**
11320      * Set a URL to be used to load the content for this TabPanelItem.
11321      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11322      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11323      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11324      * @return {Roo.UpdateManager} The UpdateManager
11325      */
11326     setUrl : function(url, params, loadOnce){
11327         if(this.refreshDelegate){
11328             this.un('activate', this.refreshDelegate);
11329         }
11330         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11331         this.on("activate", this.refreshDelegate);
11332         return this.bodyEl.getUpdateManager();
11333     },
11334
11335     /** @private */
11336     _handleRefresh : function(url, params, loadOnce){
11337         if(!loadOnce || !this.loaded){
11338             var updater = this.bodyEl.getUpdateManager();
11339             updater.update(url, params, this._setLoaded.createDelegate(this));
11340         }
11341     },
11342
11343     /**
11344      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11345      *   Will fail silently if the setUrl method has not been called.
11346      *   This does not activate the panel, just updates its content.
11347      */
11348     refresh : function(){
11349         if(this.refreshDelegate){
11350            this.loaded = false;
11351            this.refreshDelegate();
11352         }
11353     },
11354
11355     /** @private */
11356     _setLoaded : function(){
11357         this.loaded = true;
11358     },
11359
11360     /** @private */
11361     closeClick : function(e){
11362         var o = {};
11363         e.stopEvent();
11364         this.fireEvent("beforeclose", this, o);
11365         if(o.cancel !== true){
11366             this.tabPanel.removeTab(this.id);
11367         }
11368     },
11369     /**
11370      * The text displayed in the tooltip for the close icon.
11371      * @type String
11372      */
11373     closeText : "Close this tab"
11374 });
11375
11376 /** @private */
11377 Roo.TabPanel.prototype.createStrip = function(container){
11378     var strip = document.createElement("div");
11379     strip.className = "x-tabs-wrap";
11380     container.appendChild(strip);
11381     return strip;
11382 };
11383 /** @private */
11384 Roo.TabPanel.prototype.createStripList = function(strip){
11385     // div wrapper for retard IE
11386     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11387     return strip.firstChild.firstChild.firstChild.firstChild;
11388 };
11389 /** @private */
11390 Roo.TabPanel.prototype.createBody = function(container){
11391     var body = document.createElement("div");
11392     Roo.id(body, "tab-body");
11393     Roo.fly(body).addClass("x-tabs-body");
11394     container.appendChild(body);
11395     return body;
11396 };
11397 /** @private */
11398 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11399     var body = Roo.getDom(id);
11400     if(!body){
11401         body = document.createElement("div");
11402         body.id = id;
11403     }
11404     Roo.fly(body).addClass("x-tabs-item-body");
11405     bodyEl.insertBefore(body, bodyEl.firstChild);
11406     return body;
11407 };
11408 /** @private */
11409 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11410     var td = document.createElement("td");
11411     stripEl.appendChild(td);
11412     if(closable){
11413         td.className = "x-tabs-closable";
11414         if(!this.closeTpl){
11415             this.closeTpl = new Roo.Template(
11416                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11417                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11418                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11419             );
11420         }
11421         var el = this.closeTpl.overwrite(td, {"text": text});
11422         var close = el.getElementsByTagName("div")[0];
11423         var inner = el.getElementsByTagName("em")[0];
11424         return {"el": el, "close": close, "inner": inner};
11425     } else {
11426         if(!this.tabTpl){
11427             this.tabTpl = new Roo.Template(
11428                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11429                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11430             );
11431         }
11432         var el = this.tabTpl.overwrite(td, {"text": text});
11433         var inner = el.getElementsByTagName("em")[0];
11434         return {"el": el, "inner": inner};
11435     }
11436 };/*
11437  * Based on:
11438  * Ext JS Library 1.1.1
11439  * Copyright(c) 2006-2007, Ext JS, LLC.
11440  *
11441  * Originally Released Under LGPL - original licence link has changed is not relivant.
11442  *
11443  * Fork - LGPL
11444  * <script type="text/javascript">
11445  */
11446
11447 /**
11448  * @class Roo.Button
11449  * @extends Roo.util.Observable
11450  * Simple Button class
11451  * @cfg {String} text The button text
11452  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11453  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11454  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11455  * @cfg {Object} scope The scope of the handler
11456  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11457  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11458  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11459  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11460  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11461  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11462    applies if enableToggle = true)
11463  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11464  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11465   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11466  * @constructor
11467  * Create a new button
11468  * @param {Object} config The config object
11469  */
11470 Roo.Button = function(renderTo, config)
11471 {
11472     if (!config) {
11473         config = renderTo;
11474         renderTo = config.renderTo || false;
11475     }
11476     
11477     Roo.apply(this, config);
11478     this.addEvents({
11479         /**
11480              * @event click
11481              * Fires when this button is clicked
11482              * @param {Button} this
11483              * @param {EventObject} e The click event
11484              */
11485             "click" : true,
11486         /**
11487              * @event toggle
11488              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11489              * @param {Button} this
11490              * @param {Boolean} pressed
11491              */
11492             "toggle" : true,
11493         /**
11494              * @event mouseover
11495              * Fires when the mouse hovers over the button
11496              * @param {Button} this
11497              * @param {Event} e The event object
11498              */
11499         'mouseover' : true,
11500         /**
11501              * @event mouseout
11502              * Fires when the mouse exits the button
11503              * @param {Button} this
11504              * @param {Event} e The event object
11505              */
11506         'mouseout': true,
11507          /**
11508              * @event render
11509              * Fires when the button is rendered
11510              * @param {Button} this
11511              */
11512         'render': true
11513     });
11514     if(this.menu){
11515         this.menu = Roo.menu.MenuMgr.get(this.menu);
11516     }
11517     // register listeners first!!  - so render can be captured..
11518     Roo.util.Observable.call(this);
11519     if(renderTo){
11520         this.render(renderTo);
11521     }
11522     
11523   
11524 };
11525
11526 Roo.extend(Roo.Button, Roo.util.Observable, {
11527     /**
11528      * 
11529      */
11530     
11531     /**
11532      * Read-only. True if this button is hidden
11533      * @type Boolean
11534      */
11535     hidden : false,
11536     /**
11537      * Read-only. True if this button is disabled
11538      * @type Boolean
11539      */
11540     disabled : false,
11541     /**
11542      * Read-only. True if this button is pressed (only if enableToggle = true)
11543      * @type Boolean
11544      */
11545     pressed : false,
11546
11547     /**
11548      * @cfg {Number} tabIndex 
11549      * The DOM tabIndex for this button (defaults to undefined)
11550      */
11551     tabIndex : undefined,
11552
11553     /**
11554      * @cfg {Boolean} enableToggle
11555      * True to enable pressed/not pressed toggling (defaults to false)
11556      */
11557     enableToggle: false,
11558     /**
11559      * @cfg {Mixed} menu
11560      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11561      */
11562     menu : undefined,
11563     /**
11564      * @cfg {String} menuAlign
11565      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11566      */
11567     menuAlign : "tl-bl?",
11568
11569     /**
11570      * @cfg {String} iconCls
11571      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11572      */
11573     iconCls : undefined,
11574     /**
11575      * @cfg {String} type
11576      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11577      */
11578     type : 'button',
11579
11580     // private
11581     menuClassTarget: 'tr',
11582
11583     /**
11584      * @cfg {String} clickEvent
11585      * The type of event to map to the button's event handler (defaults to 'click')
11586      */
11587     clickEvent : 'click',
11588
11589     /**
11590      * @cfg {Boolean} handleMouseEvents
11591      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11592      */
11593     handleMouseEvents : true,
11594
11595     /**
11596      * @cfg {String} tooltipType
11597      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11598      */
11599     tooltipType : 'qtip',
11600
11601     /**
11602      * @cfg {String} cls
11603      * A CSS class to apply to the button's main element.
11604      */
11605     
11606     /**
11607      * @cfg {Roo.Template} template (Optional)
11608      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11609      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11610      * require code modifications if required elements (e.g. a button) aren't present.
11611      */
11612
11613     // private
11614     render : function(renderTo){
11615         var btn;
11616         if(this.hideParent){
11617             this.parentEl = Roo.get(renderTo);
11618         }
11619         if(!this.dhconfig){
11620             if(!this.template){
11621                 if(!Roo.Button.buttonTemplate){
11622                     // hideous table template
11623                     Roo.Button.buttonTemplate = new Roo.Template(
11624                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11625                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11626                         "</tr></tbody></table>");
11627                 }
11628                 this.template = Roo.Button.buttonTemplate;
11629             }
11630             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11631             var btnEl = btn.child("button:first");
11632             btnEl.on('focus', this.onFocus, this);
11633             btnEl.on('blur', this.onBlur, this);
11634             if(this.cls){
11635                 btn.addClass(this.cls);
11636             }
11637             if(this.icon){
11638                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11639             }
11640             if(this.iconCls){
11641                 btnEl.addClass(this.iconCls);
11642                 if(!this.cls){
11643                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11644                 }
11645             }
11646             if(this.tabIndex !== undefined){
11647                 btnEl.dom.tabIndex = this.tabIndex;
11648             }
11649             if(this.tooltip){
11650                 if(typeof this.tooltip == 'object'){
11651                     Roo.QuickTips.tips(Roo.apply({
11652                           target: btnEl.id
11653                     }, this.tooltip));
11654                 } else {
11655                     btnEl.dom[this.tooltipType] = this.tooltip;
11656                 }
11657             }
11658         }else{
11659             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11660         }
11661         this.el = btn;
11662         if(this.id){
11663             this.el.dom.id = this.el.id = this.id;
11664         }
11665         if(this.menu){
11666             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11667             this.menu.on("show", this.onMenuShow, this);
11668             this.menu.on("hide", this.onMenuHide, this);
11669         }
11670         btn.addClass("x-btn");
11671         if(Roo.isIE && !Roo.isIE7){
11672             this.autoWidth.defer(1, this);
11673         }else{
11674             this.autoWidth();
11675         }
11676         if(this.handleMouseEvents){
11677             btn.on("mouseover", this.onMouseOver, this);
11678             btn.on("mouseout", this.onMouseOut, this);
11679             btn.on("mousedown", this.onMouseDown, this);
11680         }
11681         btn.on(this.clickEvent, this.onClick, this);
11682         //btn.on("mouseup", this.onMouseUp, this);
11683         if(this.hidden){
11684             this.hide();
11685         }
11686         if(this.disabled){
11687             this.disable();
11688         }
11689         Roo.ButtonToggleMgr.register(this);
11690         if(this.pressed){
11691             this.el.addClass("x-btn-pressed");
11692         }
11693         if(this.repeat){
11694             var repeater = new Roo.util.ClickRepeater(btn,
11695                 typeof this.repeat == "object" ? this.repeat : {}
11696             );
11697             repeater.on("click", this.onClick,  this);
11698         }
11699         
11700         this.fireEvent('render', this);
11701         
11702     },
11703     /**
11704      * Returns the button's underlying element
11705      * @return {Roo.Element} The element
11706      */
11707     getEl : function(){
11708         return this.el;  
11709     },
11710     
11711     /**
11712      * Destroys this Button and removes any listeners.
11713      */
11714     destroy : function(){
11715         Roo.ButtonToggleMgr.unregister(this);
11716         this.el.removeAllListeners();
11717         this.purgeListeners();
11718         this.el.remove();
11719     },
11720
11721     // private
11722     autoWidth : function(){
11723         if(this.el){
11724             this.el.setWidth("auto");
11725             if(Roo.isIE7 && Roo.isStrict){
11726                 var ib = this.el.child('button');
11727                 if(ib && ib.getWidth() > 20){
11728                     ib.clip();
11729                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11730                 }
11731             }
11732             if(this.minWidth){
11733                 if(this.hidden){
11734                     this.el.beginMeasure();
11735                 }
11736                 if(this.el.getWidth() < this.minWidth){
11737                     this.el.setWidth(this.minWidth);
11738                 }
11739                 if(this.hidden){
11740                     this.el.endMeasure();
11741                 }
11742             }
11743         }
11744     },
11745
11746     /**
11747      * Assigns this button's click handler
11748      * @param {Function} handler The function to call when the button is clicked
11749      * @param {Object} scope (optional) Scope for the function passed in
11750      */
11751     setHandler : function(handler, scope){
11752         this.handler = handler;
11753         this.scope = scope;  
11754     },
11755     
11756     /**
11757      * Sets this button's text
11758      * @param {String} text The button text
11759      */
11760     setText : function(text){
11761         this.text = text;
11762         if(this.el){
11763             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11764         }
11765         this.autoWidth();
11766     },
11767     
11768     /**
11769      * Gets the text for this button
11770      * @return {String} The button text
11771      */
11772     getText : function(){
11773         return this.text;  
11774     },
11775     
11776     /**
11777      * Show this button
11778      */
11779     show: function(){
11780         this.hidden = false;
11781         if(this.el){
11782             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11783         }
11784     },
11785     
11786     /**
11787      * Hide this button
11788      */
11789     hide: function(){
11790         this.hidden = true;
11791         if(this.el){
11792             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11793         }
11794     },
11795     
11796     /**
11797      * Convenience function for boolean show/hide
11798      * @param {Boolean} visible True to show, false to hide
11799      */
11800     setVisible: function(visible){
11801         if(visible) {
11802             this.show();
11803         }else{
11804             this.hide();
11805         }
11806     },
11807     
11808     /**
11809      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11810      * @param {Boolean} state (optional) Force a particular state
11811      */
11812     toggle : function(state){
11813         state = state === undefined ? !this.pressed : state;
11814         if(state != this.pressed){
11815             if(state){
11816                 this.el.addClass("x-btn-pressed");
11817                 this.pressed = true;
11818                 this.fireEvent("toggle", this, true);
11819             }else{
11820                 this.el.removeClass("x-btn-pressed");
11821                 this.pressed = false;
11822                 this.fireEvent("toggle", this, false);
11823             }
11824             if(this.toggleHandler){
11825                 this.toggleHandler.call(this.scope || this, this, state);
11826             }
11827         }
11828     },
11829     
11830     /**
11831      * Focus the button
11832      */
11833     focus : function(){
11834         this.el.child('button:first').focus();
11835     },
11836     
11837     /**
11838      * Disable this button
11839      */
11840     disable : function(){
11841         if(this.el){
11842             this.el.addClass("x-btn-disabled");
11843         }
11844         this.disabled = true;
11845     },
11846     
11847     /**
11848      * Enable this button
11849      */
11850     enable : function(){
11851         if(this.el){
11852             this.el.removeClass("x-btn-disabled");
11853         }
11854         this.disabled = false;
11855     },
11856
11857     /**
11858      * Convenience function for boolean enable/disable
11859      * @param {Boolean} enabled True to enable, false to disable
11860      */
11861     setDisabled : function(v){
11862         this[v !== true ? "enable" : "disable"]();
11863     },
11864
11865     // private
11866     onClick : function(e){
11867         if(e){
11868             e.preventDefault();
11869         }
11870         if(e.button != 0){
11871             return;
11872         }
11873         if(!this.disabled){
11874             if(this.enableToggle){
11875                 this.toggle();
11876             }
11877             if(this.menu && !this.menu.isVisible()){
11878                 this.menu.show(this.el, this.menuAlign);
11879             }
11880             this.fireEvent("click", this, e);
11881             if(this.handler){
11882                 this.el.removeClass("x-btn-over");
11883                 this.handler.call(this.scope || this, this, e);
11884             }
11885         }
11886     },
11887     // private
11888     onMouseOver : function(e){
11889         if(!this.disabled){
11890             this.el.addClass("x-btn-over");
11891             this.fireEvent('mouseover', this, e);
11892         }
11893     },
11894     // private
11895     onMouseOut : function(e){
11896         if(!e.within(this.el,  true)){
11897             this.el.removeClass("x-btn-over");
11898             this.fireEvent('mouseout', this, e);
11899         }
11900     },
11901     // private
11902     onFocus : function(e){
11903         if(!this.disabled){
11904             this.el.addClass("x-btn-focus");
11905         }
11906     },
11907     // private
11908     onBlur : function(e){
11909         this.el.removeClass("x-btn-focus");
11910     },
11911     // private
11912     onMouseDown : function(e){
11913         if(!this.disabled && e.button == 0){
11914             this.el.addClass("x-btn-click");
11915             Roo.get(document).on('mouseup', this.onMouseUp, this);
11916         }
11917     },
11918     // private
11919     onMouseUp : function(e){
11920         if(e.button == 0){
11921             this.el.removeClass("x-btn-click");
11922             Roo.get(document).un('mouseup', this.onMouseUp, this);
11923         }
11924     },
11925     // private
11926     onMenuShow : function(e){
11927         this.el.addClass("x-btn-menu-active");
11928     },
11929     // private
11930     onMenuHide : function(e){
11931         this.el.removeClass("x-btn-menu-active");
11932     }   
11933 });
11934
11935 // Private utility class used by Button
11936 Roo.ButtonToggleMgr = function(){
11937    var groups = {};
11938    
11939    function toggleGroup(btn, state){
11940        if(state){
11941            var g = groups[btn.toggleGroup];
11942            for(var i = 0, l = g.length; i < l; i++){
11943                if(g[i] != btn){
11944                    g[i].toggle(false);
11945                }
11946            }
11947        }
11948    }
11949    
11950    return {
11951        register : function(btn){
11952            if(!btn.toggleGroup){
11953                return;
11954            }
11955            var g = groups[btn.toggleGroup];
11956            if(!g){
11957                g = groups[btn.toggleGroup] = [];
11958            }
11959            g.push(btn);
11960            btn.on("toggle", toggleGroup);
11961        },
11962        
11963        unregister : function(btn){
11964            if(!btn.toggleGroup){
11965                return;
11966            }
11967            var g = groups[btn.toggleGroup];
11968            if(g){
11969                g.remove(btn);
11970                btn.un("toggle", toggleGroup);
11971            }
11972        }
11973    };
11974 }();/*
11975  * Based on:
11976  * Ext JS Library 1.1.1
11977  * Copyright(c) 2006-2007, Ext JS, LLC.
11978  *
11979  * Originally Released Under LGPL - original licence link has changed is not relivant.
11980  *
11981  * Fork - LGPL
11982  * <script type="text/javascript">
11983  */
11984  
11985 /**
11986  * @class Roo.SplitButton
11987  * @extends Roo.Button
11988  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11989  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11990  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11991  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11992  * @cfg {String} arrowTooltip The title attribute of the arrow
11993  * @constructor
11994  * Create a new menu button
11995  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11996  * @param {Object} config The config object
11997  */
11998 Roo.SplitButton = function(renderTo, config){
11999     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12000     /**
12001      * @event arrowclick
12002      * Fires when this button's arrow is clicked
12003      * @param {SplitButton} this
12004      * @param {EventObject} e The click event
12005      */
12006     this.addEvents({"arrowclick":true});
12007 };
12008
12009 Roo.extend(Roo.SplitButton, Roo.Button, {
12010     render : function(renderTo){
12011         // this is one sweet looking template!
12012         var tpl = new Roo.Template(
12013             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12014             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12015             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12016             "</tbody></table></td><td>",
12017             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12018             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12019             "</tbody></table></td></tr></table>"
12020         );
12021         var btn = tpl.append(renderTo, [this.text, this.type], true);
12022         var btnEl = btn.child("button");
12023         if(this.cls){
12024             btn.addClass(this.cls);
12025         }
12026         if(this.icon){
12027             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12028         }
12029         if(this.iconCls){
12030             btnEl.addClass(this.iconCls);
12031             if(!this.cls){
12032                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12033             }
12034         }
12035         this.el = btn;
12036         if(this.handleMouseEvents){
12037             btn.on("mouseover", this.onMouseOver, this);
12038             btn.on("mouseout", this.onMouseOut, this);
12039             btn.on("mousedown", this.onMouseDown, this);
12040             btn.on("mouseup", this.onMouseUp, this);
12041         }
12042         btn.on(this.clickEvent, this.onClick, this);
12043         if(this.tooltip){
12044             if(typeof this.tooltip == 'object'){
12045                 Roo.QuickTips.tips(Roo.apply({
12046                       target: btnEl.id
12047                 }, this.tooltip));
12048             } else {
12049                 btnEl.dom[this.tooltipType] = this.tooltip;
12050             }
12051         }
12052         if(this.arrowTooltip){
12053             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12054         }
12055         if(this.hidden){
12056             this.hide();
12057         }
12058         if(this.disabled){
12059             this.disable();
12060         }
12061         if(this.pressed){
12062             this.el.addClass("x-btn-pressed");
12063         }
12064         if(Roo.isIE && !Roo.isIE7){
12065             this.autoWidth.defer(1, this);
12066         }else{
12067             this.autoWidth();
12068         }
12069         if(this.menu){
12070             this.menu.on("show", this.onMenuShow, this);
12071             this.menu.on("hide", this.onMenuHide, this);
12072         }
12073         this.fireEvent('render', this);
12074     },
12075
12076     // private
12077     autoWidth : function(){
12078         if(this.el){
12079             var tbl = this.el.child("table:first");
12080             var tbl2 = this.el.child("table:last");
12081             this.el.setWidth("auto");
12082             tbl.setWidth("auto");
12083             if(Roo.isIE7 && Roo.isStrict){
12084                 var ib = this.el.child('button:first');
12085                 if(ib && ib.getWidth() > 20){
12086                     ib.clip();
12087                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12088                 }
12089             }
12090             if(this.minWidth){
12091                 if(this.hidden){
12092                     this.el.beginMeasure();
12093                 }
12094                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12095                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12096                 }
12097                 if(this.hidden){
12098                     this.el.endMeasure();
12099                 }
12100             }
12101             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12102         } 
12103     },
12104     /**
12105      * Sets this button's click handler
12106      * @param {Function} handler The function to call when the button is clicked
12107      * @param {Object} scope (optional) Scope for the function passed above
12108      */
12109     setHandler : function(handler, scope){
12110         this.handler = handler;
12111         this.scope = scope;  
12112     },
12113     
12114     /**
12115      * Sets this button's arrow click handler
12116      * @param {Function} handler The function to call when the arrow is clicked
12117      * @param {Object} scope (optional) Scope for the function passed above
12118      */
12119     setArrowHandler : function(handler, scope){
12120         this.arrowHandler = handler;
12121         this.scope = scope;  
12122     },
12123     
12124     /**
12125      * Focus the button
12126      */
12127     focus : function(){
12128         if(this.el){
12129             this.el.child("button:first").focus();
12130         }
12131     },
12132
12133     // private
12134     onClick : function(e){
12135         e.preventDefault();
12136         if(!this.disabled){
12137             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12138                 if(this.menu && !this.menu.isVisible()){
12139                     this.menu.show(this.el, this.menuAlign);
12140                 }
12141                 this.fireEvent("arrowclick", this, e);
12142                 if(this.arrowHandler){
12143                     this.arrowHandler.call(this.scope || this, this, e);
12144                 }
12145             }else{
12146                 this.fireEvent("click", this, e);
12147                 if(this.handler){
12148                     this.handler.call(this.scope || this, this, e);
12149                 }
12150             }
12151         }
12152     },
12153     // private
12154     onMouseDown : function(e){
12155         if(!this.disabled){
12156             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12157         }
12158     },
12159     // private
12160     onMouseUp : function(e){
12161         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12162     }   
12163 });
12164
12165
12166 // backwards compat
12167 Roo.MenuButton = Roo.SplitButton;/*
12168  * Based on:
12169  * Ext JS Library 1.1.1
12170  * Copyright(c) 2006-2007, Ext JS, LLC.
12171  *
12172  * Originally Released Under LGPL - original licence link has changed is not relivant.
12173  *
12174  * Fork - LGPL
12175  * <script type="text/javascript">
12176  */
12177
12178 /**
12179  * @class Roo.Toolbar
12180  * Basic Toolbar class.
12181  * @constructor
12182  * Creates a new Toolbar
12183  * @param {Object} config The config object
12184  */ 
12185 Roo.Toolbar = function(container, buttons, config)
12186 {
12187     /// old consturctor format still supported..
12188     if(container instanceof Array){ // omit the container for later rendering
12189         buttons = container;
12190         config = buttons;
12191         container = null;
12192     }
12193     if (typeof(container) == 'object' && container.xtype) {
12194         config = container;
12195         container = config.container;
12196         buttons = config.buttons; // not really - use items!!
12197     }
12198     var xitems = [];
12199     if (config && config.items) {
12200         xitems = config.items;
12201         delete config.items;
12202     }
12203     Roo.apply(this, config);
12204     this.buttons = buttons;
12205     
12206     if(container){
12207         this.render(container);
12208     }
12209     Roo.each(xitems, function(b) {
12210         this.add(b);
12211     }, this);
12212     
12213 };
12214
12215 Roo.Toolbar.prototype = {
12216     /**
12217      * @cfg {Roo.data.Store} items
12218      * array of button configs or elements to add
12219      */
12220     
12221     /**
12222      * @cfg {String/HTMLElement/Element} container
12223      * The id or element that will contain the toolbar
12224      */
12225     // private
12226     render : function(ct){
12227         this.el = Roo.get(ct);
12228         if(this.cls){
12229             this.el.addClass(this.cls);
12230         }
12231         // using a table allows for vertical alignment
12232         // 100% width is needed by Safari...
12233         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12234         this.tr = this.el.child("tr", true);
12235         var autoId = 0;
12236         this.items = new Roo.util.MixedCollection(false, function(o){
12237             return o.id || ("item" + (++autoId));
12238         });
12239         if(this.buttons){
12240             this.add.apply(this, this.buttons);
12241             delete this.buttons;
12242         }
12243     },
12244
12245     /**
12246      * Adds element(s) to the toolbar -- this function takes a variable number of 
12247      * arguments of mixed type and adds them to the toolbar.
12248      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12249      * <ul>
12250      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12251      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12252      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12253      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12254      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12255      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12256      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12257      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12258      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12259      * </ul>
12260      * @param {Mixed} arg2
12261      * @param {Mixed} etc.
12262      */
12263     add : function(){
12264         var a = arguments, l = a.length;
12265         for(var i = 0; i < l; i++){
12266             this._add(a[i]);
12267         }
12268     },
12269     // private..
12270     _add : function(el) {
12271         
12272         if (el.xtype) {
12273             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12274         }
12275         
12276         if (el.applyTo){ // some kind of form field
12277             return this.addField(el);
12278         } 
12279         if (el.render){ // some kind of Toolbar.Item
12280             return this.addItem(el);
12281         }
12282         if (typeof el == "string"){ // string
12283             if(el == "separator" || el == "-"){
12284                 return this.addSeparator();
12285             }
12286             if (el == " "){
12287                 return this.addSpacer();
12288             }
12289             if(el == "->"){
12290                 return this.addFill();
12291             }
12292             return this.addText(el);
12293             
12294         }
12295         if(el.tagName){ // element
12296             return this.addElement(el);
12297         }
12298         if(typeof el == "object"){ // must be button config?
12299             return this.addButton(el);
12300         }
12301         // and now what?!?!
12302         return false;
12303         
12304     },
12305     
12306     /**
12307      * Add an Xtype element
12308      * @param {Object} xtype Xtype Object
12309      * @return {Object} created Object
12310      */
12311     addxtype : function(e){
12312         return this.add(e);  
12313     },
12314     
12315     /**
12316      * Returns the Element for this toolbar.
12317      * @return {Roo.Element}
12318      */
12319     getEl : function(){
12320         return this.el;  
12321     },
12322     
12323     /**
12324      * Adds a separator
12325      * @return {Roo.Toolbar.Item} The separator item
12326      */
12327     addSeparator : function(){
12328         return this.addItem(new Roo.Toolbar.Separator());
12329     },
12330
12331     /**
12332      * Adds a spacer element
12333      * @return {Roo.Toolbar.Spacer} The spacer item
12334      */
12335     addSpacer : function(){
12336         return this.addItem(new Roo.Toolbar.Spacer());
12337     },
12338
12339     /**
12340      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12341      * @return {Roo.Toolbar.Fill} The fill item
12342      */
12343     addFill : function(){
12344         return this.addItem(new Roo.Toolbar.Fill());
12345     },
12346
12347     /**
12348      * Adds any standard HTML element to the toolbar
12349      * @param {String/HTMLElement/Element} el The element or id of the element to add
12350      * @return {Roo.Toolbar.Item} The element's item
12351      */
12352     addElement : function(el){
12353         return this.addItem(new Roo.Toolbar.Item(el));
12354     },
12355     /**
12356      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12357      * @type Roo.util.MixedCollection  
12358      */
12359     items : false,
12360      
12361     /**
12362      * Adds any Toolbar.Item or subclass
12363      * @param {Roo.Toolbar.Item} item
12364      * @return {Roo.Toolbar.Item} The item
12365      */
12366     addItem : function(item){
12367         var td = this.nextBlock();
12368         item.render(td);
12369         this.items.add(item);
12370         return item;
12371     },
12372     
12373     /**
12374      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12375      * @param {Object/Array} config A button config or array of configs
12376      * @return {Roo.Toolbar.Button/Array}
12377      */
12378     addButton : function(config){
12379         if(config instanceof Array){
12380             var buttons = [];
12381             for(var i = 0, len = config.length; i < len; i++) {
12382                 buttons.push(this.addButton(config[i]));
12383             }
12384             return buttons;
12385         }
12386         var b = config;
12387         if(!(config instanceof Roo.Toolbar.Button)){
12388             b = config.split ?
12389                 new Roo.Toolbar.SplitButton(config) :
12390                 new Roo.Toolbar.Button(config);
12391         }
12392         var td = this.nextBlock();
12393         b.render(td);
12394         this.items.add(b);
12395         return b;
12396     },
12397     
12398     /**
12399      * Adds text to the toolbar
12400      * @param {String} text The text to add
12401      * @return {Roo.Toolbar.Item} The element's item
12402      */
12403     addText : function(text){
12404         return this.addItem(new Roo.Toolbar.TextItem(text));
12405     },
12406     
12407     /**
12408      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12409      * @param {Number} index The index where the item is to be inserted
12410      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12411      * @return {Roo.Toolbar.Button/Item}
12412      */
12413     insertButton : function(index, item){
12414         if(item instanceof Array){
12415             var buttons = [];
12416             for(var i = 0, len = item.length; i < len; i++) {
12417                buttons.push(this.insertButton(index + i, item[i]));
12418             }
12419             return buttons;
12420         }
12421         if (!(item instanceof Roo.Toolbar.Button)){
12422            item = new Roo.Toolbar.Button(item);
12423         }
12424         var td = document.createElement("td");
12425         this.tr.insertBefore(td, this.tr.childNodes[index]);
12426         item.render(td);
12427         this.items.insert(index, item);
12428         return item;
12429     },
12430     
12431     /**
12432      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12433      * @param {Object} config
12434      * @return {Roo.Toolbar.Item} The element's item
12435      */
12436     addDom : function(config, returnEl){
12437         var td = this.nextBlock();
12438         Roo.DomHelper.overwrite(td, config);
12439         var ti = new Roo.Toolbar.Item(td.firstChild);
12440         ti.render(td);
12441         this.items.add(ti);
12442         return ti;
12443     },
12444
12445     /**
12446      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12447      * @type Roo.util.MixedCollection  
12448      */
12449     fields : false,
12450     
12451     /**
12452      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12453      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12454      * @param {Roo.form.Field} field
12455      * @return {Roo.ToolbarItem}
12456      */
12457      
12458       
12459     addField : function(field) {
12460         if (!this.fields) {
12461             var autoId = 0;
12462             this.fields = new Roo.util.MixedCollection(false, function(o){
12463                 return o.id || ("item" + (++autoId));
12464             });
12465
12466         }
12467         
12468         var td = this.nextBlock();
12469         field.render(td);
12470         var ti = new Roo.Toolbar.Item(td.firstChild);
12471         ti.render(td);
12472         this.items.add(ti);
12473         this.fields.add(field);
12474         return ti;
12475     },
12476     /**
12477      * Hide the toolbar
12478      * @method hide
12479      */
12480      
12481       
12482     hide : function()
12483     {
12484         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12485         this.el.child('div').hide();
12486     },
12487     /**
12488      * Show the toolbar
12489      * @method show
12490      */
12491     show : function()
12492     {
12493         this.el.child('div').show();
12494     },
12495       
12496     // private
12497     nextBlock : function(){
12498         var td = document.createElement("td");
12499         this.tr.appendChild(td);
12500         return td;
12501     },
12502
12503     // private
12504     destroy : function(){
12505         if(this.items){ // rendered?
12506             Roo.destroy.apply(Roo, this.items.items);
12507         }
12508         if(this.fields){ // rendered?
12509             Roo.destroy.apply(Roo, this.fields.items);
12510         }
12511         Roo.Element.uncache(this.el, this.tr);
12512     }
12513 };
12514
12515 /**
12516  * @class Roo.Toolbar.Item
12517  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12518  * @constructor
12519  * Creates a new Item
12520  * @param {HTMLElement} el 
12521  */
12522 Roo.Toolbar.Item = function(el){
12523     this.el = Roo.getDom(el);
12524     this.id = Roo.id(this.el);
12525     this.hidden = false;
12526 };
12527
12528 Roo.Toolbar.Item.prototype = {
12529     
12530     /**
12531      * Get this item's HTML Element
12532      * @return {HTMLElement}
12533      */
12534     getEl : function(){
12535        return this.el;  
12536     },
12537
12538     // private
12539     render : function(td){
12540         this.td = td;
12541         td.appendChild(this.el);
12542     },
12543     
12544     /**
12545      * Removes and destroys this item.
12546      */
12547     destroy : function(){
12548         this.td.parentNode.removeChild(this.td);
12549     },
12550     
12551     /**
12552      * Shows this item.
12553      */
12554     show: function(){
12555         this.hidden = false;
12556         this.td.style.display = "";
12557     },
12558     
12559     /**
12560      * Hides this item.
12561      */
12562     hide: function(){
12563         this.hidden = true;
12564         this.td.style.display = "none";
12565     },
12566     
12567     /**
12568      * Convenience function for boolean show/hide.
12569      * @param {Boolean} visible true to show/false to hide
12570      */
12571     setVisible: function(visible){
12572         if(visible) {
12573             this.show();
12574         }else{
12575             this.hide();
12576         }
12577     },
12578     
12579     /**
12580      * Try to focus this item.
12581      */
12582     focus : function(){
12583         Roo.fly(this.el).focus();
12584     },
12585     
12586     /**
12587      * Disables this item.
12588      */
12589     disable : function(){
12590         Roo.fly(this.td).addClass("x-item-disabled");
12591         this.disabled = true;
12592         this.el.disabled = true;
12593     },
12594     
12595     /**
12596      * Enables this item.
12597      */
12598     enable : function(){
12599         Roo.fly(this.td).removeClass("x-item-disabled");
12600         this.disabled = false;
12601         this.el.disabled = false;
12602     }
12603 };
12604
12605
12606 /**
12607  * @class Roo.Toolbar.Separator
12608  * @extends Roo.Toolbar.Item
12609  * A simple toolbar separator class
12610  * @constructor
12611  * Creates a new Separator
12612  */
12613 Roo.Toolbar.Separator = function(){
12614     var s = document.createElement("span");
12615     s.className = "ytb-sep";
12616     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12617 };
12618 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12619     enable:Roo.emptyFn,
12620     disable:Roo.emptyFn,
12621     focus:Roo.emptyFn
12622 });
12623
12624 /**
12625  * @class Roo.Toolbar.Spacer
12626  * @extends Roo.Toolbar.Item
12627  * A simple element that adds extra horizontal space to a toolbar.
12628  * @constructor
12629  * Creates a new Spacer
12630  */
12631 Roo.Toolbar.Spacer = function(){
12632     var s = document.createElement("div");
12633     s.className = "ytb-spacer";
12634     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12635 };
12636 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12637     enable:Roo.emptyFn,
12638     disable:Roo.emptyFn,
12639     focus:Roo.emptyFn
12640 });
12641
12642 /**
12643  * @class Roo.Toolbar.Fill
12644  * @extends Roo.Toolbar.Spacer
12645  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12646  * @constructor
12647  * Creates a new Spacer
12648  */
12649 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12650     // private
12651     render : function(td){
12652         td.style.width = '100%';
12653         Roo.Toolbar.Fill.superclass.render.call(this, td);
12654     }
12655 });
12656
12657 /**
12658  * @class Roo.Toolbar.TextItem
12659  * @extends Roo.Toolbar.Item
12660  * A simple class that renders text directly into a toolbar.
12661  * @constructor
12662  * Creates a new TextItem
12663  * @param {String} text
12664  */
12665 Roo.Toolbar.TextItem = function(text){
12666     if (typeof(text) == 'object') {
12667         text = text.text;
12668     }
12669     var s = document.createElement("span");
12670     s.className = "ytb-text";
12671     s.innerHTML = text;
12672     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12673 };
12674 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12675     enable:Roo.emptyFn,
12676     disable:Roo.emptyFn,
12677     focus:Roo.emptyFn
12678 });
12679
12680 /**
12681  * @class Roo.Toolbar.Button
12682  * @extends Roo.Button
12683  * A button that renders into a toolbar.
12684  * @constructor
12685  * Creates a new Button
12686  * @param {Object} config A standard {@link Roo.Button} config object
12687  */
12688 Roo.Toolbar.Button = function(config){
12689     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12690 };
12691 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12692     render : function(td){
12693         this.td = td;
12694         Roo.Toolbar.Button.superclass.render.call(this, td);
12695     },
12696     
12697     /**
12698      * Removes and destroys this button
12699      */
12700     destroy : function(){
12701         Roo.Toolbar.Button.superclass.destroy.call(this);
12702         this.td.parentNode.removeChild(this.td);
12703     },
12704     
12705     /**
12706      * Shows this button
12707      */
12708     show: function(){
12709         this.hidden = false;
12710         this.td.style.display = "";
12711     },
12712     
12713     /**
12714      * Hides this button
12715      */
12716     hide: function(){
12717         this.hidden = true;
12718         this.td.style.display = "none";
12719     },
12720
12721     /**
12722      * Disables this item
12723      */
12724     disable : function(){
12725         Roo.fly(this.td).addClass("x-item-disabled");
12726         this.disabled = true;
12727     },
12728
12729     /**
12730      * Enables this item
12731      */
12732     enable : function(){
12733         Roo.fly(this.td).removeClass("x-item-disabled");
12734         this.disabled = false;
12735     }
12736 });
12737 // backwards compat
12738 Roo.ToolbarButton = Roo.Toolbar.Button;
12739
12740 /**
12741  * @class Roo.Toolbar.SplitButton
12742  * @extends Roo.SplitButton
12743  * A menu button that renders into a toolbar.
12744  * @constructor
12745  * Creates a new SplitButton
12746  * @param {Object} config A standard {@link Roo.SplitButton} config object
12747  */
12748 Roo.Toolbar.SplitButton = function(config){
12749     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12750 };
12751 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12752     render : function(td){
12753         this.td = td;
12754         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12755     },
12756     
12757     /**
12758      * Removes and destroys this button
12759      */
12760     destroy : function(){
12761         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12762         this.td.parentNode.removeChild(this.td);
12763     },
12764     
12765     /**
12766      * Shows this button
12767      */
12768     show: function(){
12769         this.hidden = false;
12770         this.td.style.display = "";
12771     },
12772     
12773     /**
12774      * Hides this button
12775      */
12776     hide: function(){
12777         this.hidden = true;
12778         this.td.style.display = "none";
12779     }
12780 });
12781
12782 // backwards compat
12783 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12784  * Based on:
12785  * Ext JS Library 1.1.1
12786  * Copyright(c) 2006-2007, Ext JS, LLC.
12787  *
12788  * Originally Released Under LGPL - original licence link has changed is not relivant.
12789  *
12790  * Fork - LGPL
12791  * <script type="text/javascript">
12792  */
12793  
12794 /**
12795  * @class Roo.PagingToolbar
12796  * @extends Roo.Toolbar
12797  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12798  * @constructor
12799  * Create a new PagingToolbar
12800  * @param {Object} config The config object
12801  */
12802 Roo.PagingToolbar = function(el, ds, config)
12803 {
12804     // old args format still supported... - xtype is prefered..
12805     if (typeof(el) == 'object' && el.xtype) {
12806         // created from xtype...
12807         config = el;
12808         ds = el.dataSource;
12809         el = config.container;
12810     }
12811     var items = [];
12812     if (config.items) {
12813         items = config.items;
12814         config.items = [];
12815     }
12816     
12817     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12818     this.ds = ds;
12819     this.cursor = 0;
12820     this.renderButtons(this.el);
12821     this.bind(ds);
12822     
12823     // supprot items array.
12824    
12825     Roo.each(items, function(e) {
12826         this.add(Roo.factory(e));
12827     },this);
12828     
12829 };
12830
12831 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12832     /**
12833      * @cfg {Roo.data.Store} dataSource
12834      * The underlying data store providing the paged data
12835      */
12836     /**
12837      * @cfg {String/HTMLElement/Element} container
12838      * container The id or element that will contain the toolbar
12839      */
12840     /**
12841      * @cfg {Boolean} displayInfo
12842      * True to display the displayMsg (defaults to false)
12843      */
12844     /**
12845      * @cfg {Number} pageSize
12846      * The number of records to display per page (defaults to 20)
12847      */
12848     pageSize: 20,
12849     /**
12850      * @cfg {String} displayMsg
12851      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12852      */
12853     displayMsg : 'Displaying {0} - {1} of {2}',
12854     /**
12855      * @cfg {String} emptyMsg
12856      * The message to display when no records are found (defaults to "No data to display")
12857      */
12858     emptyMsg : 'No data to display',
12859     /**
12860      * Customizable piece of the default paging text (defaults to "Page")
12861      * @type String
12862      */
12863     beforePageText : "Page",
12864     /**
12865      * Customizable piece of the default paging text (defaults to "of %0")
12866      * @type String
12867      */
12868     afterPageText : "of {0}",
12869     /**
12870      * Customizable piece of the default paging text (defaults to "First Page")
12871      * @type String
12872      */
12873     firstText : "First Page",
12874     /**
12875      * Customizable piece of the default paging text (defaults to "Previous Page")
12876      * @type String
12877      */
12878     prevText : "Previous Page",
12879     /**
12880      * Customizable piece of the default paging text (defaults to "Next Page")
12881      * @type String
12882      */
12883     nextText : "Next Page",
12884     /**
12885      * Customizable piece of the default paging text (defaults to "Last Page")
12886      * @type String
12887      */
12888     lastText : "Last Page",
12889     /**
12890      * Customizable piece of the default paging text (defaults to "Refresh")
12891      * @type String
12892      */
12893     refreshText : "Refresh",
12894
12895     // private
12896     renderButtons : function(el){
12897         Roo.PagingToolbar.superclass.render.call(this, el);
12898         this.first = this.addButton({
12899             tooltip: this.firstText,
12900             cls: "x-btn-icon x-grid-page-first",
12901             disabled: true,
12902             handler: this.onClick.createDelegate(this, ["first"])
12903         });
12904         this.prev = this.addButton({
12905             tooltip: this.prevText,
12906             cls: "x-btn-icon x-grid-page-prev",
12907             disabled: true,
12908             handler: this.onClick.createDelegate(this, ["prev"])
12909         });
12910         //this.addSeparator();
12911         this.add(this.beforePageText);
12912         this.field = Roo.get(this.addDom({
12913            tag: "input",
12914            type: "text",
12915            size: "3",
12916            value: "1",
12917            cls: "x-grid-page-number"
12918         }).el);
12919         this.field.on("keydown", this.onPagingKeydown, this);
12920         this.field.on("focus", function(){this.dom.select();});
12921         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12922         this.field.setHeight(18);
12923         //this.addSeparator();
12924         this.next = this.addButton({
12925             tooltip: this.nextText,
12926             cls: "x-btn-icon x-grid-page-next",
12927             disabled: true,
12928             handler: this.onClick.createDelegate(this, ["next"])
12929         });
12930         this.last = this.addButton({
12931             tooltip: this.lastText,
12932             cls: "x-btn-icon x-grid-page-last",
12933             disabled: true,
12934             handler: this.onClick.createDelegate(this, ["last"])
12935         });
12936         //this.addSeparator();
12937         this.loading = this.addButton({
12938             tooltip: this.refreshText,
12939             cls: "x-btn-icon x-grid-loading",
12940             handler: this.onClick.createDelegate(this, ["refresh"])
12941         });
12942
12943         if(this.displayInfo){
12944             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12945         }
12946     },
12947
12948     // private
12949     updateInfo : function(){
12950         if(this.displayEl){
12951             var count = this.ds.getCount();
12952             var msg = count == 0 ?
12953                 this.emptyMsg :
12954                 String.format(
12955                     this.displayMsg,
12956                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12957                 );
12958             this.displayEl.update(msg);
12959         }
12960     },
12961
12962     // private
12963     onLoad : function(ds, r, o){
12964        this.cursor = o.params ? o.params.start : 0;
12965        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12966
12967        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12968        this.field.dom.value = ap;
12969        this.first.setDisabled(ap == 1);
12970        this.prev.setDisabled(ap == 1);
12971        this.next.setDisabled(ap == ps);
12972        this.last.setDisabled(ap == ps);
12973        this.loading.enable();
12974        this.updateInfo();
12975     },
12976
12977     // private
12978     getPageData : function(){
12979         var total = this.ds.getTotalCount();
12980         return {
12981             total : total,
12982             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12983             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12984         };
12985     },
12986
12987     // private
12988     onLoadError : function(){
12989         this.loading.enable();
12990     },
12991
12992     // private
12993     onPagingKeydown : function(e){
12994         var k = e.getKey();
12995         var d = this.getPageData();
12996         if(k == e.RETURN){
12997             var v = this.field.dom.value, pageNum;
12998             if(!v || isNaN(pageNum = parseInt(v, 10))){
12999                 this.field.dom.value = d.activePage;
13000                 return;
13001             }
13002             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13003             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13004             e.stopEvent();
13005         }
13006         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
13007         {
13008           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13009           this.field.dom.value = pageNum;
13010           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13011           e.stopEvent();
13012         }
13013         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13014         {
13015           var v = this.field.dom.value, pageNum; 
13016           var increment = (e.shiftKey) ? 10 : 1;
13017           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13018             increment *= -1;
13019           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13020             this.field.dom.value = d.activePage;
13021             return;
13022           }
13023           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13024           {
13025             this.field.dom.value = parseInt(v, 10) + increment;
13026             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13027             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13028           }
13029           e.stopEvent();
13030         }
13031     },
13032
13033     // private
13034     beforeLoad : function(){
13035         if(this.loading){
13036             this.loading.disable();
13037         }
13038     },
13039
13040     // private
13041     onClick : function(which){
13042         var ds = this.ds;
13043         switch(which){
13044             case "first":
13045                 ds.load({params:{start: 0, limit: this.pageSize}});
13046             break;
13047             case "prev":
13048                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13049             break;
13050             case "next":
13051                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13052             break;
13053             case "last":
13054                 var total = ds.getTotalCount();
13055                 var extra = total % this.pageSize;
13056                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13057                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13058             break;
13059             case "refresh":
13060                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13061             break;
13062         }
13063     },
13064
13065     /**
13066      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13067      * @param {Roo.data.Store} store The data store to unbind
13068      */
13069     unbind : function(ds){
13070         ds.un("beforeload", this.beforeLoad, this);
13071         ds.un("load", this.onLoad, this);
13072         ds.un("loadexception", this.onLoadError, this);
13073         ds.un("remove", this.updateInfo, this);
13074         ds.un("add", this.updateInfo, this);
13075         this.ds = undefined;
13076     },
13077
13078     /**
13079      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13080      * @param {Roo.data.Store} store The data store to bind
13081      */
13082     bind : function(ds){
13083         ds.on("beforeload", this.beforeLoad, this);
13084         ds.on("load", this.onLoad, this);
13085         ds.on("loadexception", this.onLoadError, this);
13086         ds.on("remove", this.updateInfo, this);
13087         ds.on("add", this.updateInfo, this);
13088         this.ds = ds;
13089     }
13090 });/*
13091  * Based on:
13092  * Ext JS Library 1.1.1
13093  * Copyright(c) 2006-2007, Ext JS, LLC.
13094  *
13095  * Originally Released Under LGPL - original licence link has changed is not relivant.
13096  *
13097  * Fork - LGPL
13098  * <script type="text/javascript">
13099  */
13100
13101 /**
13102  * @class Roo.Resizable
13103  * @extends Roo.util.Observable
13104  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13105  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13106  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13107  * the element will be wrapped for you automatically.</p>
13108  * <p>Here is the list of valid resize handles:</p>
13109  * <pre>
13110 Value   Description
13111 ------  -------------------
13112  'n'     north
13113  's'     south
13114  'e'     east
13115  'w'     west
13116  'nw'    northwest
13117  'sw'    southwest
13118  'se'    southeast
13119  'ne'    northeast
13120  'hd'    horizontal drag
13121  'all'   all
13122 </pre>
13123  * <p>Here's an example showing the creation of a typical Resizable:</p>
13124  * <pre><code>
13125 var resizer = new Roo.Resizable("element-id", {
13126     handles: 'all',
13127     minWidth: 200,
13128     minHeight: 100,
13129     maxWidth: 500,
13130     maxHeight: 400,
13131     pinned: true
13132 });
13133 resizer.on("resize", myHandler);
13134 </code></pre>
13135  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13136  * resizer.east.setDisplayed(false);</p>
13137  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13138  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13139  * resize operation's new size (defaults to [0, 0])
13140  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13141  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13142  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13143  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13144  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13145  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13146  * @cfg {Number} width The width of the element in pixels (defaults to null)
13147  * @cfg {Number} height The height of the element in pixels (defaults to null)
13148  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13149  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13150  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13151  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13152  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13153  * in favor of the handles config option (defaults to false)
13154  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13155  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13156  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13157  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13158  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13159  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13160  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13161  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13162  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13163  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13164  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13165  * @constructor
13166  * Create a new resizable component
13167  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13168  * @param {Object} config configuration options
13169   */
13170 Roo.Resizable = function(el, config)
13171 {
13172     this.el = Roo.get(el);
13173
13174     if(config && config.wrap){
13175         config.resizeChild = this.el;
13176         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13177         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13178         this.el.setStyle("overflow", "hidden");
13179         this.el.setPositioning(config.resizeChild.getPositioning());
13180         config.resizeChild.clearPositioning();
13181         if(!config.width || !config.height){
13182             var csize = config.resizeChild.getSize();
13183             this.el.setSize(csize.width, csize.height);
13184         }
13185         if(config.pinned && !config.adjustments){
13186             config.adjustments = "auto";
13187         }
13188     }
13189
13190     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13191     this.proxy.unselectable();
13192     this.proxy.enableDisplayMode('block');
13193
13194     Roo.apply(this, config);
13195
13196     if(this.pinned){
13197         this.disableTrackOver = true;
13198         this.el.addClass("x-resizable-pinned");
13199     }
13200     // if the element isn't positioned, make it relative
13201     var position = this.el.getStyle("position");
13202     if(position != "absolute" && position != "fixed"){
13203         this.el.setStyle("position", "relative");
13204     }
13205     if(!this.handles){ // no handles passed, must be legacy style
13206         this.handles = 's,e,se';
13207         if(this.multiDirectional){
13208             this.handles += ',n,w';
13209         }
13210     }
13211     if(this.handles == "all"){
13212         this.handles = "n s e w ne nw se sw";
13213     }
13214     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13215     var ps = Roo.Resizable.positions;
13216     for(var i = 0, len = hs.length; i < len; i++){
13217         if(hs[i] && ps[hs[i]]){
13218             var pos = ps[hs[i]];
13219             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13220         }
13221     }
13222     // legacy
13223     this.corner = this.southeast;
13224     
13225     // updateBox = the box can move..
13226     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13227         this.updateBox = true;
13228     }
13229
13230     this.activeHandle = null;
13231
13232     if(this.resizeChild){
13233         if(typeof this.resizeChild == "boolean"){
13234             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13235         }else{
13236             this.resizeChild = Roo.get(this.resizeChild, true);
13237         }
13238     }
13239     
13240     if(this.adjustments == "auto"){
13241         var rc = this.resizeChild;
13242         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13243         if(rc && (hw || hn)){
13244             rc.position("relative");
13245             rc.setLeft(hw ? hw.el.getWidth() : 0);
13246             rc.setTop(hn ? hn.el.getHeight() : 0);
13247         }
13248         this.adjustments = [
13249             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13250             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13251         ];
13252     }
13253
13254     if(this.draggable){
13255         this.dd = this.dynamic ?
13256             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13257         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13258     }
13259
13260     // public events
13261     this.addEvents({
13262         /**
13263          * @event beforeresize
13264          * Fired before resize is allowed. Set enabled to false to cancel resize.
13265          * @param {Roo.Resizable} this
13266          * @param {Roo.EventObject} e The mousedown event
13267          */
13268         "beforeresize" : true,
13269         /**
13270          * @event resize
13271          * Fired after a resize.
13272          * @param {Roo.Resizable} this
13273          * @param {Number} width The new width
13274          * @param {Number} height The new height
13275          * @param {Roo.EventObject} e The mouseup event
13276          */
13277         "resize" : true
13278     });
13279
13280     if(this.width !== null && this.height !== null){
13281         this.resizeTo(this.width, this.height);
13282     }else{
13283         this.updateChildSize();
13284     }
13285     if(Roo.isIE){
13286         this.el.dom.style.zoom = 1;
13287     }
13288     Roo.Resizable.superclass.constructor.call(this);
13289 };
13290
13291 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13292         resizeChild : false,
13293         adjustments : [0, 0],
13294         minWidth : 5,
13295         minHeight : 5,
13296         maxWidth : 10000,
13297         maxHeight : 10000,
13298         enabled : true,
13299         animate : false,
13300         duration : .35,
13301         dynamic : false,
13302         handles : false,
13303         multiDirectional : false,
13304         disableTrackOver : false,
13305         easing : 'easeOutStrong',
13306         widthIncrement : 0,
13307         heightIncrement : 0,
13308         pinned : false,
13309         width : null,
13310         height : null,
13311         preserveRatio : false,
13312         transparent: false,
13313         minX: 0,
13314         minY: 0,
13315         draggable: false,
13316
13317         /**
13318          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13319          */
13320         constrainTo: undefined,
13321         /**
13322          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13323          */
13324         resizeRegion: undefined,
13325
13326
13327     /**
13328      * Perform a manual resize
13329      * @param {Number} width
13330      * @param {Number} height
13331      */
13332     resizeTo : function(width, height){
13333         this.el.setSize(width, height);
13334         this.updateChildSize();
13335         this.fireEvent("resize", this, width, height, null);
13336     },
13337
13338     // private
13339     startSizing : function(e, handle){
13340         this.fireEvent("beforeresize", this, e);
13341         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13342
13343             if(!this.overlay){
13344                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13345                 this.overlay.unselectable();
13346                 this.overlay.enableDisplayMode("block");
13347                 this.overlay.on("mousemove", this.onMouseMove, this);
13348                 this.overlay.on("mouseup", this.onMouseUp, this);
13349             }
13350             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13351
13352             this.resizing = true;
13353             this.startBox = this.el.getBox();
13354             this.startPoint = e.getXY();
13355             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13356                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13357
13358             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13359             this.overlay.show();
13360
13361             if(this.constrainTo) {
13362                 var ct = Roo.get(this.constrainTo);
13363                 this.resizeRegion = ct.getRegion().adjust(
13364                     ct.getFrameWidth('t'),
13365                     ct.getFrameWidth('l'),
13366                     -ct.getFrameWidth('b'),
13367                     -ct.getFrameWidth('r')
13368                 );
13369             }
13370
13371             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13372             this.proxy.show();
13373             this.proxy.setBox(this.startBox);
13374             if(!this.dynamic){
13375                 this.proxy.setStyle('visibility', 'visible');
13376             }
13377         }
13378     },
13379
13380     // private
13381     onMouseDown : function(handle, e){
13382         if(this.enabled){
13383             e.stopEvent();
13384             this.activeHandle = handle;
13385             this.startSizing(e, handle);
13386         }
13387     },
13388
13389     // private
13390     onMouseUp : function(e){
13391         var size = this.resizeElement();
13392         this.resizing = false;
13393         this.handleOut();
13394         this.overlay.hide();
13395         this.proxy.hide();
13396         this.fireEvent("resize", this, size.width, size.height, e);
13397     },
13398
13399     // private
13400     updateChildSize : function(){
13401         if(this.resizeChild){
13402             var el = this.el;
13403             var child = this.resizeChild;
13404             var adj = this.adjustments;
13405             if(el.dom.offsetWidth){
13406                 var b = el.getSize(true);
13407                 child.setSize(b.width+adj[0], b.height+adj[1]);
13408             }
13409             // Second call here for IE
13410             // The first call enables instant resizing and
13411             // the second call corrects scroll bars if they
13412             // exist
13413             if(Roo.isIE){
13414                 setTimeout(function(){
13415                     if(el.dom.offsetWidth){
13416                         var b = el.getSize(true);
13417                         child.setSize(b.width+adj[0], b.height+adj[1]);
13418                     }
13419                 }, 10);
13420             }
13421         }
13422     },
13423
13424     // private
13425     snap : function(value, inc, min){
13426         if(!inc || !value) return value;
13427         var newValue = value;
13428         var m = value % inc;
13429         if(m > 0){
13430             if(m > (inc/2)){
13431                 newValue = value + (inc-m);
13432             }else{
13433                 newValue = value - m;
13434             }
13435         }
13436         return Math.max(min, newValue);
13437     },
13438
13439     // private
13440     resizeElement : function(){
13441         var box = this.proxy.getBox();
13442         if(this.updateBox){
13443             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13444         }else{
13445             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13446         }
13447         this.updateChildSize();
13448         if(!this.dynamic){
13449             this.proxy.hide();
13450         }
13451         return box;
13452     },
13453
13454     // private
13455     constrain : function(v, diff, m, mx){
13456         if(v - diff < m){
13457             diff = v - m;
13458         }else if(v - diff > mx){
13459             diff = mx - v;
13460         }
13461         return diff;
13462     },
13463
13464     // private
13465     onMouseMove : function(e){
13466         if(this.enabled){
13467             try{// try catch so if something goes wrong the user doesn't get hung
13468
13469             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13470                 return;
13471             }
13472
13473             //var curXY = this.startPoint;
13474             var curSize = this.curSize || this.startBox;
13475             var x = this.startBox.x, y = this.startBox.y;
13476             var ox = x, oy = y;
13477             var w = curSize.width, h = curSize.height;
13478             var ow = w, oh = h;
13479             var mw = this.minWidth, mh = this.minHeight;
13480             var mxw = this.maxWidth, mxh = this.maxHeight;
13481             var wi = this.widthIncrement;
13482             var hi = this.heightIncrement;
13483
13484             var eventXY = e.getXY();
13485             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13486             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13487
13488             var pos = this.activeHandle.position;
13489
13490             switch(pos){
13491                 case "east":
13492                     w += diffX;
13493                     w = Math.min(Math.max(mw, w), mxw);
13494                     break;
13495              
13496                 case "south":
13497                     h += diffY;
13498                     h = Math.min(Math.max(mh, h), mxh);
13499                     break;
13500                 case "southeast":
13501                     w += diffX;
13502                     h += diffY;
13503                     w = Math.min(Math.max(mw, w), mxw);
13504                     h = Math.min(Math.max(mh, h), mxh);
13505                     break;
13506                 case "north":
13507                     diffY = this.constrain(h, diffY, mh, mxh);
13508                     y += diffY;
13509                     h -= diffY;
13510                     break;
13511                 case "hdrag":
13512                     
13513                     if (wi) {
13514                         var adiffX = Math.abs(diffX);
13515                         var sub = (adiffX % wi); // how much 
13516                         if (sub > (wi/2)) { // far enough to snap
13517                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13518                         } else {
13519                             // remove difference.. 
13520                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13521                         }
13522                     }
13523                     x += diffX;
13524                     x = Math.max(this.minX, x);
13525                     break;
13526                 case "west":
13527                     diffX = this.constrain(w, diffX, mw, mxw);
13528                     x += diffX;
13529                     w -= diffX;
13530                     break;
13531                 case "northeast":
13532                     w += diffX;
13533                     w = Math.min(Math.max(mw, w), mxw);
13534                     diffY = this.constrain(h, diffY, mh, mxh);
13535                     y += diffY;
13536                     h -= diffY;
13537                     break;
13538                 case "northwest":
13539                     diffX = this.constrain(w, diffX, mw, mxw);
13540                     diffY = this.constrain(h, diffY, mh, mxh);
13541                     y += diffY;
13542                     h -= diffY;
13543                     x += diffX;
13544                     w -= diffX;
13545                     break;
13546                case "southwest":
13547                     diffX = this.constrain(w, diffX, mw, mxw);
13548                     h += diffY;
13549                     h = Math.min(Math.max(mh, h), mxh);
13550                     x += diffX;
13551                     w -= diffX;
13552                     break;
13553             }
13554
13555             var sw = this.snap(w, wi, mw);
13556             var sh = this.snap(h, hi, mh);
13557             if(sw != w || sh != h){
13558                 switch(pos){
13559                     case "northeast":
13560                         y -= sh - h;
13561                     break;
13562                     case "north":
13563                         y -= sh - h;
13564                         break;
13565                     case "southwest":
13566                         x -= sw - w;
13567                     break;
13568                     case "west":
13569                         x -= sw - w;
13570                         break;
13571                     case "northwest":
13572                         x -= sw - w;
13573                         y -= sh - h;
13574                     break;
13575                 }
13576                 w = sw;
13577                 h = sh;
13578             }
13579
13580             if(this.preserveRatio){
13581                 switch(pos){
13582                     case "southeast":
13583                     case "east":
13584                         h = oh * (w/ow);
13585                         h = Math.min(Math.max(mh, h), mxh);
13586                         w = ow * (h/oh);
13587                        break;
13588                     case "south":
13589                         w = ow * (h/oh);
13590                         w = Math.min(Math.max(mw, w), mxw);
13591                         h = oh * (w/ow);
13592                         break;
13593                     case "northeast":
13594                         w = ow * (h/oh);
13595                         w = Math.min(Math.max(mw, w), mxw);
13596                         h = oh * (w/ow);
13597                     break;
13598                     case "north":
13599                         var tw = w;
13600                         w = ow * (h/oh);
13601                         w = Math.min(Math.max(mw, w), mxw);
13602                         h = oh * (w/ow);
13603                         x += (tw - w) / 2;
13604                         break;
13605                     case "southwest":
13606                         h = oh * (w/ow);
13607                         h = Math.min(Math.max(mh, h), mxh);
13608                         var tw = w;
13609                         w = ow * (h/oh);
13610                         x += tw - w;
13611                         break;
13612                     case "west":
13613                         var th = h;
13614                         h = oh * (w/ow);
13615                         h = Math.min(Math.max(mh, h), mxh);
13616                         y += (th - h) / 2;
13617                         var tw = w;
13618                         w = ow * (h/oh);
13619                         x += tw - w;
13620                        break;
13621                     case "northwest":
13622                         var tw = w;
13623                         var th = h;
13624                         h = oh * (w/ow);
13625                         h = Math.min(Math.max(mh, h), mxh);
13626                         w = ow * (h/oh);
13627                         y += th - h;
13628                         x += tw - w;
13629                        break;
13630
13631                 }
13632             }
13633             if (pos == 'hdrag') {
13634                 w = ow;
13635             }
13636             this.proxy.setBounds(x, y, w, h);
13637             if(this.dynamic){
13638                 this.resizeElement();
13639             }
13640             }catch(e){}
13641         }
13642     },
13643
13644     // private
13645     handleOver : function(){
13646         if(this.enabled){
13647             this.el.addClass("x-resizable-over");
13648         }
13649     },
13650
13651     // private
13652     handleOut : function(){
13653         if(!this.resizing){
13654             this.el.removeClass("x-resizable-over");
13655         }
13656     },
13657
13658     /**
13659      * Returns the element this component is bound to.
13660      * @return {Roo.Element}
13661      */
13662     getEl : function(){
13663         return this.el;
13664     },
13665
13666     /**
13667      * Returns the resizeChild element (or null).
13668      * @return {Roo.Element}
13669      */
13670     getResizeChild : function(){
13671         return this.resizeChild;
13672     },
13673
13674     /**
13675      * Destroys this resizable. If the element was wrapped and
13676      * removeEl is not true then the element remains.
13677      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13678      */
13679     destroy : function(removeEl){
13680         this.proxy.remove();
13681         if(this.overlay){
13682             this.overlay.removeAllListeners();
13683             this.overlay.remove();
13684         }
13685         var ps = Roo.Resizable.positions;
13686         for(var k in ps){
13687             if(typeof ps[k] != "function" && this[ps[k]]){
13688                 var h = this[ps[k]];
13689                 h.el.removeAllListeners();
13690                 h.el.remove();
13691             }
13692         }
13693         if(removeEl){
13694             this.el.update("");
13695             this.el.remove();
13696         }
13697     }
13698 });
13699
13700 // private
13701 // hash to map config positions to true positions
13702 Roo.Resizable.positions = {
13703     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13704     hd: "hdrag"
13705 };
13706
13707 // private
13708 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13709     if(!this.tpl){
13710         // only initialize the template if resizable is used
13711         var tpl = Roo.DomHelper.createTemplate(
13712             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13713         );
13714         tpl.compile();
13715         Roo.Resizable.Handle.prototype.tpl = tpl;
13716     }
13717     this.position = pos;
13718     this.rz = rz;
13719     // show north drag fro topdra
13720     var handlepos = pos == 'hdrag' ? 'north' : pos;
13721     
13722     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13723     if (pos == 'hdrag') {
13724         this.el.setStyle('cursor', 'pointer');
13725     }
13726     this.el.unselectable();
13727     if(transparent){
13728         this.el.setOpacity(0);
13729     }
13730     this.el.on("mousedown", this.onMouseDown, this);
13731     if(!disableTrackOver){
13732         this.el.on("mouseover", this.onMouseOver, this);
13733         this.el.on("mouseout", this.onMouseOut, this);
13734     }
13735 };
13736
13737 // private
13738 Roo.Resizable.Handle.prototype = {
13739     afterResize : function(rz){
13740         // do nothing
13741     },
13742     // private
13743     onMouseDown : function(e){
13744         this.rz.onMouseDown(this, e);
13745     },
13746     // private
13747     onMouseOver : function(e){
13748         this.rz.handleOver(this, e);
13749     },
13750     // private
13751     onMouseOut : function(e){
13752         this.rz.handleOut(this, e);
13753     }
13754 };/*
13755  * Based on:
13756  * Ext JS Library 1.1.1
13757  * Copyright(c) 2006-2007, Ext JS, LLC.
13758  *
13759  * Originally Released Under LGPL - original licence link has changed is not relivant.
13760  *
13761  * Fork - LGPL
13762  * <script type="text/javascript">
13763  */
13764
13765 /**
13766  * @class Roo.Editor
13767  * @extends Roo.Component
13768  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13769  * @constructor
13770  * Create a new Editor
13771  * @param {Roo.form.Field} field The Field object (or descendant)
13772  * @param {Object} config The config object
13773  */
13774 Roo.Editor = function(field, config){
13775     Roo.Editor.superclass.constructor.call(this, config);
13776     this.field = field;
13777     this.addEvents({
13778         /**
13779              * @event beforestartedit
13780              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13781              * false from the handler of this event.
13782              * @param {Editor} this
13783              * @param {Roo.Element} boundEl The underlying element bound to this editor
13784              * @param {Mixed} value The field value being set
13785              */
13786         "beforestartedit" : true,
13787         /**
13788              * @event startedit
13789              * Fires when this editor is displayed
13790              * @param {Roo.Element} boundEl The underlying element bound to this editor
13791              * @param {Mixed} value The starting field value
13792              */
13793         "startedit" : true,
13794         /**
13795              * @event beforecomplete
13796              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13797              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13798              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13799              * event will not fire since no edit actually occurred.
13800              * @param {Editor} this
13801              * @param {Mixed} value The current field value
13802              * @param {Mixed} startValue The original field value
13803              */
13804         "beforecomplete" : true,
13805         /**
13806              * @event complete
13807              * Fires after editing is complete and any changed value has been written to the underlying field.
13808              * @param {Editor} this
13809              * @param {Mixed} value The current field value
13810              * @param {Mixed} startValue The original field value
13811              */
13812         "complete" : true,
13813         /**
13814          * @event specialkey
13815          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13816          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13817          * @param {Roo.form.Field} this
13818          * @param {Roo.EventObject} e The event object
13819          */
13820         "specialkey" : true
13821     });
13822 };
13823
13824 Roo.extend(Roo.Editor, Roo.Component, {
13825     /**
13826      * @cfg {Boolean/String} autosize
13827      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13828      * or "height" to adopt the height only (defaults to false)
13829      */
13830     /**
13831      * @cfg {Boolean} revertInvalid
13832      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13833      * validation fails (defaults to true)
13834      */
13835     /**
13836      * @cfg {Boolean} ignoreNoChange
13837      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13838      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13839      * will never be ignored.
13840      */
13841     /**
13842      * @cfg {Boolean} hideEl
13843      * False to keep the bound element visible while the editor is displayed (defaults to true)
13844      */
13845     /**
13846      * @cfg {Mixed} value
13847      * The data value of the underlying field (defaults to "")
13848      */
13849     value : "",
13850     /**
13851      * @cfg {String} alignment
13852      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13853      */
13854     alignment: "c-c?",
13855     /**
13856      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13857      * for bottom-right shadow (defaults to "frame")
13858      */
13859     shadow : "frame",
13860     /**
13861      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13862      */
13863     constrain : false,
13864     /**
13865      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13866      */
13867     completeOnEnter : false,
13868     /**
13869      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13870      */
13871     cancelOnEsc : false,
13872     /**
13873      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13874      */
13875     updateEl : false,
13876
13877     // private
13878     onRender : function(ct, position){
13879         this.el = new Roo.Layer({
13880             shadow: this.shadow,
13881             cls: "x-editor",
13882             parentEl : ct,
13883             shim : this.shim,
13884             shadowOffset:4,
13885             id: this.id,
13886             constrain: this.constrain
13887         });
13888         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13889         if(this.field.msgTarget != 'title'){
13890             this.field.msgTarget = 'qtip';
13891         }
13892         this.field.render(this.el);
13893         if(Roo.isGecko){
13894             this.field.el.dom.setAttribute('autocomplete', 'off');
13895         }
13896         this.field.on("specialkey", this.onSpecialKey, this);
13897         if(this.swallowKeys){
13898             this.field.el.swallowEvent(['keydown','keypress']);
13899         }
13900         this.field.show();
13901         this.field.on("blur", this.onBlur, this);
13902         if(this.field.grow){
13903             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13904         }
13905     },
13906
13907     onSpecialKey : function(field, e){
13908         //Roo.log('editor onSpecialKey');
13909         if(this.completeOnEnter && e.getKey() == e.ENTER){
13910             e.stopEvent();
13911             this.completeEdit();
13912         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13913             this.cancelEdit();
13914         }else{
13915             this.fireEvent('specialkey', field, e);
13916         }
13917     },
13918
13919     /**
13920      * Starts the editing process and shows the editor.
13921      * @param {String/HTMLElement/Element} el The element to edit
13922      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13923       * to the innerHTML of el.
13924      */
13925     startEdit : function(el, value){
13926         if(this.editing){
13927             this.completeEdit();
13928         }
13929         this.boundEl = Roo.get(el);
13930         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13931         if(!this.rendered){
13932             this.render(this.parentEl || document.body);
13933         }
13934         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13935             return;
13936         }
13937         this.startValue = v;
13938         this.field.setValue(v);
13939         if(this.autoSize){
13940             var sz = this.boundEl.getSize();
13941             switch(this.autoSize){
13942                 case "width":
13943                 this.setSize(sz.width,  "");
13944                 break;
13945                 case "height":
13946                 this.setSize("",  sz.height);
13947                 break;
13948                 default:
13949                 this.setSize(sz.width,  sz.height);
13950             }
13951         }
13952         this.el.alignTo(this.boundEl, this.alignment);
13953         this.editing = true;
13954         if(Roo.QuickTips){
13955             Roo.QuickTips.disable();
13956         }
13957         this.show();
13958     },
13959
13960     /**
13961      * Sets the height and width of this editor.
13962      * @param {Number} width The new width
13963      * @param {Number} height The new height
13964      */
13965     setSize : function(w, h){
13966         this.field.setSize(w, h);
13967         if(this.el){
13968             this.el.sync();
13969         }
13970     },
13971
13972     /**
13973      * Realigns the editor to the bound field based on the current alignment config value.
13974      */
13975     realign : function(){
13976         this.el.alignTo(this.boundEl, this.alignment);
13977     },
13978
13979     /**
13980      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13981      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13982      */
13983     completeEdit : function(remainVisible){
13984         if(!this.editing){
13985             return;
13986         }
13987         var v = this.getValue();
13988         if(this.revertInvalid !== false && !this.field.isValid()){
13989             v = this.startValue;
13990             this.cancelEdit(true);
13991         }
13992         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13993             this.editing = false;
13994             this.hide();
13995             return;
13996         }
13997         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13998             this.editing = false;
13999             if(this.updateEl && this.boundEl){
14000                 this.boundEl.update(v);
14001             }
14002             if(remainVisible !== true){
14003                 this.hide();
14004             }
14005             this.fireEvent("complete", this, v, this.startValue);
14006         }
14007     },
14008
14009     // private
14010     onShow : function(){
14011         this.el.show();
14012         if(this.hideEl !== false){
14013             this.boundEl.hide();
14014         }
14015         this.field.show();
14016         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14017             this.fixIEFocus = true;
14018             this.deferredFocus.defer(50, this);
14019         }else{
14020             this.field.focus();
14021         }
14022         this.fireEvent("startedit", this.boundEl, this.startValue);
14023     },
14024
14025     deferredFocus : function(){
14026         if(this.editing){
14027             this.field.focus();
14028         }
14029     },
14030
14031     /**
14032      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14033      * reverted to the original starting value.
14034      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14035      * cancel (defaults to false)
14036      */
14037     cancelEdit : function(remainVisible){
14038         if(this.editing){
14039             this.setValue(this.startValue);
14040             if(remainVisible !== true){
14041                 this.hide();
14042             }
14043         }
14044     },
14045
14046     // private
14047     onBlur : function(){
14048         if(this.allowBlur !== true && this.editing){
14049             this.completeEdit();
14050         }
14051     },
14052
14053     // private
14054     onHide : function(){
14055         if(this.editing){
14056             this.completeEdit();
14057             return;
14058         }
14059         this.field.blur();
14060         if(this.field.collapse){
14061             this.field.collapse();
14062         }
14063         this.el.hide();
14064         if(this.hideEl !== false){
14065             this.boundEl.show();
14066         }
14067         if(Roo.QuickTips){
14068             Roo.QuickTips.enable();
14069         }
14070     },
14071
14072     /**
14073      * Sets the data value of the editor
14074      * @param {Mixed} value Any valid value supported by the underlying field
14075      */
14076     setValue : function(v){
14077         this.field.setValue(v);
14078     },
14079
14080     /**
14081      * Gets the data value of the editor
14082      * @return {Mixed} The data value
14083      */
14084     getValue : function(){
14085         return this.field.getValue();
14086     }
14087 });/*
14088  * Based on:
14089  * Ext JS Library 1.1.1
14090  * Copyright(c) 2006-2007, Ext JS, LLC.
14091  *
14092  * Originally Released Under LGPL - original licence link has changed is not relivant.
14093  *
14094  * Fork - LGPL
14095  * <script type="text/javascript">
14096  */
14097  
14098 /**
14099  * @class Roo.BasicDialog
14100  * @extends Roo.util.Observable
14101  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14102  * <pre><code>
14103 var dlg = new Roo.BasicDialog("my-dlg", {
14104     height: 200,
14105     width: 300,
14106     minHeight: 100,
14107     minWidth: 150,
14108     modal: true,
14109     proxyDrag: true,
14110     shadow: true
14111 });
14112 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14113 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14114 dlg.addButton('Cancel', dlg.hide, dlg);
14115 dlg.show();
14116 </code></pre>
14117   <b>A Dialog should always be a direct child of the body element.</b>
14118  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14119  * @cfg {String} title Default text to display in the title bar (defaults to null)
14120  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14121  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14122  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14123  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14124  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14125  * (defaults to null with no animation)
14126  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14127  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14128  * property for valid values (defaults to 'all')
14129  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14130  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14131  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14132  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14133  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14134  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14135  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14136  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14137  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14138  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14139  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14140  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14141  * draggable = true (defaults to false)
14142  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14143  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14144  * shadow (defaults to false)
14145  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14146  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14147  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14148  * @cfg {Array} buttons Array of buttons
14149  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14150  * @constructor
14151  * Create a new BasicDialog.
14152  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14153  * @param {Object} config Configuration options
14154  */
14155 Roo.BasicDialog = function(el, config){
14156     this.el = Roo.get(el);
14157     var dh = Roo.DomHelper;
14158     if(!this.el && config && config.autoCreate){
14159         if(typeof config.autoCreate == "object"){
14160             if(!config.autoCreate.id){
14161                 config.autoCreate.id = el;
14162             }
14163             this.el = dh.append(document.body,
14164                         config.autoCreate, true);
14165         }else{
14166             this.el = dh.append(document.body,
14167                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14168         }
14169     }
14170     el = this.el;
14171     el.setDisplayed(true);
14172     el.hide = this.hideAction;
14173     this.id = el.id;
14174     el.addClass("x-dlg");
14175
14176     Roo.apply(this, config);
14177
14178     this.proxy = el.createProxy("x-dlg-proxy");
14179     this.proxy.hide = this.hideAction;
14180     this.proxy.setOpacity(.5);
14181     this.proxy.hide();
14182
14183     if(config.width){
14184         el.setWidth(config.width);
14185     }
14186     if(config.height){
14187         el.setHeight(config.height);
14188     }
14189     this.size = el.getSize();
14190     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14191         this.xy = [config.x,config.y];
14192     }else{
14193         this.xy = el.getCenterXY(true);
14194     }
14195     /** The header element @type Roo.Element */
14196     this.header = el.child("> .x-dlg-hd");
14197     /** The body element @type Roo.Element */
14198     this.body = el.child("> .x-dlg-bd");
14199     /** The footer element @type Roo.Element */
14200     this.footer = el.child("> .x-dlg-ft");
14201
14202     if(!this.header){
14203         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14204     }
14205     if(!this.body){
14206         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14207     }
14208
14209     this.header.unselectable();
14210     if(this.title){
14211         this.header.update(this.title);
14212     }
14213     // this element allows the dialog to be focused for keyboard event
14214     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14215     this.focusEl.swallowEvent("click", true);
14216
14217     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14218
14219     // wrap the body and footer for special rendering
14220     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14221     if(this.footer){
14222         this.bwrap.dom.appendChild(this.footer.dom);
14223     }
14224
14225     this.bg = this.el.createChild({
14226         tag: "div", cls:"x-dlg-bg",
14227         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14228     });
14229     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14230
14231
14232     if(this.autoScroll !== false && !this.autoTabs){
14233         this.body.setStyle("overflow", "auto");
14234     }
14235
14236     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14237
14238     if(this.closable !== false){
14239         this.el.addClass("x-dlg-closable");
14240         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14241         this.close.on("click", this.closeClick, this);
14242         this.close.addClassOnOver("x-dlg-close-over");
14243     }
14244     if(this.collapsible !== false){
14245         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14246         this.collapseBtn.on("click", this.collapseClick, this);
14247         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14248         this.header.on("dblclick", this.collapseClick, this);
14249     }
14250     if(this.resizable !== false){
14251         this.el.addClass("x-dlg-resizable");
14252         this.resizer = new Roo.Resizable(el, {
14253             minWidth: this.minWidth || 80,
14254             minHeight:this.minHeight || 80,
14255             handles: this.resizeHandles || "all",
14256             pinned: true
14257         });
14258         this.resizer.on("beforeresize", this.beforeResize, this);
14259         this.resizer.on("resize", this.onResize, this);
14260     }
14261     if(this.draggable !== false){
14262         el.addClass("x-dlg-draggable");
14263         if (!this.proxyDrag) {
14264             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14265         }
14266         else {
14267             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14268         }
14269         dd.setHandleElId(this.header.id);
14270         dd.endDrag = this.endMove.createDelegate(this);
14271         dd.startDrag = this.startMove.createDelegate(this);
14272         dd.onDrag = this.onDrag.createDelegate(this);
14273         dd.scroll = false;
14274         this.dd = dd;
14275     }
14276     if(this.modal){
14277         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14278         this.mask.enableDisplayMode("block");
14279         this.mask.hide();
14280         this.el.addClass("x-dlg-modal");
14281     }
14282     if(this.shadow){
14283         this.shadow = new Roo.Shadow({
14284             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14285             offset : this.shadowOffset
14286         });
14287     }else{
14288         this.shadowOffset = 0;
14289     }
14290     if(Roo.useShims && this.shim !== false){
14291         this.shim = this.el.createShim();
14292         this.shim.hide = this.hideAction;
14293         this.shim.hide();
14294     }else{
14295         this.shim = false;
14296     }
14297     if(this.autoTabs){
14298         this.initTabs();
14299     }
14300     if (this.buttons) { 
14301         var bts= this.buttons;
14302         this.buttons = [];
14303         Roo.each(bts, function(b) {
14304             this.addButton(b);
14305         }, this);
14306     }
14307     
14308     
14309     this.addEvents({
14310         /**
14311          * @event keydown
14312          * Fires when a key is pressed
14313          * @param {Roo.BasicDialog} this
14314          * @param {Roo.EventObject} e
14315          */
14316         "keydown" : true,
14317         /**
14318          * @event move
14319          * Fires when this dialog is moved by the user.
14320          * @param {Roo.BasicDialog} this
14321          * @param {Number} x The new page X
14322          * @param {Number} y The new page Y
14323          */
14324         "move" : true,
14325         /**
14326          * @event resize
14327          * Fires when this dialog is resized by the user.
14328          * @param {Roo.BasicDialog} this
14329          * @param {Number} width The new width
14330          * @param {Number} height The new height
14331          */
14332         "resize" : true,
14333         /**
14334          * @event beforehide
14335          * Fires before this dialog is hidden.
14336          * @param {Roo.BasicDialog} this
14337          */
14338         "beforehide" : true,
14339         /**
14340          * @event hide
14341          * Fires when this dialog is hidden.
14342          * @param {Roo.BasicDialog} this
14343          */
14344         "hide" : true,
14345         /**
14346          * @event beforeshow
14347          * Fires before this dialog is shown.
14348          * @param {Roo.BasicDialog} this
14349          */
14350         "beforeshow" : true,
14351         /**
14352          * @event show
14353          * Fires when this dialog is shown.
14354          * @param {Roo.BasicDialog} this
14355          */
14356         "show" : true
14357     });
14358     el.on("keydown", this.onKeyDown, this);
14359     el.on("mousedown", this.toFront, this);
14360     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14361     this.el.hide();
14362     Roo.DialogManager.register(this);
14363     Roo.BasicDialog.superclass.constructor.call(this);
14364 };
14365
14366 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14367     shadowOffset: Roo.isIE ? 6 : 5,
14368     minHeight: 80,
14369     minWidth: 200,
14370     minButtonWidth: 75,
14371     defaultButton: null,
14372     buttonAlign: "right",
14373     tabTag: 'div',
14374     firstShow: true,
14375
14376     /**
14377      * Sets the dialog title text
14378      * @param {String} text The title text to display
14379      * @return {Roo.BasicDialog} this
14380      */
14381     setTitle : function(text){
14382         this.header.update(text);
14383         return this;
14384     },
14385
14386     // private
14387     closeClick : function(){
14388         this.hide();
14389     },
14390
14391     // private
14392     collapseClick : function(){
14393         this[this.collapsed ? "expand" : "collapse"]();
14394     },
14395
14396     /**
14397      * Collapses the dialog to its minimized state (only the title bar is visible).
14398      * Equivalent to the user clicking the collapse dialog button.
14399      */
14400     collapse : function(){
14401         if(!this.collapsed){
14402             this.collapsed = true;
14403             this.el.addClass("x-dlg-collapsed");
14404             this.restoreHeight = this.el.getHeight();
14405             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14406         }
14407     },
14408
14409     /**
14410      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14411      * clicking the expand dialog button.
14412      */
14413     expand : function(){
14414         if(this.collapsed){
14415             this.collapsed = false;
14416             this.el.removeClass("x-dlg-collapsed");
14417             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14418         }
14419     },
14420
14421     /**
14422      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14423      * @return {Roo.TabPanel} The tabs component
14424      */
14425     initTabs : function(){
14426         var tabs = this.getTabs();
14427         while(tabs.getTab(0)){
14428             tabs.removeTab(0);
14429         }
14430         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14431             var dom = el.dom;
14432             tabs.addTab(Roo.id(dom), dom.title);
14433             dom.title = "";
14434         });
14435         tabs.activate(0);
14436         return tabs;
14437     },
14438
14439     // private
14440     beforeResize : function(){
14441         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14442     },
14443
14444     // private
14445     onResize : function(){
14446         this.refreshSize();
14447         this.syncBodyHeight();
14448         this.adjustAssets();
14449         this.focus();
14450         this.fireEvent("resize", this, this.size.width, this.size.height);
14451     },
14452
14453     // private
14454     onKeyDown : function(e){
14455         if(this.isVisible()){
14456             this.fireEvent("keydown", this, e);
14457         }
14458     },
14459
14460     /**
14461      * Resizes the dialog.
14462      * @param {Number} width
14463      * @param {Number} height
14464      * @return {Roo.BasicDialog} this
14465      */
14466     resizeTo : function(width, height){
14467         this.el.setSize(width, height);
14468         this.size = {width: width, height: height};
14469         this.syncBodyHeight();
14470         if(this.fixedcenter){
14471             this.center();
14472         }
14473         if(this.isVisible()){
14474             this.constrainXY();
14475             this.adjustAssets();
14476         }
14477         this.fireEvent("resize", this, width, height);
14478         return this;
14479     },
14480
14481
14482     /**
14483      * Resizes the dialog to fit the specified content size.
14484      * @param {Number} width
14485      * @param {Number} height
14486      * @return {Roo.BasicDialog} this
14487      */
14488     setContentSize : function(w, h){
14489         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14490         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14491         //if(!this.el.isBorderBox()){
14492             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14493             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14494         //}
14495         if(this.tabs){
14496             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14497             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14498         }
14499         this.resizeTo(w, h);
14500         return this;
14501     },
14502
14503     /**
14504      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14505      * executed in response to a particular key being pressed while the dialog is active.
14506      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14507      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14508      * @param {Function} fn The function to call
14509      * @param {Object} scope (optional) The scope of the function
14510      * @return {Roo.BasicDialog} this
14511      */
14512     addKeyListener : function(key, fn, scope){
14513         var keyCode, shift, ctrl, alt;
14514         if(typeof key == "object" && !(key instanceof Array)){
14515             keyCode = key["key"];
14516             shift = key["shift"];
14517             ctrl = key["ctrl"];
14518             alt = key["alt"];
14519         }else{
14520             keyCode = key;
14521         }
14522         var handler = function(dlg, e){
14523             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14524                 var k = e.getKey();
14525                 if(keyCode instanceof Array){
14526                     for(var i = 0, len = keyCode.length; i < len; i++){
14527                         if(keyCode[i] == k){
14528                           fn.call(scope || window, dlg, k, e);
14529                           return;
14530                         }
14531                     }
14532                 }else{
14533                     if(k == keyCode){
14534                         fn.call(scope || window, dlg, k, e);
14535                     }
14536                 }
14537             }
14538         };
14539         this.on("keydown", handler);
14540         return this;
14541     },
14542
14543     /**
14544      * Returns the TabPanel component (creates it if it doesn't exist).
14545      * Note: If you wish to simply check for the existence of tabs without creating them,
14546      * check for a null 'tabs' property.
14547      * @return {Roo.TabPanel} The tabs component
14548      */
14549     getTabs : function(){
14550         if(!this.tabs){
14551             this.el.addClass("x-dlg-auto-tabs");
14552             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14553             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14554         }
14555         return this.tabs;
14556     },
14557
14558     /**
14559      * Adds a button to the footer section of the dialog.
14560      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14561      * object or a valid Roo.DomHelper element config
14562      * @param {Function} handler The function called when the button is clicked
14563      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14564      * @return {Roo.Button} The new button
14565      */
14566     addButton : function(config, handler, scope){
14567         var dh = Roo.DomHelper;
14568         if(!this.footer){
14569             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14570         }
14571         if(!this.btnContainer){
14572             var tb = this.footer.createChild({
14573
14574                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14575                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14576             }, null, true);
14577             this.btnContainer = tb.firstChild.firstChild.firstChild;
14578         }
14579         var bconfig = {
14580             handler: handler,
14581             scope: scope,
14582             minWidth: this.minButtonWidth,
14583             hideParent:true
14584         };
14585         if(typeof config == "string"){
14586             bconfig.text = config;
14587         }else{
14588             if(config.tag){
14589                 bconfig.dhconfig = config;
14590             }else{
14591                 Roo.apply(bconfig, config);
14592             }
14593         }
14594         var fc = false;
14595         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14596             bconfig.position = Math.max(0, bconfig.position);
14597             fc = this.btnContainer.childNodes[bconfig.position];
14598         }
14599          
14600         var btn = new Roo.Button(
14601             fc ? 
14602                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14603                 : this.btnContainer.appendChild(document.createElement("td")),
14604             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14605             bconfig
14606         );
14607         this.syncBodyHeight();
14608         if(!this.buttons){
14609             /**
14610              * Array of all the buttons that have been added to this dialog via addButton
14611              * @type Array
14612              */
14613             this.buttons = [];
14614         }
14615         this.buttons.push(btn);
14616         return btn;
14617     },
14618
14619     /**
14620      * Sets the default button to be focused when the dialog is displayed.
14621      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14622      * @return {Roo.BasicDialog} this
14623      */
14624     setDefaultButton : function(btn){
14625         this.defaultButton = btn;
14626         return this;
14627     },
14628
14629     // private
14630     getHeaderFooterHeight : function(safe){
14631         var height = 0;
14632         if(this.header){
14633            height += this.header.getHeight();
14634         }
14635         if(this.footer){
14636            var fm = this.footer.getMargins();
14637             height += (this.footer.getHeight()+fm.top+fm.bottom);
14638         }
14639         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14640         height += this.centerBg.getPadding("tb");
14641         return height;
14642     },
14643
14644     // private
14645     syncBodyHeight : function(){
14646         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14647         var height = this.size.height - this.getHeaderFooterHeight(false);
14648         bd.setHeight(height-bd.getMargins("tb"));
14649         var hh = this.header.getHeight();
14650         var h = this.size.height-hh;
14651         cb.setHeight(h);
14652         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14653         bw.setHeight(h-cb.getPadding("tb"));
14654         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14655         bd.setWidth(bw.getWidth(true));
14656         if(this.tabs){
14657             this.tabs.syncHeight();
14658             if(Roo.isIE){
14659                 this.tabs.el.repaint();
14660             }
14661         }
14662     },
14663
14664     /**
14665      * Restores the previous state of the dialog if Roo.state is configured.
14666      * @return {Roo.BasicDialog} this
14667      */
14668     restoreState : function(){
14669         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14670         if(box && box.width){
14671             this.xy = [box.x, box.y];
14672             this.resizeTo(box.width, box.height);
14673         }
14674         return this;
14675     },
14676
14677     // private
14678     beforeShow : function(){
14679         this.expand();
14680         if(this.fixedcenter){
14681             this.xy = this.el.getCenterXY(true);
14682         }
14683         if(this.modal){
14684             Roo.get(document.body).addClass("x-body-masked");
14685             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14686             this.mask.show();
14687         }
14688         this.constrainXY();
14689     },
14690
14691     // private
14692     animShow : function(){
14693         var b = Roo.get(this.animateTarget).getBox();
14694         this.proxy.setSize(b.width, b.height);
14695         this.proxy.setLocation(b.x, b.y);
14696         this.proxy.show();
14697         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14698                     true, .35, this.showEl.createDelegate(this));
14699     },
14700
14701     /**
14702      * Shows the dialog.
14703      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14704      * @return {Roo.BasicDialog} this
14705      */
14706     show : function(animateTarget){
14707         if (this.fireEvent("beforeshow", this) === false){
14708             return;
14709         }
14710         if(this.syncHeightBeforeShow){
14711             this.syncBodyHeight();
14712         }else if(this.firstShow){
14713             this.firstShow = false;
14714             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14715         }
14716         this.animateTarget = animateTarget || this.animateTarget;
14717         if(!this.el.isVisible()){
14718             this.beforeShow();
14719             if(this.animateTarget && Roo.get(this.animateTarget)){
14720                 this.animShow();
14721             }else{
14722                 this.showEl();
14723             }
14724         }
14725         return this;
14726     },
14727
14728     // private
14729     showEl : function(){
14730         this.proxy.hide();
14731         this.el.setXY(this.xy);
14732         this.el.show();
14733         this.adjustAssets(true);
14734         this.toFront();
14735         this.focus();
14736         // IE peekaboo bug - fix found by Dave Fenwick
14737         if(Roo.isIE){
14738             this.el.repaint();
14739         }
14740         this.fireEvent("show", this);
14741     },
14742
14743     /**
14744      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14745      * dialog itself will receive focus.
14746      */
14747     focus : function(){
14748         if(this.defaultButton){
14749             this.defaultButton.focus();
14750         }else{
14751             this.focusEl.focus();
14752         }
14753     },
14754
14755     // private
14756     constrainXY : function(){
14757         if(this.constraintoviewport !== false){
14758             if(!this.viewSize){
14759                 if(this.container){
14760                     var s = this.container.getSize();
14761                     this.viewSize = [s.width, s.height];
14762                 }else{
14763                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14764                 }
14765             }
14766             var s = Roo.get(this.container||document).getScroll();
14767
14768             var x = this.xy[0], y = this.xy[1];
14769             var w = this.size.width, h = this.size.height;
14770             var vw = this.viewSize[0], vh = this.viewSize[1];
14771             // only move it if it needs it
14772             var moved = false;
14773             // first validate right/bottom
14774             if(x + w > vw+s.left){
14775                 x = vw - w;
14776                 moved = true;
14777             }
14778             if(y + h > vh+s.top){
14779                 y = vh - h;
14780                 moved = true;
14781             }
14782             // then make sure top/left isn't negative
14783             if(x < s.left){
14784                 x = s.left;
14785                 moved = true;
14786             }
14787             if(y < s.top){
14788                 y = s.top;
14789                 moved = true;
14790             }
14791             if(moved){
14792                 // cache xy
14793                 this.xy = [x, y];
14794                 if(this.isVisible()){
14795                     this.el.setLocation(x, y);
14796                     this.adjustAssets();
14797                 }
14798             }
14799         }
14800     },
14801
14802     // private
14803     onDrag : function(){
14804         if(!this.proxyDrag){
14805             this.xy = this.el.getXY();
14806             this.adjustAssets();
14807         }
14808     },
14809
14810     // private
14811     adjustAssets : function(doShow){
14812         var x = this.xy[0], y = this.xy[1];
14813         var w = this.size.width, h = this.size.height;
14814         if(doShow === true){
14815             if(this.shadow){
14816                 this.shadow.show(this.el);
14817             }
14818             if(this.shim){
14819                 this.shim.show();
14820             }
14821         }
14822         if(this.shadow && this.shadow.isVisible()){
14823             this.shadow.show(this.el);
14824         }
14825         if(this.shim && this.shim.isVisible()){
14826             this.shim.setBounds(x, y, w, h);
14827         }
14828     },
14829
14830     // private
14831     adjustViewport : function(w, h){
14832         if(!w || !h){
14833             w = Roo.lib.Dom.getViewWidth();
14834             h = Roo.lib.Dom.getViewHeight();
14835         }
14836         // cache the size
14837         this.viewSize = [w, h];
14838         if(this.modal && this.mask.isVisible()){
14839             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14840             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14841         }
14842         if(this.isVisible()){
14843             this.constrainXY();
14844         }
14845     },
14846
14847     /**
14848      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14849      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14850      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14851      */
14852     destroy : function(removeEl){
14853         if(this.isVisible()){
14854             this.animateTarget = null;
14855             this.hide();
14856         }
14857         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14858         if(this.tabs){
14859             this.tabs.destroy(removeEl);
14860         }
14861         Roo.destroy(
14862              this.shim,
14863              this.proxy,
14864              this.resizer,
14865              this.close,
14866              this.mask
14867         );
14868         if(this.dd){
14869             this.dd.unreg();
14870         }
14871         if(this.buttons){
14872            for(var i = 0, len = this.buttons.length; i < len; i++){
14873                this.buttons[i].destroy();
14874            }
14875         }
14876         this.el.removeAllListeners();
14877         if(removeEl === true){
14878             this.el.update("");
14879             this.el.remove();
14880         }
14881         Roo.DialogManager.unregister(this);
14882     },
14883
14884     // private
14885     startMove : function(){
14886         if(this.proxyDrag){
14887             this.proxy.show();
14888         }
14889         if(this.constraintoviewport !== false){
14890             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14891         }
14892     },
14893
14894     // private
14895     endMove : function(){
14896         if(!this.proxyDrag){
14897             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14898         }else{
14899             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14900             this.proxy.hide();
14901         }
14902         this.refreshSize();
14903         this.adjustAssets();
14904         this.focus();
14905         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14906     },
14907
14908     /**
14909      * Brings this dialog to the front of any other visible dialogs
14910      * @return {Roo.BasicDialog} this
14911      */
14912     toFront : function(){
14913         Roo.DialogManager.bringToFront(this);
14914         return this;
14915     },
14916
14917     /**
14918      * Sends this dialog to the back (under) of any other visible dialogs
14919      * @return {Roo.BasicDialog} this
14920      */
14921     toBack : function(){
14922         Roo.DialogManager.sendToBack(this);
14923         return this;
14924     },
14925
14926     /**
14927      * Centers this dialog in the viewport
14928      * @return {Roo.BasicDialog} this
14929      */
14930     center : function(){
14931         var xy = this.el.getCenterXY(true);
14932         this.moveTo(xy[0], xy[1]);
14933         return this;
14934     },
14935
14936     /**
14937      * Moves the dialog's top-left corner to the specified point
14938      * @param {Number} x
14939      * @param {Number} y
14940      * @return {Roo.BasicDialog} this
14941      */
14942     moveTo : function(x, y){
14943         this.xy = [x,y];
14944         if(this.isVisible()){
14945             this.el.setXY(this.xy);
14946             this.adjustAssets();
14947         }
14948         return this;
14949     },
14950
14951     /**
14952      * Aligns the dialog to the specified element
14953      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14954      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14955      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14956      * @return {Roo.BasicDialog} this
14957      */
14958     alignTo : function(element, position, offsets){
14959         this.xy = this.el.getAlignToXY(element, position, offsets);
14960         if(this.isVisible()){
14961             this.el.setXY(this.xy);
14962             this.adjustAssets();
14963         }
14964         return this;
14965     },
14966
14967     /**
14968      * Anchors an element to another element and realigns it when the window is resized.
14969      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14970      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14971      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14972      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14973      * is a number, it is used as the buffer delay (defaults to 50ms).
14974      * @return {Roo.BasicDialog} this
14975      */
14976     anchorTo : function(el, alignment, offsets, monitorScroll){
14977         var action = function(){
14978             this.alignTo(el, alignment, offsets);
14979         };
14980         Roo.EventManager.onWindowResize(action, this);
14981         var tm = typeof monitorScroll;
14982         if(tm != 'undefined'){
14983             Roo.EventManager.on(window, 'scroll', action, this,
14984                 {buffer: tm == 'number' ? monitorScroll : 50});
14985         }
14986         action.call(this);
14987         return this;
14988     },
14989
14990     /**
14991      * Returns true if the dialog is visible
14992      * @return {Boolean}
14993      */
14994     isVisible : function(){
14995         return this.el.isVisible();
14996     },
14997
14998     // private
14999     animHide : function(callback){
15000         var b = Roo.get(this.animateTarget).getBox();
15001         this.proxy.show();
15002         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15003         this.el.hide();
15004         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15005                     this.hideEl.createDelegate(this, [callback]));
15006     },
15007
15008     /**
15009      * Hides the dialog.
15010      * @param {Function} callback (optional) Function to call when the dialog is hidden
15011      * @return {Roo.BasicDialog} this
15012      */
15013     hide : function(callback){
15014         if (this.fireEvent("beforehide", this) === false){
15015             return;
15016         }
15017         if(this.shadow){
15018             this.shadow.hide();
15019         }
15020         if(this.shim) {
15021           this.shim.hide();
15022         }
15023         // sometimes animateTarget seems to get set.. causing problems...
15024         // this just double checks..
15025         if(this.animateTarget && Roo.get(this.animateTarget)) {
15026            this.animHide(callback);
15027         }else{
15028             this.el.hide();
15029             this.hideEl(callback);
15030         }
15031         return this;
15032     },
15033
15034     // private
15035     hideEl : function(callback){
15036         this.proxy.hide();
15037         if(this.modal){
15038             this.mask.hide();
15039             Roo.get(document.body).removeClass("x-body-masked");
15040         }
15041         this.fireEvent("hide", this);
15042         if(typeof callback == "function"){
15043             callback();
15044         }
15045     },
15046
15047     // private
15048     hideAction : function(){
15049         this.setLeft("-10000px");
15050         this.setTop("-10000px");
15051         this.setStyle("visibility", "hidden");
15052     },
15053
15054     // private
15055     refreshSize : function(){
15056         this.size = this.el.getSize();
15057         this.xy = this.el.getXY();
15058         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15059     },
15060
15061     // private
15062     // z-index is managed by the DialogManager and may be overwritten at any time
15063     setZIndex : function(index){
15064         if(this.modal){
15065             this.mask.setStyle("z-index", index);
15066         }
15067         if(this.shim){
15068             this.shim.setStyle("z-index", ++index);
15069         }
15070         if(this.shadow){
15071             this.shadow.setZIndex(++index);
15072         }
15073         this.el.setStyle("z-index", ++index);
15074         if(this.proxy){
15075             this.proxy.setStyle("z-index", ++index);
15076         }
15077         if(this.resizer){
15078             this.resizer.proxy.setStyle("z-index", ++index);
15079         }
15080
15081         this.lastZIndex = index;
15082     },
15083
15084     /**
15085      * Returns the element for this dialog
15086      * @return {Roo.Element} The underlying dialog Element
15087      */
15088     getEl : function(){
15089         return this.el;
15090     }
15091 });
15092
15093 /**
15094  * @class Roo.DialogManager
15095  * Provides global access to BasicDialogs that have been created and
15096  * support for z-indexing (layering) multiple open dialogs.
15097  */
15098 Roo.DialogManager = function(){
15099     var list = {};
15100     var accessList = [];
15101     var front = null;
15102
15103     // private
15104     var sortDialogs = function(d1, d2){
15105         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15106     };
15107
15108     // private
15109     var orderDialogs = function(){
15110         accessList.sort(sortDialogs);
15111         var seed = Roo.DialogManager.zseed;
15112         for(var i = 0, len = accessList.length; i < len; i++){
15113             var dlg = accessList[i];
15114             if(dlg){
15115                 dlg.setZIndex(seed + (i*10));
15116             }
15117         }
15118     };
15119
15120     return {
15121         /**
15122          * The starting z-index for BasicDialogs (defaults to 9000)
15123          * @type Number The z-index value
15124          */
15125         zseed : 9000,
15126
15127         // private
15128         register : function(dlg){
15129             list[dlg.id] = dlg;
15130             accessList.push(dlg);
15131         },
15132
15133         // private
15134         unregister : function(dlg){
15135             delete list[dlg.id];
15136             var i=0;
15137             var len=0;
15138             if(!accessList.indexOf){
15139                 for(  i = 0, len = accessList.length; i < len; i++){
15140                     if(accessList[i] == dlg){
15141                         accessList.splice(i, 1);
15142                         return;
15143                     }
15144                 }
15145             }else{
15146                  i = accessList.indexOf(dlg);
15147                 if(i != -1){
15148                     accessList.splice(i, 1);
15149                 }
15150             }
15151         },
15152
15153         /**
15154          * Gets a registered dialog by id
15155          * @param {String/Object} id The id of the dialog or a dialog
15156          * @return {Roo.BasicDialog} this
15157          */
15158         get : function(id){
15159             return typeof id == "object" ? id : list[id];
15160         },
15161
15162         /**
15163          * Brings the specified dialog to the front
15164          * @param {String/Object} dlg The id of the dialog or a dialog
15165          * @return {Roo.BasicDialog} this
15166          */
15167         bringToFront : function(dlg){
15168             dlg = this.get(dlg);
15169             if(dlg != front){
15170                 front = dlg;
15171                 dlg._lastAccess = new Date().getTime();
15172                 orderDialogs();
15173             }
15174             return dlg;
15175         },
15176
15177         /**
15178          * Sends the specified dialog to the back
15179          * @param {String/Object} dlg The id of the dialog or a dialog
15180          * @return {Roo.BasicDialog} this
15181          */
15182         sendToBack : function(dlg){
15183             dlg = this.get(dlg);
15184             dlg._lastAccess = -(new Date().getTime());
15185             orderDialogs();
15186             return dlg;
15187         },
15188
15189         /**
15190          * Hides all dialogs
15191          */
15192         hideAll : function(){
15193             for(var id in list){
15194                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15195                     list[id].hide();
15196                 }
15197             }
15198         }
15199     };
15200 }();
15201
15202 /**
15203  * @class Roo.LayoutDialog
15204  * @extends Roo.BasicDialog
15205  * Dialog which provides adjustments for working with a layout in a Dialog.
15206  * Add your necessary layout config options to the dialog's config.<br>
15207  * Example usage (including a nested layout):
15208  * <pre><code>
15209 if(!dialog){
15210     dialog = new Roo.LayoutDialog("download-dlg", {
15211         modal: true,
15212         width:600,
15213         height:450,
15214         shadow:true,
15215         minWidth:500,
15216         minHeight:350,
15217         autoTabs:true,
15218         proxyDrag:true,
15219         // layout config merges with the dialog config
15220         center:{
15221             tabPosition: "top",
15222             alwaysShowTabs: true
15223         }
15224     });
15225     dialog.addKeyListener(27, dialog.hide, dialog);
15226     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15227     dialog.addButton("Build It!", this.getDownload, this);
15228
15229     // we can even add nested layouts
15230     var innerLayout = new Roo.BorderLayout("dl-inner", {
15231         east: {
15232             initialSize: 200,
15233             autoScroll:true,
15234             split:true
15235         },
15236         center: {
15237             autoScroll:true
15238         }
15239     });
15240     innerLayout.beginUpdate();
15241     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15242     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15243     innerLayout.endUpdate(true);
15244
15245     var layout = dialog.getLayout();
15246     layout.beginUpdate();
15247     layout.add("center", new Roo.ContentPanel("standard-panel",
15248                         {title: "Download the Source", fitToFrame:true}));
15249     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15250                {title: "Build your own roo.js"}));
15251     layout.getRegion("center").showPanel(sp);
15252     layout.endUpdate();
15253 }
15254 </code></pre>
15255     * @constructor
15256     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15257     * @param {Object} config configuration options
15258   */
15259 Roo.LayoutDialog = function(el, cfg){
15260     
15261     var config=  cfg;
15262     if (typeof(cfg) == 'undefined') {
15263         config = Roo.apply({}, el);
15264         // not sure why we use documentElement here.. - it should always be body.
15265         // IE7 borks horribly if we use documentElement.
15266         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15267         //config.autoCreate = true;
15268     }
15269     
15270     
15271     config.autoTabs = false;
15272     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15273     this.body.setStyle({overflow:"hidden", position:"relative"});
15274     this.layout = new Roo.BorderLayout(this.body.dom, config);
15275     this.layout.monitorWindowResize = false;
15276     this.el.addClass("x-dlg-auto-layout");
15277     // fix case when center region overwrites center function
15278     this.center = Roo.BasicDialog.prototype.center;
15279     this.on("show", this.layout.layout, this.layout, true);
15280     if (config.items) {
15281         var xitems = config.items;
15282         delete config.items;
15283         Roo.each(xitems, this.addxtype, this);
15284     }
15285     
15286     
15287 };
15288 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15289     /**
15290      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15291      * @deprecated
15292      */
15293     endUpdate : function(){
15294         this.layout.endUpdate();
15295     },
15296
15297     /**
15298      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15299      *  @deprecated
15300      */
15301     beginUpdate : function(){
15302         this.layout.beginUpdate();
15303     },
15304
15305     /**
15306      * Get the BorderLayout for this dialog
15307      * @return {Roo.BorderLayout}
15308      */
15309     getLayout : function(){
15310         return this.layout;
15311     },
15312
15313     showEl : function(){
15314         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15315         if(Roo.isIE7){
15316             this.layout.layout();
15317         }
15318     },
15319
15320     // private
15321     // Use the syncHeightBeforeShow config option to control this automatically
15322     syncBodyHeight : function(){
15323         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15324         if(this.layout){this.layout.layout();}
15325     },
15326     
15327       /**
15328      * Add an xtype element (actually adds to the layout.)
15329      * @return {Object} xdata xtype object data.
15330      */
15331     
15332     addxtype : function(c) {
15333         return this.layout.addxtype(c);
15334     }
15335 });/*
15336  * Based on:
15337  * Ext JS Library 1.1.1
15338  * Copyright(c) 2006-2007, Ext JS, LLC.
15339  *
15340  * Originally Released Under LGPL - original licence link has changed is not relivant.
15341  *
15342  * Fork - LGPL
15343  * <script type="text/javascript">
15344  */
15345  
15346 /**
15347  * @class Roo.MessageBox
15348  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15349  * Example usage:
15350  *<pre><code>
15351 // Basic alert:
15352 Roo.Msg.alert('Status', 'Changes saved successfully.');
15353
15354 // Prompt for user data:
15355 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15356     if (btn == 'ok'){
15357         // process text value...
15358     }
15359 });
15360
15361 // Show a dialog using config options:
15362 Roo.Msg.show({
15363    title:'Save Changes?',
15364    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15365    buttons: Roo.Msg.YESNOCANCEL,
15366    fn: processResult,
15367    animEl: 'elId'
15368 });
15369 </code></pre>
15370  * @singleton
15371  */
15372 Roo.MessageBox = function(){
15373     var dlg, opt, mask, waitTimer;
15374     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15375     var buttons, activeTextEl, bwidth;
15376
15377     // private
15378     var handleButton = function(button){
15379         dlg.hide();
15380         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15381     };
15382
15383     // private
15384     var handleHide = function(){
15385         if(opt && opt.cls){
15386             dlg.el.removeClass(opt.cls);
15387         }
15388         if(waitTimer){
15389             Roo.TaskMgr.stop(waitTimer);
15390             waitTimer = null;
15391         }
15392     };
15393
15394     // private
15395     var updateButtons = function(b){
15396         var width = 0;
15397         if(!b){
15398             buttons["ok"].hide();
15399             buttons["cancel"].hide();
15400             buttons["yes"].hide();
15401             buttons["no"].hide();
15402             dlg.footer.dom.style.display = 'none';
15403             return width;
15404         }
15405         dlg.footer.dom.style.display = '';
15406         for(var k in buttons){
15407             if(typeof buttons[k] != "function"){
15408                 if(b[k]){
15409                     buttons[k].show();
15410                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15411                     width += buttons[k].el.getWidth()+15;
15412                 }else{
15413                     buttons[k].hide();
15414                 }
15415             }
15416         }
15417         return width;
15418     };
15419
15420     // private
15421     var handleEsc = function(d, k, e){
15422         if(opt && opt.closable !== false){
15423             dlg.hide();
15424         }
15425         if(e){
15426             e.stopEvent();
15427         }
15428     };
15429
15430     return {
15431         /**
15432          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15433          * @return {Roo.BasicDialog} The BasicDialog element
15434          */
15435         getDialog : function(){
15436            if(!dlg){
15437                 dlg = new Roo.BasicDialog("x-msg-box", {
15438                     autoCreate : true,
15439                     shadow: true,
15440                     draggable: true,
15441                     resizable:false,
15442                     constraintoviewport:false,
15443                     fixedcenter:true,
15444                     collapsible : false,
15445                     shim:true,
15446                     modal: true,
15447                     width:400, height:100,
15448                     buttonAlign:"center",
15449                     closeClick : function(){
15450                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15451                             handleButton("no");
15452                         }else{
15453                             handleButton("cancel");
15454                         }
15455                     }
15456                 });
15457                 dlg.on("hide", handleHide);
15458                 mask = dlg.mask;
15459                 dlg.addKeyListener(27, handleEsc);
15460                 buttons = {};
15461                 var bt = this.buttonText;
15462                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15463                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15464                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15465                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15466                 bodyEl = dlg.body.createChild({
15467
15468                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15469                 });
15470                 msgEl = bodyEl.dom.firstChild;
15471                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15472                 textboxEl.enableDisplayMode();
15473                 textboxEl.addKeyListener([10,13], function(){
15474                     if(dlg.isVisible() && opt && opt.buttons){
15475                         if(opt.buttons.ok){
15476                             handleButton("ok");
15477                         }else if(opt.buttons.yes){
15478                             handleButton("yes");
15479                         }
15480                     }
15481                 });
15482                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15483                 textareaEl.enableDisplayMode();
15484                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15485                 progressEl.enableDisplayMode();
15486                 var pf = progressEl.dom.firstChild;
15487                 if (pf) {
15488                     pp = Roo.get(pf.firstChild);
15489                     pp.setHeight(pf.offsetHeight);
15490                 }
15491                 
15492             }
15493             return dlg;
15494         },
15495
15496         /**
15497          * Updates the message box body text
15498          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15499          * the XHTML-compliant non-breaking space character '&amp;#160;')
15500          * @return {Roo.MessageBox} This message box
15501          */
15502         updateText : function(text){
15503             if(!dlg.isVisible() && !opt.width){
15504                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15505             }
15506             msgEl.innerHTML = text || '&#160;';
15507             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15508                         Math.max(opt.minWidth || this.minWidth, bwidth));
15509             if(opt.prompt){
15510                 activeTextEl.setWidth(w);
15511             }
15512             if(dlg.isVisible()){
15513                 dlg.fixedcenter = false;
15514             }
15515             dlg.setContentSize(w, bodyEl.getHeight());
15516             if(dlg.isVisible()){
15517                 dlg.fixedcenter = true;
15518             }
15519             return this;
15520         },
15521
15522         /**
15523          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15524          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15525          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15526          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15527          * @return {Roo.MessageBox} This message box
15528          */
15529         updateProgress : function(value, text){
15530             if(text){
15531                 this.updateText(text);
15532             }
15533             if (pp) { // weird bug on my firefox - for some reason this is not defined
15534                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15535             }
15536             return this;
15537         },        
15538
15539         /**
15540          * Returns true if the message box is currently displayed
15541          * @return {Boolean} True if the message box is visible, else false
15542          */
15543         isVisible : function(){
15544             return dlg && dlg.isVisible();  
15545         },
15546
15547         /**
15548          * Hides the message box if it is displayed
15549          */
15550         hide : function(){
15551             if(this.isVisible()){
15552                 dlg.hide();
15553             }  
15554         },
15555
15556         /**
15557          * Displays a new message box, or reinitializes an existing message box, based on the config options
15558          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15559          * The following config object properties are supported:
15560          * <pre>
15561 Property    Type             Description
15562 ----------  ---------------  ------------------------------------------------------------------------------------
15563 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15564                                    closes (defaults to undefined)
15565 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15566                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15567 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15568                                    progress and wait dialogs will ignore this property and always hide the
15569                                    close button as they can only be closed programmatically.
15570 cls               String           A custom CSS class to apply to the message box element
15571 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15572                                    displayed (defaults to 75)
15573 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15574                                    function will be btn (the name of the button that was clicked, if applicable,
15575                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15576                                    Progress and wait dialogs will ignore this option since they do not respond to
15577                                    user actions and can only be closed programmatically, so any required function
15578                                    should be called by the same code after it closes the dialog.
15579 icon              String           A CSS class that provides a background image to be used as an icon for
15580                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15581 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15582 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15583 modal             Boolean          False to allow user interaction with the page while the message box is
15584                                    displayed (defaults to true)
15585 msg               String           A string that will replace the existing message box body text (defaults
15586                                    to the XHTML-compliant non-breaking space character '&#160;')
15587 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15588 progress          Boolean          True to display a progress bar (defaults to false)
15589 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15590 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15591 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15592 title             String           The title text
15593 value             String           The string value to set into the active textbox element if displayed
15594 wait              Boolean          True to display a progress bar (defaults to false)
15595 width             Number           The width of the dialog in pixels
15596 </pre>
15597          *
15598          * Example usage:
15599          * <pre><code>
15600 Roo.Msg.show({
15601    title: 'Address',
15602    msg: 'Please enter your address:',
15603    width: 300,
15604    buttons: Roo.MessageBox.OKCANCEL,
15605    multiline: true,
15606    fn: saveAddress,
15607    animEl: 'addAddressBtn'
15608 });
15609 </code></pre>
15610          * @param {Object} config Configuration options
15611          * @return {Roo.MessageBox} This message box
15612          */
15613         show : function(options){
15614             if(this.isVisible()){
15615                 this.hide();
15616             }
15617             var d = this.getDialog();
15618             opt = options;
15619             d.setTitle(opt.title || "&#160;");
15620             d.close.setDisplayed(opt.closable !== false);
15621             activeTextEl = textboxEl;
15622             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15623             if(opt.prompt){
15624                 if(opt.multiline){
15625                     textboxEl.hide();
15626                     textareaEl.show();
15627                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15628                         opt.multiline : this.defaultTextHeight);
15629                     activeTextEl = textareaEl;
15630                 }else{
15631                     textboxEl.show();
15632                     textareaEl.hide();
15633                 }
15634             }else{
15635                 textboxEl.hide();
15636                 textareaEl.hide();
15637             }
15638             progressEl.setDisplayed(opt.progress === true);
15639             this.updateProgress(0);
15640             activeTextEl.dom.value = opt.value || "";
15641             if(opt.prompt){
15642                 dlg.setDefaultButton(activeTextEl);
15643             }else{
15644                 var bs = opt.buttons;
15645                 var db = null;
15646                 if(bs && bs.ok){
15647                     db = buttons["ok"];
15648                 }else if(bs && bs.yes){
15649                     db = buttons["yes"];
15650                 }
15651                 dlg.setDefaultButton(db);
15652             }
15653             bwidth = updateButtons(opt.buttons);
15654             this.updateText(opt.msg);
15655             if(opt.cls){
15656                 d.el.addClass(opt.cls);
15657             }
15658             d.proxyDrag = opt.proxyDrag === true;
15659             d.modal = opt.modal !== false;
15660             d.mask = opt.modal !== false ? mask : false;
15661             if(!d.isVisible()){
15662                 // force it to the end of the z-index stack so it gets a cursor in FF
15663                 document.body.appendChild(dlg.el.dom);
15664                 d.animateTarget = null;
15665                 d.show(options.animEl);
15666             }
15667             return this;
15668         },
15669
15670         /**
15671          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15672          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15673          * and closing the message box when the process is complete.
15674          * @param {String} title The title bar text
15675          * @param {String} msg The message box body text
15676          * @return {Roo.MessageBox} This message box
15677          */
15678         progress : function(title, msg){
15679             this.show({
15680                 title : title,
15681                 msg : msg,
15682                 buttons: false,
15683                 progress:true,
15684                 closable:false,
15685                 minWidth: this.minProgressWidth,
15686                 modal : true
15687             });
15688             return this;
15689         },
15690
15691         /**
15692          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15693          * If a callback function is passed it will be called after the user clicks the button, and the
15694          * id of the button that was clicked will be passed as the only parameter to the callback
15695          * (could also be the top-right close button).
15696          * @param {String} title The title bar text
15697          * @param {String} msg The message box body text
15698          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15699          * @param {Object} scope (optional) The scope of the callback function
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         alert : function(title, msg, fn, scope){
15703             this.show({
15704                 title : title,
15705                 msg : msg,
15706                 buttons: this.OK,
15707                 fn: fn,
15708                 scope : scope,
15709                 modal : true
15710             });
15711             return this;
15712         },
15713
15714         /**
15715          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15716          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15717          * You are responsible for closing the message box when the process is complete.
15718          * @param {String} msg The message box body text
15719          * @param {String} title (optional) The title bar text
15720          * @return {Roo.MessageBox} This message box
15721          */
15722         wait : function(msg, title){
15723             this.show({
15724                 title : title,
15725                 msg : msg,
15726                 buttons: false,
15727                 closable:false,
15728                 progress:true,
15729                 modal:true,
15730                 width:300,
15731                 wait:true
15732             });
15733             waitTimer = Roo.TaskMgr.start({
15734                 run: function(i){
15735                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15736                 },
15737                 interval: 1000
15738             });
15739             return this;
15740         },
15741
15742         /**
15743          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15744          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15745          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15746          * @param {String} title The title bar text
15747          * @param {String} msg The message box body text
15748          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15749          * @param {Object} scope (optional) The scope of the callback function
15750          * @return {Roo.MessageBox} This message box
15751          */
15752         confirm : function(title, msg, fn, scope){
15753             this.show({
15754                 title : title,
15755                 msg : msg,
15756                 buttons: this.YESNO,
15757                 fn: fn,
15758                 scope : scope,
15759                 modal : true
15760             });
15761             return this;
15762         },
15763
15764         /**
15765          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15766          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15767          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15768          * (could also be the top-right close button) and the text that was entered will be passed as the two
15769          * parameters to the callback.
15770          * @param {String} title The title bar text
15771          * @param {String} msg The message box body text
15772          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15773          * @param {Object} scope (optional) The scope of the callback function
15774          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15775          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15776          * @return {Roo.MessageBox} This message box
15777          */
15778         prompt : function(title, msg, fn, scope, multiline){
15779             this.show({
15780                 title : title,
15781                 msg : msg,
15782                 buttons: this.OKCANCEL,
15783                 fn: fn,
15784                 minWidth:250,
15785                 scope : scope,
15786                 prompt:true,
15787                 multiline: multiline,
15788                 modal : true
15789             });
15790             return this;
15791         },
15792
15793         /**
15794          * Button config that displays a single OK button
15795          * @type Object
15796          */
15797         OK : {ok:true},
15798         /**
15799          * Button config that displays Yes and No buttons
15800          * @type Object
15801          */
15802         YESNO : {yes:true, no:true},
15803         /**
15804          * Button config that displays OK and Cancel buttons
15805          * @type Object
15806          */
15807         OKCANCEL : {ok:true, cancel:true},
15808         /**
15809          * Button config that displays Yes, No and Cancel buttons
15810          * @type Object
15811          */
15812         YESNOCANCEL : {yes:true, no:true, cancel:true},
15813
15814         /**
15815          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15816          * @type Number
15817          */
15818         defaultTextHeight : 75,
15819         /**
15820          * The maximum width in pixels of the message box (defaults to 600)
15821          * @type Number
15822          */
15823         maxWidth : 600,
15824         /**
15825          * The minimum width in pixels of the message box (defaults to 100)
15826          * @type Number
15827          */
15828         minWidth : 100,
15829         /**
15830          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15831          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15832          * @type Number
15833          */
15834         minProgressWidth : 250,
15835         /**
15836          * An object containing the default button text strings that can be overriden for localized language support.
15837          * Supported properties are: ok, cancel, yes and no.
15838          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15839          * @type Object
15840          */
15841         buttonText : {
15842             ok : "OK",
15843             cancel : "Cancel",
15844             yes : "Yes",
15845             no : "No"
15846         }
15847     };
15848 }();
15849
15850 /**
15851  * Shorthand for {@link Roo.MessageBox}
15852  */
15853 Roo.Msg = Roo.MessageBox;/*
15854  * Based on:
15855  * Ext JS Library 1.1.1
15856  * Copyright(c) 2006-2007, Ext JS, LLC.
15857  *
15858  * Originally Released Under LGPL - original licence link has changed is not relivant.
15859  *
15860  * Fork - LGPL
15861  * <script type="text/javascript">
15862  */
15863 /**
15864  * @class Roo.QuickTips
15865  * Provides attractive and customizable tooltips for any element.
15866  * @singleton
15867  */
15868 Roo.QuickTips = function(){
15869     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15870     var ce, bd, xy, dd;
15871     var visible = false, disabled = true, inited = false;
15872     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15873     
15874     var onOver = function(e){
15875         if(disabled){
15876             return;
15877         }
15878         var t = e.getTarget();
15879         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15880             return;
15881         }
15882         if(ce && t == ce.el){
15883             clearTimeout(hideProc);
15884             return;
15885         }
15886         if(t && tagEls[t.id]){
15887             tagEls[t.id].el = t;
15888             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15889             return;
15890         }
15891         var ttp, et = Roo.fly(t);
15892         var ns = cfg.namespace;
15893         if(tm.interceptTitles && t.title){
15894             ttp = t.title;
15895             t.qtip = ttp;
15896             t.removeAttribute("title");
15897             e.preventDefault();
15898         }else{
15899             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15900         }
15901         if(ttp){
15902             showProc = show.defer(tm.showDelay, tm, [{
15903                 el: t, 
15904                 text: ttp, 
15905                 width: et.getAttributeNS(ns, cfg.width),
15906                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15907                 title: et.getAttributeNS(ns, cfg.title),
15908                     cls: et.getAttributeNS(ns, cfg.cls)
15909             }]);
15910         }
15911     };
15912     
15913     var onOut = function(e){
15914         clearTimeout(showProc);
15915         var t = e.getTarget();
15916         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15917             hideProc = setTimeout(hide, tm.hideDelay);
15918         }
15919     };
15920     
15921     var onMove = function(e){
15922         if(disabled){
15923             return;
15924         }
15925         xy = e.getXY();
15926         xy[1] += 18;
15927         if(tm.trackMouse && ce){
15928             el.setXY(xy);
15929         }
15930     };
15931     
15932     var onDown = function(e){
15933         clearTimeout(showProc);
15934         clearTimeout(hideProc);
15935         if(!e.within(el)){
15936             if(tm.hideOnClick){
15937                 hide();
15938                 tm.disable();
15939                 tm.enable.defer(100, tm);
15940             }
15941         }
15942     };
15943     
15944     var getPad = function(){
15945         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15946     };
15947
15948     var show = function(o){
15949         if(disabled){
15950             return;
15951         }
15952         clearTimeout(dismissProc);
15953         ce = o;
15954         if(removeCls){ // in case manually hidden
15955             el.removeClass(removeCls);
15956             removeCls = null;
15957         }
15958         if(ce.cls){
15959             el.addClass(ce.cls);
15960             removeCls = ce.cls;
15961         }
15962         if(ce.title){
15963             tipTitle.update(ce.title);
15964             tipTitle.show();
15965         }else{
15966             tipTitle.update('');
15967             tipTitle.hide();
15968         }
15969         el.dom.style.width  = tm.maxWidth+'px';
15970         //tipBody.dom.style.width = '';
15971         tipBodyText.update(o.text);
15972         var p = getPad(), w = ce.width;
15973         if(!w){
15974             var td = tipBodyText.dom;
15975             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15976             if(aw > tm.maxWidth){
15977                 w = tm.maxWidth;
15978             }else if(aw < tm.minWidth){
15979                 w = tm.minWidth;
15980             }else{
15981                 w = aw;
15982             }
15983         }
15984         //tipBody.setWidth(w);
15985         el.setWidth(parseInt(w, 10) + p);
15986         if(ce.autoHide === false){
15987             close.setDisplayed(true);
15988             if(dd){
15989                 dd.unlock();
15990             }
15991         }else{
15992             close.setDisplayed(false);
15993             if(dd){
15994                 dd.lock();
15995             }
15996         }
15997         if(xy){
15998             el.avoidY = xy[1]-18;
15999             el.setXY(xy);
16000         }
16001         if(tm.animate){
16002             el.setOpacity(.1);
16003             el.setStyle("visibility", "visible");
16004             el.fadeIn({callback: afterShow});
16005         }else{
16006             afterShow();
16007         }
16008     };
16009     
16010     var afterShow = function(){
16011         if(ce){
16012             el.show();
16013             esc.enable();
16014             if(tm.autoDismiss && ce.autoHide !== false){
16015                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16016             }
16017         }
16018     };
16019     
16020     var hide = function(noanim){
16021         clearTimeout(dismissProc);
16022         clearTimeout(hideProc);
16023         ce = null;
16024         if(el.isVisible()){
16025             esc.disable();
16026             if(noanim !== true && tm.animate){
16027                 el.fadeOut({callback: afterHide});
16028             }else{
16029                 afterHide();
16030             } 
16031         }
16032     };
16033     
16034     var afterHide = function(){
16035         el.hide();
16036         if(removeCls){
16037             el.removeClass(removeCls);
16038             removeCls = null;
16039         }
16040     };
16041     
16042     return {
16043         /**
16044         * @cfg {Number} minWidth
16045         * The minimum width of the quick tip (defaults to 40)
16046         */
16047        minWidth : 40,
16048         /**
16049         * @cfg {Number} maxWidth
16050         * The maximum width of the quick tip (defaults to 300)
16051         */
16052        maxWidth : 300,
16053         /**
16054         * @cfg {Boolean} interceptTitles
16055         * True to automatically use the element's DOM title value if available (defaults to false)
16056         */
16057        interceptTitles : false,
16058         /**
16059         * @cfg {Boolean} trackMouse
16060         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16061         */
16062        trackMouse : false,
16063         /**
16064         * @cfg {Boolean} hideOnClick
16065         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16066         */
16067        hideOnClick : true,
16068         /**
16069         * @cfg {Number} showDelay
16070         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16071         */
16072        showDelay : 500,
16073         /**
16074         * @cfg {Number} hideDelay
16075         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16076         */
16077        hideDelay : 200,
16078         /**
16079         * @cfg {Boolean} autoHide
16080         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16081         * Used in conjunction with hideDelay.
16082         */
16083        autoHide : true,
16084         /**
16085         * @cfg {Boolean}
16086         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16087         * (defaults to true).  Used in conjunction with autoDismissDelay.
16088         */
16089        autoDismiss : true,
16090         /**
16091         * @cfg {Number}
16092         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16093         */
16094        autoDismissDelay : 5000,
16095        /**
16096         * @cfg {Boolean} animate
16097         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16098         */
16099        animate : false,
16100
16101        /**
16102         * @cfg {String} title
16103         * Title text to display (defaults to '').  This can be any valid HTML markup.
16104         */
16105         title: '',
16106        /**
16107         * @cfg {String} text
16108         * Body text to display (defaults to '').  This can be any valid HTML markup.
16109         */
16110         text : '',
16111        /**
16112         * @cfg {String} cls
16113         * A CSS class to apply to the base quick tip element (defaults to '').
16114         */
16115         cls : '',
16116        /**
16117         * @cfg {Number} width
16118         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16119         * minWidth or maxWidth.
16120         */
16121         width : null,
16122
16123     /**
16124      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16125      * or display QuickTips in a page.
16126      */
16127        init : function(){
16128           tm = Roo.QuickTips;
16129           cfg = tm.tagConfig;
16130           if(!inited){
16131               if(!Roo.isReady){ // allow calling of init() before onReady
16132                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16133                   return;
16134               }
16135               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16136               el.fxDefaults = {stopFx: true};
16137               // maximum custom styling
16138               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16139               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16140               tipTitle = el.child('h3');
16141               tipTitle.enableDisplayMode("block");
16142               tipBody = el.child('div.x-tip-bd');
16143               tipBodyText = el.child('div.x-tip-bd-inner');
16144               //bdLeft = el.child('div.x-tip-bd-left');
16145               //bdRight = el.child('div.x-tip-bd-right');
16146               close = el.child('div.x-tip-close');
16147               close.enableDisplayMode("block");
16148               close.on("click", hide);
16149               var d = Roo.get(document);
16150               d.on("mousedown", onDown);
16151               d.on("mouseover", onOver);
16152               d.on("mouseout", onOut);
16153               d.on("mousemove", onMove);
16154               esc = d.addKeyListener(27, hide);
16155               esc.disable();
16156               if(Roo.dd.DD){
16157                   dd = el.initDD("default", null, {
16158                       onDrag : function(){
16159                           el.sync();  
16160                       }
16161                   });
16162                   dd.setHandleElId(tipTitle.id);
16163                   dd.lock();
16164               }
16165               inited = true;
16166           }
16167           this.enable(); 
16168        },
16169
16170     /**
16171      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16172      * are supported:
16173      * <pre>
16174 Property    Type                   Description
16175 ----------  ---------------------  ------------------------------------------------------------------------
16176 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16177      * </ul>
16178      * @param {Object} config The config object
16179      */
16180        register : function(config){
16181            var cs = config instanceof Array ? config : arguments;
16182            for(var i = 0, len = cs.length; i < len; i++) {
16183                var c = cs[i];
16184                var target = c.target;
16185                if(target){
16186                    if(target instanceof Array){
16187                        for(var j = 0, jlen = target.length; j < jlen; j++){
16188                            tagEls[target[j]] = c;
16189                        }
16190                    }else{
16191                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16192                    }
16193                }
16194            }
16195        },
16196
16197     /**
16198      * Removes this quick tip from its element and destroys it.
16199      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16200      */
16201        unregister : function(el){
16202            delete tagEls[Roo.id(el)];
16203        },
16204
16205     /**
16206      * Enable this quick tip.
16207      */
16208        enable : function(){
16209            if(inited && disabled){
16210                locks.pop();
16211                if(locks.length < 1){
16212                    disabled = false;
16213                }
16214            }
16215        },
16216
16217     /**
16218      * Disable this quick tip.
16219      */
16220        disable : function(){
16221           disabled = true;
16222           clearTimeout(showProc);
16223           clearTimeout(hideProc);
16224           clearTimeout(dismissProc);
16225           if(ce){
16226               hide(true);
16227           }
16228           locks.push(1);
16229        },
16230
16231     /**
16232      * Returns true if the quick tip is enabled, else false.
16233      */
16234        isEnabled : function(){
16235             return !disabled;
16236        },
16237
16238         // private
16239        tagConfig : {
16240            namespace : "ext",
16241            attribute : "qtip",
16242            width : "width",
16243            target : "target",
16244            title : "qtitle",
16245            hide : "hide",
16246            cls : "qclass"
16247        }
16248    };
16249 }();
16250
16251 // backwards compat
16252 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16253  * Based on:
16254  * Ext JS Library 1.1.1
16255  * Copyright(c) 2006-2007, Ext JS, LLC.
16256  *
16257  * Originally Released Under LGPL - original licence link has changed is not relivant.
16258  *
16259  * Fork - LGPL
16260  * <script type="text/javascript">
16261  */
16262  
16263
16264 /**
16265  * @class Roo.tree.TreePanel
16266  * @extends Roo.data.Tree
16267
16268  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16269  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16270  * @cfg {Boolean} enableDD true to enable drag and drop
16271  * @cfg {Boolean} enableDrag true to enable just drag
16272  * @cfg {Boolean} enableDrop true to enable just drop
16273  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16274  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16275  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16276  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16277  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16278  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16279  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16280  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16281  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16282  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16283  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16284  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16285  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16286  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16287  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16288  * 
16289  * @constructor
16290  * @param {String/HTMLElement/Element} el The container element
16291  * @param {Object} config
16292  */
16293 Roo.tree.TreePanel = function(el, config){
16294     var root = false;
16295     var loader = false;
16296     if (config.root) {
16297         root = config.root;
16298         delete config.root;
16299     }
16300     if (config.loader) {
16301         loader = config.loader;
16302         delete config.loader;
16303     }
16304     
16305     Roo.apply(this, config);
16306     Roo.tree.TreePanel.superclass.constructor.call(this);
16307     this.el = Roo.get(el);
16308     this.el.addClass('x-tree');
16309     //console.log(root);
16310     if (root) {
16311         this.setRootNode( Roo.factory(root, Roo.tree));
16312     }
16313     if (loader) {
16314         this.loader = Roo.factory(loader, Roo.tree);
16315     }
16316    /**
16317     * Read-only. The id of the container element becomes this TreePanel's id.
16318     */
16319    this.id = this.el.id;
16320    this.addEvents({
16321         /**
16322         * @event beforeload
16323         * Fires before a node is loaded, return false to cancel
16324         * @param {Node} node The node being loaded
16325         */
16326         "beforeload" : true,
16327         /**
16328         * @event load
16329         * Fires when a node is loaded
16330         * @param {Node} node The node that was loaded
16331         */
16332         "load" : true,
16333         /**
16334         * @event textchange
16335         * Fires when the text for a node is changed
16336         * @param {Node} node The node
16337         * @param {String} text The new text
16338         * @param {String} oldText The old text
16339         */
16340         "textchange" : true,
16341         /**
16342         * @event beforeexpand
16343         * Fires before a node is expanded, return false to cancel.
16344         * @param {Node} node The node
16345         * @param {Boolean} deep
16346         * @param {Boolean} anim
16347         */
16348         "beforeexpand" : true,
16349         /**
16350         * @event beforecollapse
16351         * Fires before a node is collapsed, return false to cancel.
16352         * @param {Node} node The node
16353         * @param {Boolean} deep
16354         * @param {Boolean} anim
16355         */
16356         "beforecollapse" : true,
16357         /**
16358         * @event expand
16359         * Fires when a node is expanded
16360         * @param {Node} node The node
16361         */
16362         "expand" : true,
16363         /**
16364         * @event disabledchange
16365         * Fires when the disabled status of a node changes
16366         * @param {Node} node The node
16367         * @param {Boolean} disabled
16368         */
16369         "disabledchange" : true,
16370         /**
16371         * @event collapse
16372         * Fires when a node is collapsed
16373         * @param {Node} node The node
16374         */
16375         "collapse" : true,
16376         /**
16377         * @event beforeclick
16378         * Fires before click processing on a node. Return false to cancel the default action.
16379         * @param {Node} node The node
16380         * @param {Roo.EventObject} e The event object
16381         */
16382         "beforeclick":true,
16383         /**
16384         * @event checkchange
16385         * Fires when a node with a checkbox's checked property changes
16386         * @param {Node} this This node
16387         * @param {Boolean} checked
16388         */
16389         "checkchange":true,
16390         /**
16391         * @event click
16392         * Fires when a node is clicked
16393         * @param {Node} node The node
16394         * @param {Roo.EventObject} e The event object
16395         */
16396         "click":true,
16397         /**
16398         * @event dblclick
16399         * Fires when a node is double clicked
16400         * @param {Node} node The node
16401         * @param {Roo.EventObject} e The event object
16402         */
16403         "dblclick":true,
16404         /**
16405         * @event contextmenu
16406         * Fires when a node is right clicked
16407         * @param {Node} node The node
16408         * @param {Roo.EventObject} e The event object
16409         */
16410         "contextmenu":true,
16411         /**
16412         * @event beforechildrenrendered
16413         * Fires right before the child nodes for a node are rendered
16414         * @param {Node} node The node
16415         */
16416         "beforechildrenrendered":true,
16417        /**
16418              * @event startdrag
16419              * Fires when a node starts being dragged
16420              * @param {Roo.tree.TreePanel} this
16421              * @param {Roo.tree.TreeNode} node
16422              * @param {event} e The raw browser event
16423              */ 
16424             "startdrag" : true,
16425             /**
16426              * @event enddrag
16427              * Fires when a drag operation is complete
16428              * @param {Roo.tree.TreePanel} this
16429              * @param {Roo.tree.TreeNode} node
16430              * @param {event} e The raw browser event
16431              */
16432             "enddrag" : true,
16433             /**
16434              * @event dragdrop
16435              * Fires when a dragged node is dropped on a valid DD target
16436              * @param {Roo.tree.TreePanel} this
16437              * @param {Roo.tree.TreeNode} node
16438              * @param {DD} dd The dd it was dropped on
16439              * @param {event} e The raw browser event
16440              */
16441             "dragdrop" : true,
16442             /**
16443              * @event beforenodedrop
16444              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16445              * passed to handlers has the following properties:<br />
16446              * <ul style="padding:5px;padding-left:16px;">
16447              * <li>tree - The TreePanel</li>
16448              * <li>target - The node being targeted for the drop</li>
16449              * <li>data - The drag data from the drag source</li>
16450              * <li>point - The point of the drop - append, above or below</li>
16451              * <li>source - The drag source</li>
16452              * <li>rawEvent - Raw mouse event</li>
16453              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16454              * to be inserted by setting them on this object.</li>
16455              * <li>cancel - Set this to true to cancel the drop.</li>
16456              * </ul>
16457              * @param {Object} dropEvent
16458              */
16459             "beforenodedrop" : true,
16460             /**
16461              * @event nodedrop
16462              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16463              * passed to handlers has the following properties:<br />
16464              * <ul style="padding:5px;padding-left:16px;">
16465              * <li>tree - The TreePanel</li>
16466              * <li>target - The node being targeted for the drop</li>
16467              * <li>data - The drag data from the drag source</li>
16468              * <li>point - The point of the drop - append, above or below</li>
16469              * <li>source - The drag source</li>
16470              * <li>rawEvent - Raw mouse event</li>
16471              * <li>dropNode - Dropped node(s).</li>
16472              * </ul>
16473              * @param {Object} dropEvent
16474              */
16475             "nodedrop" : true,
16476              /**
16477              * @event nodedragover
16478              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16479              * passed to handlers has the following properties:<br />
16480              * <ul style="padding:5px;padding-left:16px;">
16481              * <li>tree - The TreePanel</li>
16482              * <li>target - The node being targeted for the drop</li>
16483              * <li>data - The drag data from the drag source</li>
16484              * <li>point - The point of the drop - append, above or below</li>
16485              * <li>source - The drag source</li>
16486              * <li>rawEvent - Raw mouse event</li>
16487              * <li>dropNode - Drop node(s) provided by the source.</li>
16488              * <li>cancel - Set this to true to signal drop not allowed.</li>
16489              * </ul>
16490              * @param {Object} dragOverEvent
16491              */
16492             "nodedragover" : true
16493         
16494    });
16495    if(this.singleExpand){
16496        this.on("beforeexpand", this.restrictExpand, this);
16497    }
16498 };
16499 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16500     rootVisible : true,
16501     animate: Roo.enableFx,
16502     lines : true,
16503     enableDD : false,
16504     hlDrop : Roo.enableFx,
16505   
16506     renderer: false,
16507     
16508     rendererTip: false,
16509     // private
16510     restrictExpand : function(node){
16511         var p = node.parentNode;
16512         if(p){
16513             if(p.expandedChild && p.expandedChild.parentNode == p){
16514                 p.expandedChild.collapse();
16515             }
16516             p.expandedChild = node;
16517         }
16518     },
16519
16520     // private override
16521     setRootNode : function(node){
16522         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16523         if(!this.rootVisible){
16524             node.ui = new Roo.tree.RootTreeNodeUI(node);
16525         }
16526         return node;
16527     },
16528
16529     /**
16530      * Returns the container element for this TreePanel
16531      */
16532     getEl : function(){
16533         return this.el;
16534     },
16535
16536     /**
16537      * Returns the default TreeLoader for this TreePanel
16538      */
16539     getLoader : function(){
16540         return this.loader;
16541     },
16542
16543     /**
16544      * Expand all nodes
16545      */
16546     expandAll : function(){
16547         this.root.expand(true);
16548     },
16549
16550     /**
16551      * Collapse all nodes
16552      */
16553     collapseAll : function(){
16554         this.root.collapse(true);
16555     },
16556
16557     /**
16558      * Returns the selection model used by this TreePanel
16559      */
16560     getSelectionModel : function(){
16561         if(!this.selModel){
16562             this.selModel = new Roo.tree.DefaultSelectionModel();
16563         }
16564         return this.selModel;
16565     },
16566
16567     /**
16568      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16569      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16570      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16571      * @return {Array}
16572      */
16573     getChecked : function(a, startNode){
16574         startNode = startNode || this.root;
16575         var r = [];
16576         var f = function(){
16577             if(this.attributes.checked){
16578                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16579             }
16580         }
16581         startNode.cascade(f);
16582         return r;
16583     },
16584
16585     /**
16586      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16587      * @param {String} path
16588      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16589      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16590      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16591      */
16592     expandPath : function(path, attr, callback){
16593         attr = attr || "id";
16594         var keys = path.split(this.pathSeparator);
16595         var curNode = this.root;
16596         if(curNode.attributes[attr] != keys[1]){ // invalid root
16597             if(callback){
16598                 callback(false, null);
16599             }
16600             return;
16601         }
16602         var index = 1;
16603         var f = function(){
16604             if(++index == keys.length){
16605                 if(callback){
16606                     callback(true, curNode);
16607                 }
16608                 return;
16609             }
16610             var c = curNode.findChild(attr, keys[index]);
16611             if(!c){
16612                 if(callback){
16613                     callback(false, curNode);
16614                 }
16615                 return;
16616             }
16617             curNode = c;
16618             c.expand(false, false, f);
16619         };
16620         curNode.expand(false, false, f);
16621     },
16622
16623     /**
16624      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16625      * @param {String} path
16626      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16627      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16628      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16629      */
16630     selectPath : function(path, attr, callback){
16631         attr = attr || "id";
16632         var keys = path.split(this.pathSeparator);
16633         var v = keys.pop();
16634         if(keys.length > 0){
16635             var f = function(success, node){
16636                 if(success && node){
16637                     var n = node.findChild(attr, v);
16638                     if(n){
16639                         n.select();
16640                         if(callback){
16641                             callback(true, n);
16642                         }
16643                     }else if(callback){
16644                         callback(false, n);
16645                     }
16646                 }else{
16647                     if(callback){
16648                         callback(false, n);
16649                     }
16650                 }
16651             };
16652             this.expandPath(keys.join(this.pathSeparator), attr, f);
16653         }else{
16654             this.root.select();
16655             if(callback){
16656                 callback(true, this.root);
16657             }
16658         }
16659     },
16660
16661     getTreeEl : function(){
16662         return this.el;
16663     },
16664
16665     /**
16666      * Trigger rendering of this TreePanel
16667      */
16668     render : function(){
16669         if (this.innerCt) {
16670             return this; // stop it rendering more than once!!
16671         }
16672         
16673         this.innerCt = this.el.createChild({tag:"ul",
16674                cls:"x-tree-root-ct " +
16675                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16676
16677         if(this.containerScroll){
16678             Roo.dd.ScrollManager.register(this.el);
16679         }
16680         if((this.enableDD || this.enableDrop) && !this.dropZone){
16681            /**
16682             * The dropZone used by this tree if drop is enabled
16683             * @type Roo.tree.TreeDropZone
16684             */
16685              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16686                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16687            });
16688         }
16689         if((this.enableDD || this.enableDrag) && !this.dragZone){
16690            /**
16691             * The dragZone used by this tree if drag is enabled
16692             * @type Roo.tree.TreeDragZone
16693             */
16694             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16695                ddGroup: this.ddGroup || "TreeDD",
16696                scroll: this.ddScroll
16697            });
16698         }
16699         this.getSelectionModel().init(this);
16700         if (!this.root) {
16701             console.log("ROOT not set in tree");
16702             return;
16703         }
16704         this.root.render();
16705         if(!this.rootVisible){
16706             this.root.renderChildren();
16707         }
16708         return this;
16709     }
16710 });/*
16711  * Based on:
16712  * Ext JS Library 1.1.1
16713  * Copyright(c) 2006-2007, Ext JS, LLC.
16714  *
16715  * Originally Released Under LGPL - original licence link has changed is not relivant.
16716  *
16717  * Fork - LGPL
16718  * <script type="text/javascript">
16719  */
16720  
16721
16722 /**
16723  * @class Roo.tree.DefaultSelectionModel
16724  * @extends Roo.util.Observable
16725  * The default single selection for a TreePanel.
16726  */
16727 Roo.tree.DefaultSelectionModel = function(){
16728    this.selNode = null;
16729    
16730    this.addEvents({
16731        /**
16732         * @event selectionchange
16733         * Fires when the selected node changes
16734         * @param {DefaultSelectionModel} this
16735         * @param {TreeNode} node the new selection
16736         */
16737        "selectionchange" : true,
16738
16739        /**
16740         * @event beforeselect
16741         * Fires before the selected node changes, return false to cancel the change
16742         * @param {DefaultSelectionModel} this
16743         * @param {TreeNode} node the new selection
16744         * @param {TreeNode} node the old selection
16745         */
16746        "beforeselect" : true
16747    });
16748 };
16749
16750 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16751     init : function(tree){
16752         this.tree = tree;
16753         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16754         tree.on("click", this.onNodeClick, this);
16755     },
16756     
16757     onNodeClick : function(node, e){
16758         if (e.ctrlKey && this.selNode == node)  {
16759             this.unselect(node);
16760             return;
16761         }
16762         this.select(node);
16763     },
16764     
16765     /**
16766      * Select a node.
16767      * @param {TreeNode} node The node to select
16768      * @return {TreeNode} The selected node
16769      */
16770     select : function(node){
16771         var last = this.selNode;
16772         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16773             if(last){
16774                 last.ui.onSelectedChange(false);
16775             }
16776             this.selNode = node;
16777             node.ui.onSelectedChange(true);
16778             this.fireEvent("selectionchange", this, node, last);
16779         }
16780         return node;
16781     },
16782     
16783     /**
16784      * Deselect a node.
16785      * @param {TreeNode} node The node to unselect
16786      */
16787     unselect : function(node){
16788         if(this.selNode == node){
16789             this.clearSelections();
16790         }    
16791     },
16792     
16793     /**
16794      * Clear all selections
16795      */
16796     clearSelections : function(){
16797         var n = this.selNode;
16798         if(n){
16799             n.ui.onSelectedChange(false);
16800             this.selNode = null;
16801             this.fireEvent("selectionchange", this, null);
16802         }
16803         return n;
16804     },
16805     
16806     /**
16807      * Get the selected node
16808      * @return {TreeNode} The selected node
16809      */
16810     getSelectedNode : function(){
16811         return this.selNode;    
16812     },
16813     
16814     /**
16815      * Returns true if the node is selected
16816      * @param {TreeNode} node The node to check
16817      * @return {Boolean}
16818      */
16819     isSelected : function(node){
16820         return this.selNode == node;  
16821     },
16822
16823     /**
16824      * Selects the node above the selected node in the tree, intelligently walking the nodes
16825      * @return TreeNode The new selection
16826      */
16827     selectPrevious : function(){
16828         var s = this.selNode || this.lastSelNode;
16829         if(!s){
16830             return null;
16831         }
16832         var ps = s.previousSibling;
16833         if(ps){
16834             if(!ps.isExpanded() || ps.childNodes.length < 1){
16835                 return this.select(ps);
16836             } else{
16837                 var lc = ps.lastChild;
16838                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16839                     lc = lc.lastChild;
16840                 }
16841                 return this.select(lc);
16842             }
16843         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16844             return this.select(s.parentNode);
16845         }
16846         return null;
16847     },
16848
16849     /**
16850      * Selects the node above the selected node in the tree, intelligently walking the nodes
16851      * @return TreeNode The new selection
16852      */
16853     selectNext : function(){
16854         var s = this.selNode || this.lastSelNode;
16855         if(!s){
16856             return null;
16857         }
16858         if(s.firstChild && s.isExpanded()){
16859              return this.select(s.firstChild);
16860          }else if(s.nextSibling){
16861              return this.select(s.nextSibling);
16862          }else if(s.parentNode){
16863             var newS = null;
16864             s.parentNode.bubble(function(){
16865                 if(this.nextSibling){
16866                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16867                     return false;
16868                 }
16869             });
16870             return newS;
16871          }
16872         return null;
16873     },
16874
16875     onKeyDown : function(e){
16876         var s = this.selNode || this.lastSelNode;
16877         // undesirable, but required
16878         var sm = this;
16879         if(!s){
16880             return;
16881         }
16882         var k = e.getKey();
16883         switch(k){
16884              case e.DOWN:
16885                  e.stopEvent();
16886                  this.selectNext();
16887              break;
16888              case e.UP:
16889                  e.stopEvent();
16890                  this.selectPrevious();
16891              break;
16892              case e.RIGHT:
16893                  e.preventDefault();
16894                  if(s.hasChildNodes()){
16895                      if(!s.isExpanded()){
16896                          s.expand();
16897                      }else if(s.firstChild){
16898                          this.select(s.firstChild, e);
16899                      }
16900                  }
16901              break;
16902              case e.LEFT:
16903                  e.preventDefault();
16904                  if(s.hasChildNodes() && s.isExpanded()){
16905                      s.collapse();
16906                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16907                      this.select(s.parentNode, e);
16908                  }
16909              break;
16910         };
16911     }
16912 });
16913
16914 /**
16915  * @class Roo.tree.MultiSelectionModel
16916  * @extends Roo.util.Observable
16917  * Multi selection for a TreePanel.
16918  */
16919 Roo.tree.MultiSelectionModel = function(){
16920    this.selNodes = [];
16921    this.selMap = {};
16922    this.addEvents({
16923        /**
16924         * @event selectionchange
16925         * Fires when the selected nodes change
16926         * @param {MultiSelectionModel} this
16927         * @param {Array} nodes Array of the selected nodes
16928         */
16929        "selectionchange" : true
16930    });
16931 };
16932
16933 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16934     init : function(tree){
16935         this.tree = tree;
16936         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16937         tree.on("click", this.onNodeClick, this);
16938     },
16939     
16940     onNodeClick : function(node, e){
16941         this.select(node, e, e.ctrlKey);
16942     },
16943     
16944     /**
16945      * Select a node.
16946      * @param {TreeNode} node The node to select
16947      * @param {EventObject} e (optional) An event associated with the selection
16948      * @param {Boolean} keepExisting True to retain existing selections
16949      * @return {TreeNode} The selected node
16950      */
16951     select : function(node, e, keepExisting){
16952         if(keepExisting !== true){
16953             this.clearSelections(true);
16954         }
16955         if(this.isSelected(node)){
16956             this.lastSelNode = node;
16957             return node;
16958         }
16959         this.selNodes.push(node);
16960         this.selMap[node.id] = node;
16961         this.lastSelNode = node;
16962         node.ui.onSelectedChange(true);
16963         this.fireEvent("selectionchange", this, this.selNodes);
16964         return node;
16965     },
16966     
16967     /**
16968      * Deselect a node.
16969      * @param {TreeNode} node The node to unselect
16970      */
16971     unselect : function(node){
16972         if(this.selMap[node.id]){
16973             node.ui.onSelectedChange(false);
16974             var sn = this.selNodes;
16975             var index = -1;
16976             if(sn.indexOf){
16977                 index = sn.indexOf(node);
16978             }else{
16979                 for(var i = 0, len = sn.length; i < len; i++){
16980                     if(sn[i] == node){
16981                         index = i;
16982                         break;
16983                     }
16984                 }
16985             }
16986             if(index != -1){
16987                 this.selNodes.splice(index, 1);
16988             }
16989             delete this.selMap[node.id];
16990             this.fireEvent("selectionchange", this, this.selNodes);
16991         }
16992     },
16993     
16994     /**
16995      * Clear all selections
16996      */
16997     clearSelections : function(suppressEvent){
16998         var sn = this.selNodes;
16999         if(sn.length > 0){
17000             for(var i = 0, len = sn.length; i < len; i++){
17001                 sn[i].ui.onSelectedChange(false);
17002             }
17003             this.selNodes = [];
17004             this.selMap = {};
17005             if(suppressEvent !== true){
17006                 this.fireEvent("selectionchange", this, this.selNodes);
17007             }
17008         }
17009     },
17010     
17011     /**
17012      * Returns true if the node is selected
17013      * @param {TreeNode} node The node to check
17014      * @return {Boolean}
17015      */
17016     isSelected : function(node){
17017         return this.selMap[node.id] ? true : false;  
17018     },
17019     
17020     /**
17021      * Returns an array of the selected nodes
17022      * @return {Array}
17023      */
17024     getSelectedNodes : function(){
17025         return this.selNodes;    
17026     },
17027
17028     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17029
17030     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17031
17032     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17033 });/*
17034  * Based on:
17035  * Ext JS Library 1.1.1
17036  * Copyright(c) 2006-2007, Ext JS, LLC.
17037  *
17038  * Originally Released Under LGPL - original licence link has changed is not relivant.
17039  *
17040  * Fork - LGPL
17041  * <script type="text/javascript">
17042  */
17043  
17044 /**
17045  * @class Roo.tree.TreeNode
17046  * @extends Roo.data.Node
17047  * @cfg {String} text The text for this node
17048  * @cfg {Boolean} expanded true to start the node expanded
17049  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17050  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17051  * @cfg {Boolean} disabled true to start the node disabled
17052  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17053  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17054  * @cfg {String} cls A css class to be added to the node
17055  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17056  * @cfg {String} href URL of the link used for the node (defaults to #)
17057  * @cfg {String} hrefTarget target frame for the link
17058  * @cfg {String} qtip An Ext QuickTip for the node
17059  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17060  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17061  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17062  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17063  * (defaults to undefined with no checkbox rendered)
17064  * @constructor
17065  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17066  */
17067 Roo.tree.TreeNode = function(attributes){
17068     attributes = attributes || {};
17069     if(typeof attributes == "string"){
17070         attributes = {text: attributes};
17071     }
17072     this.childrenRendered = false;
17073     this.rendered = false;
17074     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17075     this.expanded = attributes.expanded === true;
17076     this.isTarget = attributes.isTarget !== false;
17077     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17078     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17079
17080     /**
17081      * Read-only. The text for this node. To change it use setText().
17082      * @type String
17083      */
17084     this.text = attributes.text;
17085     /**
17086      * True if this node is disabled.
17087      * @type Boolean
17088      */
17089     this.disabled = attributes.disabled === true;
17090
17091     this.addEvents({
17092         /**
17093         * @event textchange
17094         * Fires when the text for this node is changed
17095         * @param {Node} this This node
17096         * @param {String} text The new text
17097         * @param {String} oldText The old text
17098         */
17099         "textchange" : true,
17100         /**
17101         * @event beforeexpand
17102         * Fires before this node is expanded, return false to cancel.
17103         * @param {Node} this This node
17104         * @param {Boolean} deep
17105         * @param {Boolean} anim
17106         */
17107         "beforeexpand" : true,
17108         /**
17109         * @event beforecollapse
17110         * Fires before this node is collapsed, return false to cancel.
17111         * @param {Node} this This node
17112         * @param {Boolean} deep
17113         * @param {Boolean} anim
17114         */
17115         "beforecollapse" : true,
17116         /**
17117         * @event expand
17118         * Fires when this node is expanded
17119         * @param {Node} this This node
17120         */
17121         "expand" : true,
17122         /**
17123         * @event disabledchange
17124         * Fires when the disabled status of this node changes
17125         * @param {Node} this This node
17126         * @param {Boolean} disabled
17127         */
17128         "disabledchange" : true,
17129         /**
17130         * @event collapse
17131         * Fires when this node is collapsed
17132         * @param {Node} this This node
17133         */
17134         "collapse" : true,
17135         /**
17136         * @event beforeclick
17137         * Fires before click processing. Return false to cancel the default action.
17138         * @param {Node} this This node
17139         * @param {Roo.EventObject} e The event object
17140         */
17141         "beforeclick":true,
17142         /**
17143         * @event checkchange
17144         * Fires when a node with a checkbox's checked property changes
17145         * @param {Node} this This node
17146         * @param {Boolean} checked
17147         */
17148         "checkchange":true,
17149         /**
17150         * @event click
17151         * Fires when this node is clicked
17152         * @param {Node} this This node
17153         * @param {Roo.EventObject} e The event object
17154         */
17155         "click":true,
17156         /**
17157         * @event dblclick
17158         * Fires when this node is double clicked
17159         * @param {Node} this This node
17160         * @param {Roo.EventObject} e The event object
17161         */
17162         "dblclick":true,
17163         /**
17164         * @event contextmenu
17165         * Fires when this node is right clicked
17166         * @param {Node} this This node
17167         * @param {Roo.EventObject} e The event object
17168         */
17169         "contextmenu":true,
17170         /**
17171         * @event beforechildrenrendered
17172         * Fires right before the child nodes for this node are rendered
17173         * @param {Node} this This node
17174         */
17175         "beforechildrenrendered":true
17176     });
17177
17178     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17179
17180     /**
17181      * Read-only. The UI for this node
17182      * @type TreeNodeUI
17183      */
17184     this.ui = new uiClass(this);
17185 };
17186 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17187     preventHScroll: true,
17188     /**
17189      * Returns true if this node is expanded
17190      * @return {Boolean}
17191      */
17192     isExpanded : function(){
17193         return this.expanded;
17194     },
17195
17196     /**
17197      * Returns the UI object for this node
17198      * @return {TreeNodeUI}
17199      */
17200     getUI : function(){
17201         return this.ui;
17202     },
17203
17204     // private override
17205     setFirstChild : function(node){
17206         var of = this.firstChild;
17207         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17208         if(this.childrenRendered && of && node != of){
17209             of.renderIndent(true, true);
17210         }
17211         if(this.rendered){
17212             this.renderIndent(true, true);
17213         }
17214     },
17215
17216     // private override
17217     setLastChild : function(node){
17218         var ol = this.lastChild;
17219         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17220         if(this.childrenRendered && ol && node != ol){
17221             ol.renderIndent(true, true);
17222         }
17223         if(this.rendered){
17224             this.renderIndent(true, true);
17225         }
17226     },
17227
17228     // these methods are overridden to provide lazy rendering support
17229     // private override
17230     appendChild : function(){
17231         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17232         if(node && this.childrenRendered){
17233             node.render();
17234         }
17235         this.ui.updateExpandIcon();
17236         return node;
17237     },
17238
17239     // private override
17240     removeChild : function(node){
17241         this.ownerTree.getSelectionModel().unselect(node);
17242         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17243         // if it's been rendered remove dom node
17244         if(this.childrenRendered){
17245             node.ui.remove();
17246         }
17247         if(this.childNodes.length < 1){
17248             this.collapse(false, false);
17249         }else{
17250             this.ui.updateExpandIcon();
17251         }
17252         if(!this.firstChild) {
17253             this.childrenRendered = false;
17254         }
17255         return node;
17256     },
17257
17258     // private override
17259     insertBefore : function(node, refNode){
17260         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17261         if(newNode && refNode && this.childrenRendered){
17262             node.render();
17263         }
17264         this.ui.updateExpandIcon();
17265         return newNode;
17266     },
17267
17268     /**
17269      * Sets the text for this node
17270      * @param {String} text
17271      */
17272     setText : function(text){
17273         var oldText = this.text;
17274         this.text = text;
17275         this.attributes.text = text;
17276         if(this.rendered){ // event without subscribing
17277             this.ui.onTextChange(this, text, oldText);
17278         }
17279         this.fireEvent("textchange", this, text, oldText);
17280     },
17281
17282     /**
17283      * Triggers selection of this node
17284      */
17285     select : function(){
17286         this.getOwnerTree().getSelectionModel().select(this);
17287     },
17288
17289     /**
17290      * Triggers deselection of this node
17291      */
17292     unselect : function(){
17293         this.getOwnerTree().getSelectionModel().unselect(this);
17294     },
17295
17296     /**
17297      * Returns true if this node is selected
17298      * @return {Boolean}
17299      */
17300     isSelected : function(){
17301         return this.getOwnerTree().getSelectionModel().isSelected(this);
17302     },
17303
17304     /**
17305      * Expand this node.
17306      * @param {Boolean} deep (optional) True to expand all children as well
17307      * @param {Boolean} anim (optional) false to cancel the default animation
17308      * @param {Function} callback (optional) A callback to be called when
17309      * expanding this node completes (does not wait for deep expand to complete).
17310      * Called with 1 parameter, this node.
17311      */
17312     expand : function(deep, anim, callback){
17313         if(!this.expanded){
17314             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17315                 return;
17316             }
17317             if(!this.childrenRendered){
17318                 this.renderChildren();
17319             }
17320             this.expanded = true;
17321             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17322                 this.ui.animExpand(function(){
17323                     this.fireEvent("expand", this);
17324                     if(typeof callback == "function"){
17325                         callback(this);
17326                     }
17327                     if(deep === true){
17328                         this.expandChildNodes(true);
17329                     }
17330                 }.createDelegate(this));
17331                 return;
17332             }else{
17333                 this.ui.expand();
17334                 this.fireEvent("expand", this);
17335                 if(typeof callback == "function"){
17336                     callback(this);
17337                 }
17338             }
17339         }else{
17340            if(typeof callback == "function"){
17341                callback(this);
17342            }
17343         }
17344         if(deep === true){
17345             this.expandChildNodes(true);
17346         }
17347     },
17348
17349     isHiddenRoot : function(){
17350         return this.isRoot && !this.getOwnerTree().rootVisible;
17351     },
17352
17353     /**
17354      * Collapse this node.
17355      * @param {Boolean} deep (optional) True to collapse all children as well
17356      * @param {Boolean} anim (optional) false to cancel the default animation
17357      */
17358     collapse : function(deep, anim){
17359         if(this.expanded && !this.isHiddenRoot()){
17360             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17361                 return;
17362             }
17363             this.expanded = false;
17364             if((this.getOwnerTree().animate && anim !== false) || anim){
17365                 this.ui.animCollapse(function(){
17366                     this.fireEvent("collapse", this);
17367                     if(deep === true){
17368                         this.collapseChildNodes(true);
17369                     }
17370                 }.createDelegate(this));
17371                 return;
17372             }else{
17373                 this.ui.collapse();
17374                 this.fireEvent("collapse", this);
17375             }
17376         }
17377         if(deep === true){
17378             var cs = this.childNodes;
17379             for(var i = 0, len = cs.length; i < len; i++) {
17380                 cs[i].collapse(true, false);
17381             }
17382         }
17383     },
17384
17385     // private
17386     delayedExpand : function(delay){
17387         if(!this.expandProcId){
17388             this.expandProcId = this.expand.defer(delay, this);
17389         }
17390     },
17391
17392     // private
17393     cancelExpand : function(){
17394         if(this.expandProcId){
17395             clearTimeout(this.expandProcId);
17396         }
17397         this.expandProcId = false;
17398     },
17399
17400     /**
17401      * Toggles expanded/collapsed state of the node
17402      */
17403     toggle : function(){
17404         if(this.expanded){
17405             this.collapse();
17406         }else{
17407             this.expand();
17408         }
17409     },
17410
17411     /**
17412      * Ensures all parent nodes are expanded
17413      */
17414     ensureVisible : function(callback){
17415         var tree = this.getOwnerTree();
17416         tree.expandPath(this.parentNode.getPath(), false, function(){
17417             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17418             Roo.callback(callback);
17419         }.createDelegate(this));
17420     },
17421
17422     /**
17423      * Expand all child nodes
17424      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17425      */
17426     expandChildNodes : function(deep){
17427         var cs = this.childNodes;
17428         for(var i = 0, len = cs.length; i < len; i++) {
17429                 cs[i].expand(deep);
17430         }
17431     },
17432
17433     /**
17434      * Collapse all child nodes
17435      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17436      */
17437     collapseChildNodes : function(deep){
17438         var cs = this.childNodes;
17439         for(var i = 0, len = cs.length; i < len; i++) {
17440                 cs[i].collapse(deep);
17441         }
17442     },
17443
17444     /**
17445      * Disables this node
17446      */
17447     disable : function(){
17448         this.disabled = true;
17449         this.unselect();
17450         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17451             this.ui.onDisableChange(this, true);
17452         }
17453         this.fireEvent("disabledchange", this, true);
17454     },
17455
17456     /**
17457      * Enables this node
17458      */
17459     enable : function(){
17460         this.disabled = false;
17461         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17462             this.ui.onDisableChange(this, false);
17463         }
17464         this.fireEvent("disabledchange", this, false);
17465     },
17466
17467     // private
17468     renderChildren : function(suppressEvent){
17469         if(suppressEvent !== false){
17470             this.fireEvent("beforechildrenrendered", this);
17471         }
17472         var cs = this.childNodes;
17473         for(var i = 0, len = cs.length; i < len; i++){
17474             cs[i].render(true);
17475         }
17476         this.childrenRendered = true;
17477     },
17478
17479     // private
17480     sort : function(fn, scope){
17481         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17482         if(this.childrenRendered){
17483             var cs = this.childNodes;
17484             for(var i = 0, len = cs.length; i < len; i++){
17485                 cs[i].render(true);
17486             }
17487         }
17488     },
17489
17490     // private
17491     render : function(bulkRender){
17492         this.ui.render(bulkRender);
17493         if(!this.rendered){
17494             this.rendered = true;
17495             if(this.expanded){
17496                 this.expanded = false;
17497                 this.expand(false, false);
17498             }
17499         }
17500     },
17501
17502     // private
17503     renderIndent : function(deep, refresh){
17504         if(refresh){
17505             this.ui.childIndent = null;
17506         }
17507         this.ui.renderIndent();
17508         if(deep === true && this.childrenRendered){
17509             var cs = this.childNodes;
17510             for(var i = 0, len = cs.length; i < len; i++){
17511                 cs[i].renderIndent(true, refresh);
17512             }
17513         }
17514     }
17515 });/*
17516  * Based on:
17517  * Ext JS Library 1.1.1
17518  * Copyright(c) 2006-2007, Ext JS, LLC.
17519  *
17520  * Originally Released Under LGPL - original licence link has changed is not relivant.
17521  *
17522  * Fork - LGPL
17523  * <script type="text/javascript">
17524  */
17525  
17526 /**
17527  * @class Roo.tree.AsyncTreeNode
17528  * @extends Roo.tree.TreeNode
17529  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17530  * @constructor
17531  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17532  */
17533  Roo.tree.AsyncTreeNode = function(config){
17534     this.loaded = false;
17535     this.loading = false;
17536     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17537     /**
17538     * @event beforeload
17539     * Fires before this node is loaded, return false to cancel
17540     * @param {Node} this This node
17541     */
17542     this.addEvents({'beforeload':true, 'load': true});
17543     /**
17544     * @event load
17545     * Fires when this node is loaded
17546     * @param {Node} this This node
17547     */
17548     /**
17549      * The loader used by this node (defaults to using the tree's defined loader)
17550      * @type TreeLoader
17551      * @property loader
17552      */
17553 };
17554 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17555     expand : function(deep, anim, callback){
17556         if(this.loading){ // if an async load is already running, waiting til it's done
17557             var timer;
17558             var f = function(){
17559                 if(!this.loading){ // done loading
17560                     clearInterval(timer);
17561                     this.expand(deep, anim, callback);
17562                 }
17563             }.createDelegate(this);
17564             timer = setInterval(f, 200);
17565             return;
17566         }
17567         if(!this.loaded){
17568             if(this.fireEvent("beforeload", this) === false){
17569                 return;
17570             }
17571             this.loading = true;
17572             this.ui.beforeLoad(this);
17573             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17574             if(loader){
17575                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17576                 return;
17577             }
17578         }
17579         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17580     },
17581     
17582     /**
17583      * Returns true if this node is currently loading
17584      * @return {Boolean}
17585      */
17586     isLoading : function(){
17587         return this.loading;  
17588     },
17589     
17590     loadComplete : function(deep, anim, callback){
17591         this.loading = false;
17592         this.loaded = true;
17593         this.ui.afterLoad(this);
17594         this.fireEvent("load", this);
17595         this.expand(deep, anim, callback);
17596     },
17597     
17598     /**
17599      * Returns true if this node has been loaded
17600      * @return {Boolean}
17601      */
17602     isLoaded : function(){
17603         return this.loaded;
17604     },
17605     
17606     hasChildNodes : function(){
17607         if(!this.isLeaf() && !this.loaded){
17608             return true;
17609         }else{
17610             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17611         }
17612     },
17613
17614     /**
17615      * Trigger a reload for this node
17616      * @param {Function} callback
17617      */
17618     reload : function(callback){
17619         this.collapse(false, false);
17620         while(this.firstChild){
17621             this.removeChild(this.firstChild);
17622         }
17623         this.childrenRendered = false;
17624         this.loaded = false;
17625         if(this.isHiddenRoot()){
17626             this.expanded = false;
17627         }
17628         this.expand(false, false, callback);
17629     }
17630 });/*
17631  * Based on:
17632  * Ext JS Library 1.1.1
17633  * Copyright(c) 2006-2007, Ext JS, LLC.
17634  *
17635  * Originally Released Under LGPL - original licence link has changed is not relivant.
17636  *
17637  * Fork - LGPL
17638  * <script type="text/javascript">
17639  */
17640  
17641 /**
17642  * @class Roo.tree.TreeNodeUI
17643  * @constructor
17644  * @param {Object} node The node to render
17645  * The TreeNode UI implementation is separate from the
17646  * tree implementation. Unless you are customizing the tree UI,
17647  * you should never have to use this directly.
17648  */
17649 Roo.tree.TreeNodeUI = function(node){
17650     this.node = node;
17651     this.rendered = false;
17652     this.animating = false;
17653     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17654 };
17655
17656 Roo.tree.TreeNodeUI.prototype = {
17657     removeChild : function(node){
17658         if(this.rendered){
17659             this.ctNode.removeChild(node.ui.getEl());
17660         }
17661     },
17662
17663     beforeLoad : function(){
17664          this.addClass("x-tree-node-loading");
17665     },
17666
17667     afterLoad : function(){
17668          this.removeClass("x-tree-node-loading");
17669     },
17670
17671     onTextChange : function(node, text, oldText){
17672         if(this.rendered){
17673             this.textNode.innerHTML = text;
17674         }
17675     },
17676
17677     onDisableChange : function(node, state){
17678         this.disabled = state;
17679         if(state){
17680             this.addClass("x-tree-node-disabled");
17681         }else{
17682             this.removeClass("x-tree-node-disabled");
17683         }
17684     },
17685
17686     onSelectedChange : function(state){
17687         if(state){
17688             this.focus();
17689             this.addClass("x-tree-selected");
17690         }else{
17691             //this.blur();
17692             this.removeClass("x-tree-selected");
17693         }
17694     },
17695
17696     onMove : function(tree, node, oldParent, newParent, index, refNode){
17697         this.childIndent = null;
17698         if(this.rendered){
17699             var targetNode = newParent.ui.getContainer();
17700             if(!targetNode){//target not rendered
17701                 this.holder = document.createElement("div");
17702                 this.holder.appendChild(this.wrap);
17703                 return;
17704             }
17705             var insertBefore = refNode ? refNode.ui.getEl() : null;
17706             if(insertBefore){
17707                 targetNode.insertBefore(this.wrap, insertBefore);
17708             }else{
17709                 targetNode.appendChild(this.wrap);
17710             }
17711             this.node.renderIndent(true);
17712         }
17713     },
17714
17715     addClass : function(cls){
17716         if(this.elNode){
17717             Roo.fly(this.elNode).addClass(cls);
17718         }
17719     },
17720
17721     removeClass : function(cls){
17722         if(this.elNode){
17723             Roo.fly(this.elNode).removeClass(cls);
17724         }
17725     },
17726
17727     remove : function(){
17728         if(this.rendered){
17729             this.holder = document.createElement("div");
17730             this.holder.appendChild(this.wrap);
17731         }
17732     },
17733
17734     fireEvent : function(){
17735         return this.node.fireEvent.apply(this.node, arguments);
17736     },
17737
17738     initEvents : function(){
17739         this.node.on("move", this.onMove, this);
17740         var E = Roo.EventManager;
17741         var a = this.anchor;
17742
17743         var el = Roo.fly(a, '_treeui');
17744
17745         if(Roo.isOpera){ // opera render bug ignores the CSS
17746             el.setStyle("text-decoration", "none");
17747         }
17748
17749         el.on("click", this.onClick, this);
17750         el.on("dblclick", this.onDblClick, this);
17751
17752         if(this.checkbox){
17753             Roo.EventManager.on(this.checkbox,
17754                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17755         }
17756
17757         el.on("contextmenu", this.onContextMenu, this);
17758
17759         var icon = Roo.fly(this.iconNode);
17760         icon.on("click", this.onClick, this);
17761         icon.on("dblclick", this.onDblClick, this);
17762         icon.on("contextmenu", this.onContextMenu, this);
17763         E.on(this.ecNode, "click", this.ecClick, this, true);
17764
17765         if(this.node.disabled){
17766             this.addClass("x-tree-node-disabled");
17767         }
17768         if(this.node.hidden){
17769             this.addClass("x-tree-node-disabled");
17770         }
17771         var ot = this.node.getOwnerTree();
17772         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17773         if(dd && (!this.node.isRoot || ot.rootVisible)){
17774             Roo.dd.Registry.register(this.elNode, {
17775                 node: this.node,
17776                 handles: this.getDDHandles(),
17777                 isHandle: false
17778             });
17779         }
17780     },
17781
17782     getDDHandles : function(){
17783         return [this.iconNode, this.textNode];
17784     },
17785
17786     hide : function(){
17787         if(this.rendered){
17788             this.wrap.style.display = "none";
17789         }
17790     },
17791
17792     show : function(){
17793         if(this.rendered){
17794             this.wrap.style.display = "";
17795         }
17796     },
17797
17798     onContextMenu : function(e){
17799         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17800             e.preventDefault();
17801             this.focus();
17802             this.fireEvent("contextmenu", this.node, e);
17803         }
17804     },
17805
17806     onClick : function(e){
17807         if(this.dropping){
17808             e.stopEvent();
17809             return;
17810         }
17811         if(this.fireEvent("beforeclick", this.node, e) !== false){
17812             if(!this.disabled && this.node.attributes.href){
17813                 this.fireEvent("click", this.node, e);
17814                 return;
17815             }
17816             e.preventDefault();
17817             if(this.disabled){
17818                 return;
17819             }
17820
17821             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17822                 this.node.toggle();
17823             }
17824
17825             this.fireEvent("click", this.node, e);
17826         }else{
17827             e.stopEvent();
17828         }
17829     },
17830
17831     onDblClick : function(e){
17832         e.preventDefault();
17833         if(this.disabled){
17834             return;
17835         }
17836         if(this.checkbox){
17837             this.toggleCheck();
17838         }
17839         if(!this.animating && this.node.hasChildNodes()){
17840             this.node.toggle();
17841         }
17842         this.fireEvent("dblclick", this.node, e);
17843     },
17844
17845     onCheckChange : function(){
17846         var checked = this.checkbox.checked;
17847         this.node.attributes.checked = checked;
17848         this.fireEvent('checkchange', this.node, checked);
17849     },
17850
17851     ecClick : function(e){
17852         if(!this.animating && this.node.hasChildNodes()){
17853             this.node.toggle();
17854         }
17855     },
17856
17857     startDrop : function(){
17858         this.dropping = true;
17859     },
17860
17861     // delayed drop so the click event doesn't get fired on a drop
17862     endDrop : function(){
17863        setTimeout(function(){
17864            this.dropping = false;
17865        }.createDelegate(this), 50);
17866     },
17867
17868     expand : function(){
17869         this.updateExpandIcon();
17870         this.ctNode.style.display = "";
17871     },
17872
17873     focus : function(){
17874         if(!this.node.preventHScroll){
17875             try{this.anchor.focus();
17876             }catch(e){}
17877         }else if(!Roo.isIE){
17878             try{
17879                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17880                 var l = noscroll.scrollLeft;
17881                 this.anchor.focus();
17882                 noscroll.scrollLeft = l;
17883             }catch(e){}
17884         }
17885     },
17886
17887     toggleCheck : function(value){
17888         var cb = this.checkbox;
17889         if(cb){
17890             cb.checked = (value === undefined ? !cb.checked : value);
17891         }
17892     },
17893
17894     blur : function(){
17895         try{
17896             this.anchor.blur();
17897         }catch(e){}
17898     },
17899
17900     animExpand : function(callback){
17901         var ct = Roo.get(this.ctNode);
17902         ct.stopFx();
17903         if(!this.node.hasChildNodes()){
17904             this.updateExpandIcon();
17905             this.ctNode.style.display = "";
17906             Roo.callback(callback);
17907             return;
17908         }
17909         this.animating = true;
17910         this.updateExpandIcon();
17911
17912         ct.slideIn('t', {
17913            callback : function(){
17914                this.animating = false;
17915                Roo.callback(callback);
17916             },
17917             scope: this,
17918             duration: this.node.ownerTree.duration || .25
17919         });
17920     },
17921
17922     highlight : function(){
17923         var tree = this.node.getOwnerTree();
17924         Roo.fly(this.wrap).highlight(
17925             tree.hlColor || "C3DAF9",
17926             {endColor: tree.hlBaseColor}
17927         );
17928     },
17929
17930     collapse : function(){
17931         this.updateExpandIcon();
17932         this.ctNode.style.display = "none";
17933     },
17934
17935     animCollapse : function(callback){
17936         var ct = Roo.get(this.ctNode);
17937         ct.enableDisplayMode('block');
17938         ct.stopFx();
17939
17940         this.animating = true;
17941         this.updateExpandIcon();
17942
17943         ct.slideOut('t', {
17944             callback : function(){
17945                this.animating = false;
17946                Roo.callback(callback);
17947             },
17948             scope: this,
17949             duration: this.node.ownerTree.duration || .25
17950         });
17951     },
17952
17953     getContainer : function(){
17954         return this.ctNode;
17955     },
17956
17957     getEl : function(){
17958         return this.wrap;
17959     },
17960
17961     appendDDGhost : function(ghostNode){
17962         ghostNode.appendChild(this.elNode.cloneNode(true));
17963     },
17964
17965     getDDRepairXY : function(){
17966         return Roo.lib.Dom.getXY(this.iconNode);
17967     },
17968
17969     onRender : function(){
17970         this.render();
17971     },
17972
17973     render : function(bulkRender){
17974         var n = this.node, a = n.attributes;
17975         var targetNode = n.parentNode ?
17976               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17977
17978         if(!this.rendered){
17979             this.rendered = true;
17980
17981             this.renderElements(n, a, targetNode, bulkRender);
17982
17983             if(a.qtip){
17984                if(this.textNode.setAttributeNS){
17985                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17986                    if(a.qtipTitle){
17987                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17988                    }
17989                }else{
17990                    this.textNode.setAttribute("ext:qtip", a.qtip);
17991                    if(a.qtipTitle){
17992                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17993                    }
17994                }
17995             }else if(a.qtipCfg){
17996                 a.qtipCfg.target = Roo.id(this.textNode);
17997                 Roo.QuickTips.register(a.qtipCfg);
17998             }
17999             this.initEvents();
18000             if(!this.node.expanded){
18001                 this.updateExpandIcon();
18002             }
18003         }else{
18004             if(bulkRender === true) {
18005                 targetNode.appendChild(this.wrap);
18006             }
18007         }
18008     },
18009
18010     renderElements : function(n, a, targetNode, bulkRender){
18011         // add some indent caching, this helps performance when rendering a large tree
18012         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18013         var t = n.getOwnerTree();
18014         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18015         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18016         var cb = typeof a.checked == 'boolean';
18017         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18018         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18019             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18020             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18021             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18022             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18023             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18024              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18025                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18026             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18027             "</li>"];
18028
18029         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18030             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18031                                 n.nextSibling.ui.getEl(), buf.join(""));
18032         }else{
18033             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18034         }
18035
18036         this.elNode = this.wrap.childNodes[0];
18037         this.ctNode = this.wrap.childNodes[1];
18038         var cs = this.elNode.childNodes;
18039         this.indentNode = cs[0];
18040         this.ecNode = cs[1];
18041         this.iconNode = cs[2];
18042         var index = 3;
18043         if(cb){
18044             this.checkbox = cs[3];
18045             index++;
18046         }
18047         this.anchor = cs[index];
18048         this.textNode = cs[index].firstChild;
18049     },
18050
18051     getAnchor : function(){
18052         return this.anchor;
18053     },
18054
18055     getTextEl : function(){
18056         return this.textNode;
18057     },
18058
18059     getIconEl : function(){
18060         return this.iconNode;
18061     },
18062
18063     isChecked : function(){
18064         return this.checkbox ? this.checkbox.checked : false;
18065     },
18066
18067     updateExpandIcon : function(){
18068         if(this.rendered){
18069             var n = this.node, c1, c2;
18070             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18071             var hasChild = n.hasChildNodes();
18072             if(hasChild){
18073                 if(n.expanded){
18074                     cls += "-minus";
18075                     c1 = "x-tree-node-collapsed";
18076                     c2 = "x-tree-node-expanded";
18077                 }else{
18078                     cls += "-plus";
18079                     c1 = "x-tree-node-expanded";
18080                     c2 = "x-tree-node-collapsed";
18081                 }
18082                 if(this.wasLeaf){
18083                     this.removeClass("x-tree-node-leaf");
18084                     this.wasLeaf = false;
18085                 }
18086                 if(this.c1 != c1 || this.c2 != c2){
18087                     Roo.fly(this.elNode).replaceClass(c1, c2);
18088                     this.c1 = c1; this.c2 = c2;
18089                 }
18090             }else{
18091                 if(!this.wasLeaf){
18092                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18093                     delete this.c1;
18094                     delete this.c2;
18095                     this.wasLeaf = true;
18096                 }
18097             }
18098             var ecc = "x-tree-ec-icon "+cls;
18099             if(this.ecc != ecc){
18100                 this.ecNode.className = ecc;
18101                 this.ecc = ecc;
18102             }
18103         }
18104     },
18105
18106     getChildIndent : function(){
18107         if(!this.childIndent){
18108             var buf = [];
18109             var p = this.node;
18110             while(p){
18111                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18112                     if(!p.isLast()) {
18113                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18114                     } else {
18115                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18116                     }
18117                 }
18118                 p = p.parentNode;
18119             }
18120             this.childIndent = buf.join("");
18121         }
18122         return this.childIndent;
18123     },
18124
18125     renderIndent : function(){
18126         if(this.rendered){
18127             var indent = "";
18128             var p = this.node.parentNode;
18129             if(p){
18130                 indent = p.ui.getChildIndent();
18131             }
18132             if(this.indentMarkup != indent){ // don't rerender if not required
18133                 this.indentNode.innerHTML = indent;
18134                 this.indentMarkup = indent;
18135             }
18136             this.updateExpandIcon();
18137         }
18138     }
18139 };
18140
18141 Roo.tree.RootTreeNodeUI = function(){
18142     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18143 };
18144 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18145     render : function(){
18146         if(!this.rendered){
18147             var targetNode = this.node.ownerTree.innerCt.dom;
18148             this.node.expanded = true;
18149             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18150             this.wrap = this.ctNode = targetNode.firstChild;
18151         }
18152     },
18153     collapse : function(){
18154     },
18155     expand : function(){
18156     }
18157 });/*
18158  * Based on:
18159  * Ext JS Library 1.1.1
18160  * Copyright(c) 2006-2007, Ext JS, LLC.
18161  *
18162  * Originally Released Under LGPL - original licence link has changed is not relivant.
18163  *
18164  * Fork - LGPL
18165  * <script type="text/javascript">
18166  */
18167 /**
18168  * @class Roo.tree.TreeLoader
18169  * @extends Roo.util.Observable
18170  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18171  * nodes from a specified URL. The response must be a javascript Array definition
18172  * who's elements are node definition objects. eg:
18173  * <pre><code>
18174    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18175     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18176 </code></pre>
18177  * <br><br>
18178  * A server request is sent, and child nodes are loaded only when a node is expanded.
18179  * The loading node's id is passed to the server under the parameter name "node" to
18180  * enable the server to produce the correct child nodes.
18181  * <br><br>
18182  * To pass extra parameters, an event handler may be attached to the "beforeload"
18183  * event, and the parameters specified in the TreeLoader's baseParams property:
18184  * <pre><code>
18185     myTreeLoader.on("beforeload", function(treeLoader, node) {
18186         this.baseParams.category = node.attributes.category;
18187     }, this);
18188 </code></pre><
18189  * This would pass an HTTP parameter called "category" to the server containing
18190  * the value of the Node's "category" attribute.
18191  * @constructor
18192  * Creates a new Treeloader.
18193  * @param {Object} config A config object containing config properties.
18194  */
18195 Roo.tree.TreeLoader = function(config){
18196     this.baseParams = {};
18197     this.requestMethod = "POST";
18198     Roo.apply(this, config);
18199
18200     this.addEvents({
18201     
18202         /**
18203          * @event beforeload
18204          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18205          * @param {Object} This TreeLoader object.
18206          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18207          * @param {Object} callback The callback function specified in the {@link #load} call.
18208          */
18209         beforeload : true,
18210         /**
18211          * @event load
18212          * Fires when the node has been successfuly loaded.
18213          * @param {Object} This TreeLoader object.
18214          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18215          * @param {Object} response The response object containing the data from the server.
18216          */
18217         load : true,
18218         /**
18219          * @event loadexception
18220          * Fires if the network request failed.
18221          * @param {Object} This TreeLoader object.
18222          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18223          * @param {Object} response The response object containing the data from the server.
18224          */
18225         loadexception : true,
18226         /**
18227          * @event create
18228          * Fires before a node is created, enabling you to return custom Node types 
18229          * @param {Object} This TreeLoader object.
18230          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18231          */
18232         create : true
18233     });
18234
18235     Roo.tree.TreeLoader.superclass.constructor.call(this);
18236 };
18237
18238 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18239     /**
18240     * @cfg {String} dataUrl The URL from which to request a Json string which
18241     * specifies an array of node definition object representing the child nodes
18242     * to be loaded.
18243     */
18244     /**
18245     * @cfg {Object} baseParams (optional) An object containing properties which
18246     * specify HTTP parameters to be passed to each request for child nodes.
18247     */
18248     /**
18249     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18250     * created by this loader. If the attributes sent by the server have an attribute in this object,
18251     * they take priority.
18252     */
18253     /**
18254     * @cfg {Object} uiProviders (optional) An object containing properties which
18255     * 
18256     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18257     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18258     * <i>uiProvider</i> attribute of a returned child node is a string rather
18259     * than a reference to a TreeNodeUI implementation, this that string value
18260     * is used as a property name in the uiProviders object. You can define the provider named
18261     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18262     */
18263     uiProviders : {},
18264
18265     /**
18266     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18267     * child nodes before loading.
18268     */
18269     clearOnLoad : true,
18270
18271     /**
18272     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18273     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18274     * Grid query { data : [ .....] }
18275     */
18276     
18277     root : false,
18278      /**
18279     * @cfg {String} queryParam (optional) 
18280     * Name of the query as it will be passed on the querystring (defaults to 'node')
18281     * eg. the request will be ?node=[id]
18282     */
18283     
18284     
18285     queryParam: false,
18286     
18287     /**
18288      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18289      * This is called automatically when a node is expanded, but may be used to reload
18290      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18291      * @param {Roo.tree.TreeNode} node
18292      * @param {Function} callback
18293      */
18294     load : function(node, callback){
18295         if(this.clearOnLoad){
18296             while(node.firstChild){
18297                 node.removeChild(node.firstChild);
18298             }
18299         }
18300         if(node.attributes.children){ // preloaded json children
18301             var cs = node.attributes.children;
18302             for(var i = 0, len = cs.length; i < len; i++){
18303                 node.appendChild(this.createNode(cs[i]));
18304             }
18305             if(typeof callback == "function"){
18306                 callback();
18307             }
18308         }else if(this.dataUrl){
18309             this.requestData(node, callback);
18310         }
18311     },
18312
18313     getParams: function(node){
18314         var buf = [], bp = this.baseParams;
18315         for(var key in bp){
18316             if(typeof bp[key] != "function"){
18317                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18318             }
18319         }
18320         var n = this.queryParam === false ? 'node' : this.queryParam;
18321         buf.push(n + "=", encodeURIComponent(node.id));
18322         return buf.join("");
18323     },
18324
18325     requestData : function(node, callback){
18326         if(this.fireEvent("beforeload", this, node, callback) !== false){
18327             this.transId = Roo.Ajax.request({
18328                 method:this.requestMethod,
18329                 url: this.dataUrl||this.url,
18330                 success: this.handleResponse,
18331                 failure: this.handleFailure,
18332                 scope: this,
18333                 argument: {callback: callback, node: node},
18334                 params: this.getParams(node)
18335             });
18336         }else{
18337             // if the load is cancelled, make sure we notify
18338             // the node that we are done
18339             if(typeof callback == "function"){
18340                 callback();
18341             }
18342         }
18343     },
18344
18345     isLoading : function(){
18346         return this.transId ? true : false;
18347     },
18348
18349     abort : function(){
18350         if(this.isLoading()){
18351             Roo.Ajax.abort(this.transId);
18352         }
18353     },
18354
18355     // private
18356     createNode : function(attr){
18357         // apply baseAttrs, nice idea Corey!
18358         if(this.baseAttrs){
18359             Roo.applyIf(attr, this.baseAttrs);
18360         }
18361         if(this.applyLoader !== false){
18362             attr.loader = this;
18363         }
18364         // uiProvider = depreciated..
18365         
18366         if(typeof(attr.uiProvider) == 'string'){
18367            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18368                 /**  eval:var:attr */ eval(attr.uiProvider);
18369         }
18370         if(typeof(this.uiProviders['default']) != 'undefined') {
18371             attr.uiProvider = this.uiProviders['default'];
18372         }
18373         
18374         this.fireEvent('create', this, attr);
18375         
18376         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18377         return(attr.leaf ?
18378                         new Roo.tree.TreeNode(attr) :
18379                         new Roo.tree.AsyncTreeNode(attr));
18380     },
18381
18382     processResponse : function(response, node, callback){
18383         var json = response.responseText;
18384         try {
18385             
18386             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18387             if (this.root !== false) {
18388                 o = o[this.root];
18389             }
18390             
18391             for(var i = 0, len = o.length; i < len; i++){
18392                 var n = this.createNode(o[i]);
18393                 if(n){
18394                     node.appendChild(n);
18395                 }
18396             }
18397             if(typeof callback == "function"){
18398                 callback(this, node);
18399             }
18400         }catch(e){
18401             this.handleFailure(response);
18402         }
18403     },
18404
18405     handleResponse : function(response){
18406         this.transId = false;
18407         var a = response.argument;
18408         this.processResponse(response, a.node, a.callback);
18409         this.fireEvent("load", this, a.node, response);
18410     },
18411
18412     handleFailure : function(response){
18413         this.transId = false;
18414         var a = response.argument;
18415         this.fireEvent("loadexception", this, a.node, response);
18416         if(typeof a.callback == "function"){
18417             a.callback(this, a.node);
18418         }
18419     }
18420 });/*
18421  * Based on:
18422  * Ext JS Library 1.1.1
18423  * Copyright(c) 2006-2007, Ext JS, LLC.
18424  *
18425  * Originally Released Under LGPL - original licence link has changed is not relivant.
18426  *
18427  * Fork - LGPL
18428  * <script type="text/javascript">
18429  */
18430
18431 /**
18432 * @class Roo.tree.TreeFilter
18433 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18434 * @param {TreePanel} tree
18435 * @param {Object} config (optional)
18436  */
18437 Roo.tree.TreeFilter = function(tree, config){
18438     this.tree = tree;
18439     this.filtered = {};
18440     Roo.apply(this, config);
18441 };
18442
18443 Roo.tree.TreeFilter.prototype = {
18444     clearBlank:false,
18445     reverse:false,
18446     autoClear:false,
18447     remove:false,
18448
18449      /**
18450      * Filter the data by a specific attribute.
18451      * @param {String/RegExp} value Either string that the attribute value
18452      * should start with or a RegExp to test against the attribute
18453      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18454      * @param {TreeNode} startNode (optional) The node to start the filter at.
18455      */
18456     filter : function(value, attr, startNode){
18457         attr = attr || "text";
18458         var f;
18459         if(typeof value == "string"){
18460             var vlen = value.length;
18461             // auto clear empty filter
18462             if(vlen == 0 && this.clearBlank){
18463                 this.clear();
18464                 return;
18465             }
18466             value = value.toLowerCase();
18467             f = function(n){
18468                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18469             };
18470         }else if(value.exec){ // regex?
18471             f = function(n){
18472                 return value.test(n.attributes[attr]);
18473             };
18474         }else{
18475             throw 'Illegal filter type, must be string or regex';
18476         }
18477         this.filterBy(f, null, startNode);
18478         },
18479
18480     /**
18481      * Filter by a function. The passed function will be called with each
18482      * node in the tree (or from the startNode). If the function returns true, the node is kept
18483      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18484      * @param {Function} fn The filter function
18485      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18486      */
18487     filterBy : function(fn, scope, startNode){
18488         startNode = startNode || this.tree.root;
18489         if(this.autoClear){
18490             this.clear();
18491         }
18492         var af = this.filtered, rv = this.reverse;
18493         var f = function(n){
18494             if(n == startNode){
18495                 return true;
18496             }
18497             if(af[n.id]){
18498                 return false;
18499             }
18500             var m = fn.call(scope || n, n);
18501             if(!m || rv){
18502                 af[n.id] = n;
18503                 n.ui.hide();
18504                 return false;
18505             }
18506             return true;
18507         };
18508         startNode.cascade(f);
18509         if(this.remove){
18510            for(var id in af){
18511                if(typeof id != "function"){
18512                    var n = af[id];
18513                    if(n && n.parentNode){
18514                        n.parentNode.removeChild(n);
18515                    }
18516                }
18517            }
18518         }
18519     },
18520
18521     /**
18522      * Clears the current filter. Note: with the "remove" option
18523      * set a filter cannot be cleared.
18524      */
18525     clear : function(){
18526         var t = this.tree;
18527         var af = this.filtered;
18528         for(var id in af){
18529             if(typeof id != "function"){
18530                 var n = af[id];
18531                 if(n){
18532                     n.ui.show();
18533                 }
18534             }
18535         }
18536         this.filtered = {};
18537     }
18538 };
18539 /*
18540  * Based on:
18541  * Ext JS Library 1.1.1
18542  * Copyright(c) 2006-2007, Ext JS, LLC.
18543  *
18544  * Originally Released Under LGPL - original licence link has changed is not relivant.
18545  *
18546  * Fork - LGPL
18547  * <script type="text/javascript">
18548  */
18549  
18550
18551 /**
18552  * @class Roo.tree.TreeSorter
18553  * Provides sorting of nodes in a TreePanel
18554  * 
18555  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18556  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18557  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18558  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18559  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18560  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18561  * @constructor
18562  * @param {TreePanel} tree
18563  * @param {Object} config
18564  */
18565 Roo.tree.TreeSorter = function(tree, config){
18566     Roo.apply(this, config);
18567     tree.on("beforechildrenrendered", this.doSort, this);
18568     tree.on("append", this.updateSort, this);
18569     tree.on("insert", this.updateSort, this);
18570     
18571     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18572     var p = this.property || "text";
18573     var sortType = this.sortType;
18574     var fs = this.folderSort;
18575     var cs = this.caseSensitive === true;
18576     var leafAttr = this.leafAttr || 'leaf';
18577
18578     this.sortFn = function(n1, n2){
18579         if(fs){
18580             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18581                 return 1;
18582             }
18583             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18584                 return -1;
18585             }
18586         }
18587         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18588         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18589         if(v1 < v2){
18590                         return dsc ? +1 : -1;
18591                 }else if(v1 > v2){
18592                         return dsc ? -1 : +1;
18593         }else{
18594                 return 0;
18595         }
18596     };
18597 };
18598
18599 Roo.tree.TreeSorter.prototype = {
18600     doSort : function(node){
18601         node.sort(this.sortFn);
18602     },
18603     
18604     compareNodes : function(n1, n2){
18605         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18606     },
18607     
18608     updateSort : function(tree, node){
18609         if(node.childrenRendered){
18610             this.doSort.defer(1, this, [node]);
18611         }
18612     }
18613 };/*
18614  * Based on:
18615  * Ext JS Library 1.1.1
18616  * Copyright(c) 2006-2007, Ext JS, LLC.
18617  *
18618  * Originally Released Under LGPL - original licence link has changed is not relivant.
18619  *
18620  * Fork - LGPL
18621  * <script type="text/javascript">
18622  */
18623
18624 if(Roo.dd.DropZone){
18625     
18626 Roo.tree.TreeDropZone = function(tree, config){
18627     this.allowParentInsert = false;
18628     this.allowContainerDrop = false;
18629     this.appendOnly = false;
18630     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18631     this.tree = tree;
18632     this.lastInsertClass = "x-tree-no-status";
18633     this.dragOverData = {};
18634 };
18635
18636 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18637     ddGroup : "TreeDD",
18638     
18639     expandDelay : 1000,
18640     
18641     expandNode : function(node){
18642         if(node.hasChildNodes() && !node.isExpanded()){
18643             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18644         }
18645     },
18646     
18647     queueExpand : function(node){
18648         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18649     },
18650     
18651     cancelExpand : function(){
18652         if(this.expandProcId){
18653             clearTimeout(this.expandProcId);
18654             this.expandProcId = false;
18655         }
18656     },
18657     
18658     isValidDropPoint : function(n, pt, dd, e, data){
18659         if(!n || !data){ return false; }
18660         var targetNode = n.node;
18661         var dropNode = data.node;
18662         // default drop rules
18663         if(!(targetNode && targetNode.isTarget && pt)){
18664             return false;
18665         }
18666         if(pt == "append" && targetNode.allowChildren === false){
18667             return false;
18668         }
18669         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18670             return false;
18671         }
18672         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18673             return false;
18674         }
18675         // reuse the object
18676         var overEvent = this.dragOverData;
18677         overEvent.tree = this.tree;
18678         overEvent.target = targetNode;
18679         overEvent.data = data;
18680         overEvent.point = pt;
18681         overEvent.source = dd;
18682         overEvent.rawEvent = e;
18683         overEvent.dropNode = dropNode;
18684         overEvent.cancel = false;  
18685         var result = this.tree.fireEvent("nodedragover", overEvent);
18686         return overEvent.cancel === false && result !== false;
18687     },
18688     
18689     getDropPoint : function(e, n, dd){
18690         var tn = n.node;
18691         if(tn.isRoot){
18692             return tn.allowChildren !== false ? "append" : false; // always append for root
18693         }
18694         var dragEl = n.ddel;
18695         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18696         var y = Roo.lib.Event.getPageY(e);
18697         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18698         
18699         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18700         var noAppend = tn.allowChildren === false;
18701         if(this.appendOnly || tn.parentNode.allowChildren === false){
18702             return noAppend ? false : "append";
18703         }
18704         var noBelow = false;
18705         if(!this.allowParentInsert){
18706             noBelow = tn.hasChildNodes() && tn.isExpanded();
18707         }
18708         var q = (b - t) / (noAppend ? 2 : 3);
18709         if(y >= t && y < (t + q)){
18710             return "above";
18711         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18712             return "below";
18713         }else{
18714             return "append";
18715         }
18716     },
18717     
18718     onNodeEnter : function(n, dd, e, data){
18719         this.cancelExpand();
18720     },
18721     
18722     onNodeOver : function(n, dd, e, data){
18723         var pt = this.getDropPoint(e, n, dd);
18724         var node = n.node;
18725         
18726         // auto node expand check
18727         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18728             this.queueExpand(node);
18729         }else if(pt != "append"){
18730             this.cancelExpand();
18731         }
18732         
18733         // set the insert point style on the target node
18734         var returnCls = this.dropNotAllowed;
18735         if(this.isValidDropPoint(n, pt, dd, e, data)){
18736            if(pt){
18737                var el = n.ddel;
18738                var cls;
18739                if(pt == "above"){
18740                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18741                    cls = "x-tree-drag-insert-above";
18742                }else if(pt == "below"){
18743                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18744                    cls = "x-tree-drag-insert-below";
18745                }else{
18746                    returnCls = "x-tree-drop-ok-append";
18747                    cls = "x-tree-drag-append";
18748                }
18749                if(this.lastInsertClass != cls){
18750                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18751                    this.lastInsertClass = cls;
18752                }
18753            }
18754        }
18755        return returnCls;
18756     },
18757     
18758     onNodeOut : function(n, dd, e, data){
18759         this.cancelExpand();
18760         this.removeDropIndicators(n);
18761     },
18762     
18763     onNodeDrop : function(n, dd, e, data){
18764         var point = this.getDropPoint(e, n, dd);
18765         var targetNode = n.node;
18766         targetNode.ui.startDrop();
18767         if(!this.isValidDropPoint(n, point, dd, e, data)){
18768             targetNode.ui.endDrop();
18769             return false;
18770         }
18771         // first try to find the drop node
18772         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18773         var dropEvent = {
18774             tree : this.tree,
18775             target: targetNode,
18776             data: data,
18777             point: point,
18778             source: dd,
18779             rawEvent: e,
18780             dropNode: dropNode,
18781             cancel: !dropNode   
18782         };
18783         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18784         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18785             targetNode.ui.endDrop();
18786             return false;
18787         }
18788         // allow target changing
18789         targetNode = dropEvent.target;
18790         if(point == "append" && !targetNode.isExpanded()){
18791             targetNode.expand(false, null, function(){
18792                 this.completeDrop(dropEvent);
18793             }.createDelegate(this));
18794         }else{
18795             this.completeDrop(dropEvent);
18796         }
18797         return true;
18798     },
18799     
18800     completeDrop : function(de){
18801         var ns = de.dropNode, p = de.point, t = de.target;
18802         if(!(ns instanceof Array)){
18803             ns = [ns];
18804         }
18805         var n;
18806         for(var i = 0, len = ns.length; i < len; i++){
18807             n = ns[i];
18808             if(p == "above"){
18809                 t.parentNode.insertBefore(n, t);
18810             }else if(p == "below"){
18811                 t.parentNode.insertBefore(n, t.nextSibling);
18812             }else{
18813                 t.appendChild(n);
18814             }
18815         }
18816         n.ui.focus();
18817         if(this.tree.hlDrop){
18818             n.ui.highlight();
18819         }
18820         t.ui.endDrop();
18821         this.tree.fireEvent("nodedrop", de);
18822     },
18823     
18824     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18825         if(this.tree.hlDrop){
18826             dropNode.ui.focus();
18827             dropNode.ui.highlight();
18828         }
18829         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18830     },
18831     
18832     getTree : function(){
18833         return this.tree;
18834     },
18835     
18836     removeDropIndicators : function(n){
18837         if(n && n.ddel){
18838             var el = n.ddel;
18839             Roo.fly(el).removeClass([
18840                     "x-tree-drag-insert-above",
18841                     "x-tree-drag-insert-below",
18842                     "x-tree-drag-append"]);
18843             this.lastInsertClass = "_noclass";
18844         }
18845     },
18846     
18847     beforeDragDrop : function(target, e, id){
18848         this.cancelExpand();
18849         return true;
18850     },
18851     
18852     afterRepair : function(data){
18853         if(data && Roo.enableFx){
18854             data.node.ui.highlight();
18855         }
18856         this.hideProxy();
18857     }    
18858 });
18859
18860 }
18861 /*
18862  * Based on:
18863  * Ext JS Library 1.1.1
18864  * Copyright(c) 2006-2007, Ext JS, LLC.
18865  *
18866  * Originally Released Under LGPL - original licence link has changed is not relivant.
18867  *
18868  * Fork - LGPL
18869  * <script type="text/javascript">
18870  */
18871  
18872
18873 if(Roo.dd.DragZone){
18874 Roo.tree.TreeDragZone = function(tree, config){
18875     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18876     this.tree = tree;
18877 };
18878
18879 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18880     ddGroup : "TreeDD",
18881     
18882     onBeforeDrag : function(data, e){
18883         var n = data.node;
18884         return n && n.draggable && !n.disabled;
18885     },
18886     
18887     onInitDrag : function(e){
18888         var data = this.dragData;
18889         this.tree.getSelectionModel().select(data.node);
18890         this.proxy.update("");
18891         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18892         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18893     },
18894     
18895     getRepairXY : function(e, data){
18896         return data.node.ui.getDDRepairXY();
18897     },
18898     
18899     onEndDrag : function(data, e){
18900         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18901     },
18902     
18903     onValidDrop : function(dd, e, id){
18904         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18905         this.hideProxy();
18906     },
18907     
18908     beforeInvalidDrop : function(e, id){
18909         // this scrolls the original position back into view
18910         var sm = this.tree.getSelectionModel();
18911         sm.clearSelections();
18912         sm.select(this.dragData.node);
18913     }
18914 });
18915 }/*
18916  * Based on:
18917  * Ext JS Library 1.1.1
18918  * Copyright(c) 2006-2007, Ext JS, LLC.
18919  *
18920  * Originally Released Under LGPL - original licence link has changed is not relivant.
18921  *
18922  * Fork - LGPL
18923  * <script type="text/javascript">
18924  */
18925 /**
18926  * @class Roo.tree.TreeEditor
18927  * @extends Roo.Editor
18928  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18929  * as the editor field.
18930  * @constructor
18931  * @param {TreePanel} tree
18932  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18933  */
18934 Roo.tree.TreeEditor = function(tree, config){
18935     config = config || {};
18936     var field = config.events ? config : new Roo.form.TextField(config);
18937     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18938
18939     this.tree = tree;
18940
18941     tree.on('beforeclick', this.beforeNodeClick, this);
18942     tree.getTreeEl().on('mousedown', this.hide, this);
18943     this.on('complete', this.updateNode, this);
18944     this.on('beforestartedit', this.fitToTree, this);
18945     this.on('startedit', this.bindScroll, this, {delay:10});
18946     this.on('specialkey', this.onSpecialKey, this);
18947 };
18948
18949 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18950     /**
18951      * @cfg {String} alignment
18952      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18953      */
18954     alignment: "l-l",
18955     // inherit
18956     autoSize: false,
18957     /**
18958      * @cfg {Boolean} hideEl
18959      * True to hide the bound element while the editor is displayed (defaults to false)
18960      */
18961     hideEl : false,
18962     /**
18963      * @cfg {String} cls
18964      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18965      */
18966     cls: "x-small-editor x-tree-editor",
18967     /**
18968      * @cfg {Boolean} shim
18969      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18970      */
18971     shim:false,
18972     // inherit
18973     shadow:"frame",
18974     /**
18975      * @cfg {Number} maxWidth
18976      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18977      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18978      * scroll and client offsets into account prior to each edit.
18979      */
18980     maxWidth: 250,
18981
18982     editDelay : 350,
18983
18984     // private
18985     fitToTree : function(ed, el){
18986         var td = this.tree.getTreeEl().dom, nd = el.dom;
18987         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18988             td.scrollLeft = nd.offsetLeft;
18989         }
18990         var w = Math.min(
18991                 this.maxWidth,
18992                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18993         this.setSize(w, '');
18994     },
18995
18996     // private
18997     triggerEdit : function(node){
18998         this.completeEdit();
18999         this.editNode = node;
19000         this.startEdit(node.ui.textNode, node.text);
19001     },
19002
19003     // private
19004     bindScroll : function(){
19005         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19006     },
19007
19008     // private
19009     beforeNodeClick : function(node, e){
19010         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19011         this.lastClick = new Date();
19012         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19013             e.stopEvent();
19014             this.triggerEdit(node);
19015             return false;
19016         }
19017     },
19018
19019     // private
19020     updateNode : function(ed, value){
19021         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19022         this.editNode.setText(value);
19023     },
19024
19025     // private
19026     onHide : function(){
19027         Roo.tree.TreeEditor.superclass.onHide.call(this);
19028         if(this.editNode){
19029             this.editNode.ui.focus();
19030         }
19031     },
19032
19033     // private
19034     onSpecialKey : function(field, e){
19035         var k = e.getKey();
19036         if(k == e.ESC){
19037             e.stopEvent();
19038             this.cancelEdit();
19039         }else if(k == e.ENTER && !e.hasModifier()){
19040             e.stopEvent();
19041             this.completeEdit();
19042         }
19043     }
19044 });//<Script type="text/javascript">
19045 /*
19046  * Based on:
19047  * Ext JS Library 1.1.1
19048  * Copyright(c) 2006-2007, Ext JS, LLC.
19049  *
19050  * Originally Released Under LGPL - original licence link has changed is not relivant.
19051  *
19052  * Fork - LGPL
19053  * <script type="text/javascript">
19054  */
19055  
19056 /**
19057  * Not documented??? - probably should be...
19058  */
19059
19060 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19061     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19062     
19063     renderElements : function(n, a, targetNode, bulkRender){
19064         //consel.log("renderElements?");
19065         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19066
19067         var t = n.getOwnerTree();
19068         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19069         
19070         var cols = t.columns;
19071         var bw = t.borderWidth;
19072         var c = cols[0];
19073         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19074          var cb = typeof a.checked == "boolean";
19075         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19076         var colcls = 'x-t-' + tid + '-c0';
19077         var buf = [
19078             '<li class="x-tree-node">',
19079             
19080                 
19081                 '<div class="x-tree-node-el ', a.cls,'">',
19082                     // extran...
19083                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19084                 
19085                 
19086                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19087                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19088                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19089                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19090                            (a.iconCls ? ' '+a.iconCls : ''),
19091                            '" unselectable="on" />',
19092                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19093                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19094                              
19095                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19096                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19097                             '<span unselectable="on" qtip="' + tx + '">',
19098                              tx,
19099                              '</span></a>' ,
19100                     '</div>',
19101                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19102                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19103                  ];
19104         for(var i = 1, len = cols.length; i < len; i++){
19105             c = cols[i];
19106             colcls = 'x-t-' + tid + '-c' +i;
19107             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19108             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19109                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19110                       "</div>");
19111          }
19112          
19113          buf.push(
19114             '</a>',
19115             '<div class="x-clear"></div></div>',
19116             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19117             "</li>");
19118         
19119         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19120             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19121                                 n.nextSibling.ui.getEl(), buf.join(""));
19122         }else{
19123             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19124         }
19125         var el = this.wrap.firstChild;
19126         this.elRow = el;
19127         this.elNode = el.firstChild;
19128         this.ranchor = el.childNodes[1];
19129         this.ctNode = this.wrap.childNodes[1];
19130         var cs = el.firstChild.childNodes;
19131         this.indentNode = cs[0];
19132         this.ecNode = cs[1];
19133         this.iconNode = cs[2];
19134         var index = 3;
19135         if(cb){
19136             this.checkbox = cs[3];
19137             index++;
19138         }
19139         this.anchor = cs[index];
19140         
19141         this.textNode = cs[index].firstChild;
19142         
19143         //el.on("click", this.onClick, this);
19144         //el.on("dblclick", this.onDblClick, this);
19145         
19146         
19147        // console.log(this);
19148     },
19149     initEvents : function(){
19150         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19151         
19152             
19153         var a = this.ranchor;
19154
19155         var el = Roo.get(a);
19156
19157         if(Roo.isOpera){ // opera render bug ignores the CSS
19158             el.setStyle("text-decoration", "none");
19159         }
19160
19161         el.on("click", this.onClick, this);
19162         el.on("dblclick", this.onDblClick, this);
19163         el.on("contextmenu", this.onContextMenu, this);
19164         
19165     },
19166     
19167     /*onSelectedChange : function(state){
19168         if(state){
19169             this.focus();
19170             this.addClass("x-tree-selected");
19171         }else{
19172             //this.blur();
19173             this.removeClass("x-tree-selected");
19174         }
19175     },*/
19176     addClass : function(cls){
19177         if(this.elRow){
19178             Roo.fly(this.elRow).addClass(cls);
19179         }
19180         
19181     },
19182     
19183     
19184     removeClass : function(cls){
19185         if(this.elRow){
19186             Roo.fly(this.elRow).removeClass(cls);
19187         }
19188     }
19189
19190     
19191     
19192 });//<Script type="text/javascript">
19193
19194 /*
19195  * Based on:
19196  * Ext JS Library 1.1.1
19197  * Copyright(c) 2006-2007, Ext JS, LLC.
19198  *
19199  * Originally Released Under LGPL - original licence link has changed is not relivant.
19200  *
19201  * Fork - LGPL
19202  * <script type="text/javascript">
19203  */
19204  
19205
19206 /**
19207  * @class Roo.tree.ColumnTree
19208  * @extends Roo.data.TreePanel
19209  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19210  * @cfg {int} borderWidth  compined right/left border allowance
19211  * @constructor
19212  * @param {String/HTMLElement/Element} el The container element
19213  * @param {Object} config
19214  */
19215 Roo.tree.ColumnTree =  function(el, config)
19216 {
19217    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19218    this.addEvents({
19219         /**
19220         * @event resize
19221         * Fire this event on a container when it resizes
19222         * @param {int} w Width
19223         * @param {int} h Height
19224         */
19225        "resize" : true
19226     });
19227     this.on('resize', this.onResize, this);
19228 };
19229
19230 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19231     //lines:false,
19232     
19233     
19234     borderWidth: Roo.isBorderBox ? 0 : 2, 
19235     headEls : false,
19236     
19237     render : function(){
19238         // add the header.....
19239        
19240         Roo.tree.ColumnTree.superclass.render.apply(this);
19241         
19242         this.el.addClass('x-column-tree');
19243         
19244         this.headers = this.el.createChild(
19245             {cls:'x-tree-headers'},this.innerCt.dom);
19246    
19247         var cols = this.columns, c;
19248         var totalWidth = 0;
19249         this.headEls = [];
19250         var  len = cols.length;
19251         for(var i = 0; i < len; i++){
19252              c = cols[i];
19253              totalWidth += c.width;
19254             this.headEls.push(this.headers.createChild({
19255                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19256                  cn: {
19257                      cls:'x-tree-hd-text',
19258                      html: c.header
19259                  },
19260                  style:'width:'+(c.width-this.borderWidth)+'px;'
19261              }));
19262         }
19263         this.headers.createChild({cls:'x-clear'});
19264         // prevent floats from wrapping when clipped
19265         this.headers.setWidth(totalWidth);
19266         //this.innerCt.setWidth(totalWidth);
19267         this.innerCt.setStyle({ overflow: 'auto' });
19268         this.onResize(this.width, this.height);
19269              
19270         
19271     },
19272     onResize : function(w,h)
19273     {
19274         this.height = h;
19275         this.width = w;
19276         // resize cols..
19277         this.innerCt.setWidth(this.width);
19278         this.innerCt.setHeight(this.height-20);
19279         
19280         // headers...
19281         var cols = this.columns, c;
19282         var totalWidth = 0;
19283         var expEl = false;
19284         var len = cols.length;
19285         for(var i = 0; i < len; i++){
19286             c = cols[i];
19287             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19288                 // it's the expander..
19289                 expEl  = this.headEls[i];
19290                 continue;
19291             }
19292             totalWidth += c.width;
19293             
19294         }
19295         if (expEl) {
19296             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19297         }
19298         this.headers.setWidth(w-20);
19299
19300         
19301         
19302         
19303     }
19304 });
19305 /*
19306  * Based on:
19307  * Ext JS Library 1.1.1
19308  * Copyright(c) 2006-2007, Ext JS, LLC.
19309  *
19310  * Originally Released Under LGPL - original licence link has changed is not relivant.
19311  *
19312  * Fork - LGPL
19313  * <script type="text/javascript">
19314  */
19315  
19316 /**
19317  * @class Roo.menu.Menu
19318  * @extends Roo.util.Observable
19319  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19320  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19321  * @constructor
19322  * Creates a new Menu
19323  * @param {Object} config Configuration options
19324  */
19325 Roo.menu.Menu = function(config){
19326     Roo.apply(this, config);
19327     this.id = this.id || Roo.id();
19328     this.addEvents({
19329         /**
19330          * @event beforeshow
19331          * Fires before this menu is displayed
19332          * @param {Roo.menu.Menu} this
19333          */
19334         beforeshow : true,
19335         /**
19336          * @event beforehide
19337          * Fires before this menu is hidden
19338          * @param {Roo.menu.Menu} this
19339          */
19340         beforehide : true,
19341         /**
19342          * @event show
19343          * Fires after this menu is displayed
19344          * @param {Roo.menu.Menu} this
19345          */
19346         show : true,
19347         /**
19348          * @event hide
19349          * Fires after this menu is hidden
19350          * @param {Roo.menu.Menu} this
19351          */
19352         hide : true,
19353         /**
19354          * @event click
19355          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19356          * @param {Roo.menu.Menu} this
19357          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19358          * @param {Roo.EventObject} e
19359          */
19360         click : true,
19361         /**
19362          * @event mouseover
19363          * Fires when the mouse is hovering over this menu
19364          * @param {Roo.menu.Menu} this
19365          * @param {Roo.EventObject} e
19366          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19367          */
19368         mouseover : true,
19369         /**
19370          * @event mouseout
19371          * Fires when the mouse exits this menu
19372          * @param {Roo.menu.Menu} this
19373          * @param {Roo.EventObject} e
19374          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19375          */
19376         mouseout : true,
19377         /**
19378          * @event itemclick
19379          * Fires when a menu item contained in this menu is clicked
19380          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19381          * @param {Roo.EventObject} e
19382          */
19383         itemclick: true
19384     });
19385     if (this.registerMenu) {
19386         Roo.menu.MenuMgr.register(this);
19387     }
19388     
19389     var mis = this.items;
19390     this.items = new Roo.util.MixedCollection();
19391     if(mis){
19392         this.add.apply(this, mis);
19393     }
19394 };
19395
19396 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19397     /**
19398      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19399      */
19400     minWidth : 120,
19401     /**
19402      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19403      * for bottom-right shadow (defaults to "sides")
19404      */
19405     shadow : "sides",
19406     /**
19407      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19408      * this menu (defaults to "tl-tr?")
19409      */
19410     subMenuAlign : "tl-tr?",
19411     /**
19412      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19413      * relative to its element of origin (defaults to "tl-bl?")
19414      */
19415     defaultAlign : "tl-bl?",
19416     /**
19417      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19418      */
19419     allowOtherMenus : false,
19420     /**
19421      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19422      */
19423     registerMenu : true,
19424
19425     hidden:true,
19426
19427     // private
19428     render : function(){
19429         if(this.el){
19430             return;
19431         }
19432         var el = this.el = new Roo.Layer({
19433             cls: "x-menu",
19434             shadow:this.shadow,
19435             constrain: false,
19436             parentEl: this.parentEl || document.body,
19437             zindex:15000
19438         });
19439
19440         this.keyNav = new Roo.menu.MenuNav(this);
19441
19442         if(this.plain){
19443             el.addClass("x-menu-plain");
19444         }
19445         if(this.cls){
19446             el.addClass(this.cls);
19447         }
19448         // generic focus element
19449         this.focusEl = el.createChild({
19450             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19451         });
19452         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19453         ul.on("click", this.onClick, this);
19454         ul.on("mouseover", this.onMouseOver, this);
19455         ul.on("mouseout", this.onMouseOut, this);
19456         this.items.each(function(item){
19457             var li = document.createElement("li");
19458             li.className = "x-menu-list-item";
19459             ul.dom.appendChild(li);
19460             item.render(li, this);
19461         }, this);
19462         this.ul = ul;
19463         this.autoWidth();
19464     },
19465
19466     // private
19467     autoWidth : function(){
19468         var el = this.el, ul = this.ul;
19469         if(!el){
19470             return;
19471         }
19472         var w = this.width;
19473         if(w){
19474             el.setWidth(w);
19475         }else if(Roo.isIE){
19476             el.setWidth(this.minWidth);
19477             var t = el.dom.offsetWidth; // force recalc
19478             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19479         }
19480     },
19481
19482     // private
19483     delayAutoWidth : function(){
19484         if(this.rendered){
19485             if(!this.awTask){
19486                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19487             }
19488             this.awTask.delay(20);
19489         }
19490     },
19491
19492     // private
19493     findTargetItem : function(e){
19494         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19495         if(t && t.menuItemId){
19496             return this.items.get(t.menuItemId);
19497         }
19498     },
19499
19500     // private
19501     onClick : function(e){
19502         var t;
19503         if(t = this.findTargetItem(e)){
19504             t.onClick(e);
19505             this.fireEvent("click", this, t, e);
19506         }
19507     },
19508
19509     // private
19510     setActiveItem : function(item, autoExpand){
19511         if(item != this.activeItem){
19512             if(this.activeItem){
19513                 this.activeItem.deactivate();
19514             }
19515             this.activeItem = item;
19516             item.activate(autoExpand);
19517         }else if(autoExpand){
19518             item.expandMenu();
19519         }
19520     },
19521
19522     // private
19523     tryActivate : function(start, step){
19524         var items = this.items;
19525         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19526             var item = items.get(i);
19527             if(!item.disabled && item.canActivate){
19528                 this.setActiveItem(item, false);
19529                 return item;
19530             }
19531         }
19532         return false;
19533     },
19534
19535     // private
19536     onMouseOver : function(e){
19537         var t;
19538         if(t = this.findTargetItem(e)){
19539             if(t.canActivate && !t.disabled){
19540                 this.setActiveItem(t, true);
19541             }
19542         }
19543         this.fireEvent("mouseover", this, e, t);
19544     },
19545
19546     // private
19547     onMouseOut : function(e){
19548         var t;
19549         if(t = this.findTargetItem(e)){
19550             if(t == this.activeItem && t.shouldDeactivate(e)){
19551                 this.activeItem.deactivate();
19552                 delete this.activeItem;
19553             }
19554         }
19555         this.fireEvent("mouseout", this, e, t);
19556     },
19557
19558     /**
19559      * Read-only.  Returns true if the menu is currently displayed, else false.
19560      * @type Boolean
19561      */
19562     isVisible : function(){
19563         return this.el && !this.hidden;
19564     },
19565
19566     /**
19567      * Displays this menu relative to another element
19568      * @param {String/HTMLElement/Roo.Element} element The element to align to
19569      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19570      * the element (defaults to this.defaultAlign)
19571      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19572      */
19573     show : function(el, pos, parentMenu){
19574         this.parentMenu = parentMenu;
19575         if(!this.el){
19576             this.render();
19577         }
19578         this.fireEvent("beforeshow", this);
19579         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19580     },
19581
19582     /**
19583      * Displays this menu at a specific xy position
19584      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19585      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19586      */
19587     showAt : function(xy, parentMenu, /* private: */_e){
19588         this.parentMenu = parentMenu;
19589         if(!this.el){
19590             this.render();
19591         }
19592         if(_e !== false){
19593             this.fireEvent("beforeshow", this);
19594             xy = this.el.adjustForConstraints(xy);
19595         }
19596         this.el.setXY(xy);
19597         this.el.show();
19598         this.hidden = false;
19599         this.focus();
19600         this.fireEvent("show", this);
19601     },
19602
19603     focus : function(){
19604         if(!this.hidden){
19605             this.doFocus.defer(50, this);
19606         }
19607     },
19608
19609     doFocus : function(){
19610         if(!this.hidden){
19611             this.focusEl.focus();
19612         }
19613     },
19614
19615     /**
19616      * Hides this menu and optionally all parent menus
19617      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19618      */
19619     hide : function(deep){
19620         if(this.el && this.isVisible()){
19621             this.fireEvent("beforehide", this);
19622             if(this.activeItem){
19623                 this.activeItem.deactivate();
19624                 this.activeItem = null;
19625             }
19626             this.el.hide();
19627             this.hidden = true;
19628             this.fireEvent("hide", this);
19629         }
19630         if(deep === true && this.parentMenu){
19631             this.parentMenu.hide(true);
19632         }
19633     },
19634
19635     /**
19636      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19637      * Any of the following are valid:
19638      * <ul>
19639      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19640      * <li>An HTMLElement object which will be converted to a menu item</li>
19641      * <li>A menu item config object that will be created as a new menu item</li>
19642      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19643      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19644      * </ul>
19645      * Usage:
19646      * <pre><code>
19647 // Create the menu
19648 var menu = new Roo.menu.Menu();
19649
19650 // Create a menu item to add by reference
19651 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19652
19653 // Add a bunch of items at once using different methods.
19654 // Only the last item added will be returned.
19655 var item = menu.add(
19656     menuItem,                // add existing item by ref
19657     'Dynamic Item',          // new TextItem
19658     '-',                     // new separator
19659     { text: 'Config Item' }  // new item by config
19660 );
19661 </code></pre>
19662      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19663      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19664      */
19665     add : function(){
19666         var a = arguments, l = a.length, item;
19667         for(var i = 0; i < l; i++){
19668             var el = a[i];
19669             if ((typeof(el) == "object") && el.xtype && el.xns) {
19670                 el = Roo.factory(el, Roo.menu);
19671             }
19672             
19673             if(el.render){ // some kind of Item
19674                 item = this.addItem(el);
19675             }else if(typeof el == "string"){ // string
19676                 if(el == "separator" || el == "-"){
19677                     item = this.addSeparator();
19678                 }else{
19679                     item = this.addText(el);
19680                 }
19681             }else if(el.tagName || el.el){ // element
19682                 item = this.addElement(el);
19683             }else if(typeof el == "object"){ // must be menu item config?
19684                 item = this.addMenuItem(el);
19685             }
19686         }
19687         return item;
19688     },
19689
19690     /**
19691      * Returns this menu's underlying {@link Roo.Element} object
19692      * @return {Roo.Element} The element
19693      */
19694     getEl : function(){
19695         if(!this.el){
19696             this.render();
19697         }
19698         return this.el;
19699     },
19700
19701     /**
19702      * Adds a separator bar to the menu
19703      * @return {Roo.menu.Item} The menu item that was added
19704      */
19705     addSeparator : function(){
19706         return this.addItem(new Roo.menu.Separator());
19707     },
19708
19709     /**
19710      * Adds an {@link Roo.Element} object to the menu
19711      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19712      * @return {Roo.menu.Item} The menu item that was added
19713      */
19714     addElement : function(el){
19715         return this.addItem(new Roo.menu.BaseItem(el));
19716     },
19717
19718     /**
19719      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19720      * @param {Roo.menu.Item} item The menu item to add
19721      * @return {Roo.menu.Item} The menu item that was added
19722      */
19723     addItem : function(item){
19724         this.items.add(item);
19725         if(this.ul){
19726             var li = document.createElement("li");
19727             li.className = "x-menu-list-item";
19728             this.ul.dom.appendChild(li);
19729             item.render(li, this);
19730             this.delayAutoWidth();
19731         }
19732         return item;
19733     },
19734
19735     /**
19736      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19737      * @param {Object} config A MenuItem config object
19738      * @return {Roo.menu.Item} The menu item that was added
19739      */
19740     addMenuItem : function(config){
19741         if(!(config instanceof Roo.menu.Item)){
19742             if(typeof config.checked == "boolean"){ // must be check menu item config?
19743                 config = new Roo.menu.CheckItem(config);
19744             }else{
19745                 config = new Roo.menu.Item(config);
19746             }
19747         }
19748         return this.addItem(config);
19749     },
19750
19751     /**
19752      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19753      * @param {String} text The text to display in the menu item
19754      * @return {Roo.menu.Item} The menu item that was added
19755      */
19756     addText : function(text){
19757         return this.addItem(new Roo.menu.TextItem({ text : text }));
19758     },
19759
19760     /**
19761      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19762      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19763      * @param {Roo.menu.Item} item The menu item to add
19764      * @return {Roo.menu.Item} The menu item that was added
19765      */
19766     insert : function(index, item){
19767         this.items.insert(index, item);
19768         if(this.ul){
19769             var li = document.createElement("li");
19770             li.className = "x-menu-list-item";
19771             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19772             item.render(li, this);
19773             this.delayAutoWidth();
19774         }
19775         return item;
19776     },
19777
19778     /**
19779      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19780      * @param {Roo.menu.Item} item The menu item to remove
19781      */
19782     remove : function(item){
19783         this.items.removeKey(item.id);
19784         item.destroy();
19785     },
19786
19787     /**
19788      * Removes and destroys all items in the menu
19789      */
19790     removeAll : function(){
19791         var f;
19792         while(f = this.items.first()){
19793             this.remove(f);
19794         }
19795     }
19796 });
19797
19798 // MenuNav is a private utility class used internally by the Menu
19799 Roo.menu.MenuNav = function(menu){
19800     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19801     this.scope = this.menu = menu;
19802 };
19803
19804 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19805     doRelay : function(e, h){
19806         var k = e.getKey();
19807         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19808             this.menu.tryActivate(0, 1);
19809             return false;
19810         }
19811         return h.call(this.scope || this, e, this.menu);
19812     },
19813
19814     up : function(e, m){
19815         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19816             m.tryActivate(m.items.length-1, -1);
19817         }
19818     },
19819
19820     down : function(e, m){
19821         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19822             m.tryActivate(0, 1);
19823         }
19824     },
19825
19826     right : function(e, m){
19827         if(m.activeItem){
19828             m.activeItem.expandMenu(true);
19829         }
19830     },
19831
19832     left : function(e, m){
19833         m.hide();
19834         if(m.parentMenu && m.parentMenu.activeItem){
19835             m.parentMenu.activeItem.activate();
19836         }
19837     },
19838
19839     enter : function(e, m){
19840         if(m.activeItem){
19841             e.stopPropagation();
19842             m.activeItem.onClick(e);
19843             m.fireEvent("click", this, m.activeItem);
19844             return true;
19845         }
19846     }
19847 });/*
19848  * Based on:
19849  * Ext JS Library 1.1.1
19850  * Copyright(c) 2006-2007, Ext JS, LLC.
19851  *
19852  * Originally Released Under LGPL - original licence link has changed is not relivant.
19853  *
19854  * Fork - LGPL
19855  * <script type="text/javascript">
19856  */
19857  
19858 /**
19859  * @class Roo.menu.MenuMgr
19860  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19861  * @singleton
19862  */
19863 Roo.menu.MenuMgr = function(){
19864    var menus, active, groups = {}, attached = false, lastShow = new Date();
19865
19866    // private - called when first menu is created
19867    function init(){
19868        menus = {};
19869        active = new Roo.util.MixedCollection();
19870        Roo.get(document).addKeyListener(27, function(){
19871            if(active.length > 0){
19872                hideAll();
19873            }
19874        });
19875    }
19876
19877    // private
19878    function hideAll(){
19879        if(active && active.length > 0){
19880            var c = active.clone();
19881            c.each(function(m){
19882                m.hide();
19883            });
19884        }
19885    }
19886
19887    // private
19888    function onHide(m){
19889        active.remove(m);
19890        if(active.length < 1){
19891            Roo.get(document).un("mousedown", onMouseDown);
19892            attached = false;
19893        }
19894    }
19895
19896    // private
19897    function onShow(m){
19898        var last = active.last();
19899        lastShow = new Date();
19900        active.add(m);
19901        if(!attached){
19902            Roo.get(document).on("mousedown", onMouseDown);
19903            attached = true;
19904        }
19905        if(m.parentMenu){
19906           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19907           m.parentMenu.activeChild = m;
19908        }else if(last && last.isVisible()){
19909           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19910        }
19911    }
19912
19913    // private
19914    function onBeforeHide(m){
19915        if(m.activeChild){
19916            m.activeChild.hide();
19917        }
19918        if(m.autoHideTimer){
19919            clearTimeout(m.autoHideTimer);
19920            delete m.autoHideTimer;
19921        }
19922    }
19923
19924    // private
19925    function onBeforeShow(m){
19926        var pm = m.parentMenu;
19927        if(!pm && !m.allowOtherMenus){
19928            hideAll();
19929        }else if(pm && pm.activeChild && active != m){
19930            pm.activeChild.hide();
19931        }
19932    }
19933
19934    // private
19935    function onMouseDown(e){
19936        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19937            hideAll();
19938        }
19939    }
19940
19941    // private
19942    function onBeforeCheck(mi, state){
19943        if(state){
19944            var g = groups[mi.group];
19945            for(var i = 0, l = g.length; i < l; i++){
19946                if(g[i] != mi){
19947                    g[i].setChecked(false);
19948                }
19949            }
19950        }
19951    }
19952
19953    return {
19954
19955        /**
19956         * Hides all menus that are currently visible
19957         */
19958        hideAll : function(){
19959             hideAll();  
19960        },
19961
19962        // private
19963        register : function(menu){
19964            if(!menus){
19965                init();
19966            }
19967            menus[menu.id] = menu;
19968            menu.on("beforehide", onBeforeHide);
19969            menu.on("hide", onHide);
19970            menu.on("beforeshow", onBeforeShow);
19971            menu.on("show", onShow);
19972            var g = menu.group;
19973            if(g && menu.events["checkchange"]){
19974                if(!groups[g]){
19975                    groups[g] = [];
19976                }
19977                groups[g].push(menu);
19978                menu.on("checkchange", onCheck);
19979            }
19980        },
19981
19982         /**
19983          * Returns a {@link Roo.menu.Menu} object
19984          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19985          * be used to generate and return a new Menu instance.
19986          */
19987        get : function(menu){
19988            if(typeof menu == "string"){ // menu id
19989                return menus[menu];
19990            }else if(menu.events){  // menu instance
19991                return menu;
19992            }else if(typeof menu.length == 'number'){ // array of menu items?
19993                return new Roo.menu.Menu({items:menu});
19994            }else{ // otherwise, must be a config
19995                return new Roo.menu.Menu(menu);
19996            }
19997        },
19998
19999        // private
20000        unregister : function(menu){
20001            delete menus[menu.id];
20002            menu.un("beforehide", onBeforeHide);
20003            menu.un("hide", onHide);
20004            menu.un("beforeshow", onBeforeShow);
20005            menu.un("show", onShow);
20006            var g = menu.group;
20007            if(g && menu.events["checkchange"]){
20008                groups[g].remove(menu);
20009                menu.un("checkchange", onCheck);
20010            }
20011        },
20012
20013        // private
20014        registerCheckable : function(menuItem){
20015            var g = menuItem.group;
20016            if(g){
20017                if(!groups[g]){
20018                    groups[g] = [];
20019                }
20020                groups[g].push(menuItem);
20021                menuItem.on("beforecheckchange", onBeforeCheck);
20022            }
20023        },
20024
20025        // private
20026        unregisterCheckable : function(menuItem){
20027            var g = menuItem.group;
20028            if(g){
20029                groups[g].remove(menuItem);
20030                menuItem.un("beforecheckchange", onBeforeCheck);
20031            }
20032        }
20033    };
20034 }();/*
20035  * Based on:
20036  * Ext JS Library 1.1.1
20037  * Copyright(c) 2006-2007, Ext JS, LLC.
20038  *
20039  * Originally Released Under LGPL - original licence link has changed is not relivant.
20040  *
20041  * Fork - LGPL
20042  * <script type="text/javascript">
20043  */
20044  
20045
20046 /**
20047  * @class Roo.menu.BaseItem
20048  * @extends Roo.Component
20049  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20050  * management and base configuration options shared by all menu components.
20051  * @constructor
20052  * Creates a new BaseItem
20053  * @param {Object} config Configuration options
20054  */
20055 Roo.menu.BaseItem = function(config){
20056     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20057
20058     this.addEvents({
20059         /**
20060          * @event click
20061          * Fires when this item is clicked
20062          * @param {Roo.menu.BaseItem} this
20063          * @param {Roo.EventObject} e
20064          */
20065         click: true,
20066         /**
20067          * @event activate
20068          * Fires when this item is activated
20069          * @param {Roo.menu.BaseItem} this
20070          */
20071         activate : true,
20072         /**
20073          * @event deactivate
20074          * Fires when this item is deactivated
20075          * @param {Roo.menu.BaseItem} this
20076          */
20077         deactivate : true
20078     });
20079
20080     if(this.handler){
20081         this.on("click", this.handler, this.scope, true);
20082     }
20083 };
20084
20085 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20086     /**
20087      * @cfg {Function} handler
20088      * A function that will handle the click event of this menu item (defaults to undefined)
20089      */
20090     /**
20091      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20092      */
20093     canActivate : false,
20094     /**
20095      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20096      */
20097     activeClass : "x-menu-item-active",
20098     /**
20099      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20100      */
20101     hideOnClick : true,
20102     /**
20103      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20104      */
20105     hideDelay : 100,
20106
20107     // private
20108     ctype: "Roo.menu.BaseItem",
20109
20110     // private
20111     actionMode : "container",
20112
20113     // private
20114     render : function(container, parentMenu){
20115         this.parentMenu = parentMenu;
20116         Roo.menu.BaseItem.superclass.render.call(this, container);
20117         this.container.menuItemId = this.id;
20118     },
20119
20120     // private
20121     onRender : function(container, position){
20122         this.el = Roo.get(this.el);
20123         container.dom.appendChild(this.el.dom);
20124     },
20125
20126     // private
20127     onClick : function(e){
20128         if(!this.disabled && this.fireEvent("click", this, e) !== false
20129                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20130             this.handleClick(e);
20131         }else{
20132             e.stopEvent();
20133         }
20134     },
20135
20136     // private
20137     activate : function(){
20138         if(this.disabled){
20139             return false;
20140         }
20141         var li = this.container;
20142         li.addClass(this.activeClass);
20143         this.region = li.getRegion().adjust(2, 2, -2, -2);
20144         this.fireEvent("activate", this);
20145         return true;
20146     },
20147
20148     // private
20149     deactivate : function(){
20150         this.container.removeClass(this.activeClass);
20151         this.fireEvent("deactivate", this);
20152     },
20153
20154     // private
20155     shouldDeactivate : function(e){
20156         return !this.region || !this.region.contains(e.getPoint());
20157     },
20158
20159     // private
20160     handleClick : function(e){
20161         if(this.hideOnClick){
20162             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20163         }
20164     },
20165
20166     // private
20167     expandMenu : function(autoActivate){
20168         // do nothing
20169     },
20170
20171     // private
20172     hideMenu : function(){
20173         // do nothing
20174     }
20175 });/*
20176  * Based on:
20177  * Ext JS Library 1.1.1
20178  * Copyright(c) 2006-2007, Ext JS, LLC.
20179  *
20180  * Originally Released Under LGPL - original licence link has changed is not relivant.
20181  *
20182  * Fork - LGPL
20183  * <script type="text/javascript">
20184  */
20185  
20186 /**
20187  * @class Roo.menu.Adapter
20188  * @extends Roo.menu.BaseItem
20189  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20190  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20191  * @constructor
20192  * Creates a new Adapter
20193  * @param {Object} config Configuration options
20194  */
20195 Roo.menu.Adapter = function(component, config){
20196     Roo.menu.Adapter.superclass.constructor.call(this, config);
20197     this.component = component;
20198 };
20199 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20200     // private
20201     canActivate : true,
20202
20203     // private
20204     onRender : function(container, position){
20205         this.component.render(container);
20206         this.el = this.component.getEl();
20207     },
20208
20209     // private
20210     activate : function(){
20211         if(this.disabled){
20212             return false;
20213         }
20214         this.component.focus();
20215         this.fireEvent("activate", this);
20216         return true;
20217     },
20218
20219     // private
20220     deactivate : function(){
20221         this.fireEvent("deactivate", this);
20222     },
20223
20224     // private
20225     disable : function(){
20226         this.component.disable();
20227         Roo.menu.Adapter.superclass.disable.call(this);
20228     },
20229
20230     // private
20231     enable : function(){
20232         this.component.enable();
20233         Roo.menu.Adapter.superclass.enable.call(this);
20234     }
20235 });/*
20236  * Based on:
20237  * Ext JS Library 1.1.1
20238  * Copyright(c) 2006-2007, Ext JS, LLC.
20239  *
20240  * Originally Released Under LGPL - original licence link has changed is not relivant.
20241  *
20242  * Fork - LGPL
20243  * <script type="text/javascript">
20244  */
20245
20246 /**
20247  * @class Roo.menu.TextItem
20248  * @extends Roo.menu.BaseItem
20249  * Adds a static text string to a menu, usually used as either a heading or group separator.
20250  * Note: old style constructor with text is still supported.
20251  * 
20252  * @constructor
20253  * Creates a new TextItem
20254  * @param {Object} cfg Configuration
20255  */
20256 Roo.menu.TextItem = function(cfg){
20257     if (typeof(cfg) == 'string') {
20258         this.text = cfg;
20259     } else {
20260         Roo.apply(this,cfg);
20261     }
20262     
20263     Roo.menu.TextItem.superclass.constructor.call(this);
20264 };
20265
20266 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20267     /**
20268      * @cfg {Boolean} text Text to show on item.
20269      */
20270     text : '',
20271     
20272     /**
20273      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20274      */
20275     hideOnClick : false,
20276     /**
20277      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20278      */
20279     itemCls : "x-menu-text",
20280
20281     // private
20282     onRender : function(){
20283         var s = document.createElement("span");
20284         s.className = this.itemCls;
20285         s.innerHTML = this.text;
20286         this.el = s;
20287         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20288     }
20289 });/*
20290  * Based on:
20291  * Ext JS Library 1.1.1
20292  * Copyright(c) 2006-2007, Ext JS, LLC.
20293  *
20294  * Originally Released Under LGPL - original licence link has changed is not relivant.
20295  *
20296  * Fork - LGPL
20297  * <script type="text/javascript">
20298  */
20299
20300 /**
20301  * @class Roo.menu.Separator
20302  * @extends Roo.menu.BaseItem
20303  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20304  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20305  * @constructor
20306  * @param {Object} config Configuration options
20307  */
20308 Roo.menu.Separator = function(config){
20309     Roo.menu.Separator.superclass.constructor.call(this, config);
20310 };
20311
20312 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20313     /**
20314      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20315      */
20316     itemCls : "x-menu-sep",
20317     /**
20318      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20319      */
20320     hideOnClick : false,
20321
20322     // private
20323     onRender : function(li){
20324         var s = document.createElement("span");
20325         s.className = this.itemCls;
20326         s.innerHTML = "&#160;";
20327         this.el = s;
20328         li.addClass("x-menu-sep-li");
20329         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20330     }
20331 });/*
20332  * Based on:
20333  * Ext JS Library 1.1.1
20334  * Copyright(c) 2006-2007, Ext JS, LLC.
20335  *
20336  * Originally Released Under LGPL - original licence link has changed is not relivant.
20337  *
20338  * Fork - LGPL
20339  * <script type="text/javascript">
20340  */
20341 /**
20342  * @class Roo.menu.Item
20343  * @extends Roo.menu.BaseItem
20344  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20345  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20346  * activation and click handling.
20347  * @constructor
20348  * Creates a new Item
20349  * @param {Object} config Configuration options
20350  */
20351 Roo.menu.Item = function(config){
20352     Roo.menu.Item.superclass.constructor.call(this, config);
20353     if(this.menu){
20354         this.menu = Roo.menu.MenuMgr.get(this.menu);
20355     }
20356 };
20357 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20358     
20359     /**
20360      * @cfg {String} text
20361      * The text to show on the menu item.
20362      */
20363     text: '',
20364      /**
20365      * @cfg {String} HTML to render in menu
20366      * The text to show on the menu item (HTML version).
20367      */
20368     html: '',
20369     /**
20370      * @cfg {String} icon
20371      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20372      */
20373     icon: undefined,
20374     /**
20375      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20376      */
20377     itemCls : "x-menu-item",
20378     /**
20379      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20380      */
20381     canActivate : true,
20382     /**
20383      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20384      */
20385     showDelay: 200,
20386     // doc'd in BaseItem
20387     hideDelay: 200,
20388
20389     // private
20390     ctype: "Roo.menu.Item",
20391     
20392     // private
20393     onRender : function(container, position){
20394         var el = document.createElement("a");
20395         el.hideFocus = true;
20396         el.unselectable = "on";
20397         el.href = this.href || "#";
20398         if(this.hrefTarget){
20399             el.target = this.hrefTarget;
20400         }
20401         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20402         
20403         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20404         
20405         el.innerHTML = String.format(
20406                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20407                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20408         this.el = el;
20409         Roo.menu.Item.superclass.onRender.call(this, container, position);
20410     },
20411
20412     /**
20413      * Sets the text to display in this menu item
20414      * @param {String} text The text to display
20415      * @param {Boolean} isHTML true to indicate text is pure html.
20416      */
20417     setText : function(text, isHTML){
20418         if (isHTML) {
20419             this.html = text;
20420         } else {
20421             this.text = text;
20422             this.html = '';
20423         }
20424         if(this.rendered){
20425             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20426      
20427             this.el.update(String.format(
20428                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20429                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20430             this.parentMenu.autoWidth();
20431         }
20432     },
20433
20434     // private
20435     handleClick : function(e){
20436         if(!this.href){ // if no link defined, stop the event automatically
20437             e.stopEvent();
20438         }
20439         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20440     },
20441
20442     // private
20443     activate : function(autoExpand){
20444         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20445             this.focus();
20446             if(autoExpand){
20447                 this.expandMenu();
20448             }
20449         }
20450         return true;
20451     },
20452
20453     // private
20454     shouldDeactivate : function(e){
20455         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20456             if(this.menu && this.menu.isVisible()){
20457                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20458             }
20459             return true;
20460         }
20461         return false;
20462     },
20463
20464     // private
20465     deactivate : function(){
20466         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20467         this.hideMenu();
20468     },
20469
20470     // private
20471     expandMenu : function(autoActivate){
20472         if(!this.disabled && this.menu){
20473             clearTimeout(this.hideTimer);
20474             delete this.hideTimer;
20475             if(!this.menu.isVisible() && !this.showTimer){
20476                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20477             }else if (this.menu.isVisible() && autoActivate){
20478                 this.menu.tryActivate(0, 1);
20479             }
20480         }
20481     },
20482
20483     // private
20484     deferExpand : function(autoActivate){
20485         delete this.showTimer;
20486         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20487         if(autoActivate){
20488             this.menu.tryActivate(0, 1);
20489         }
20490     },
20491
20492     // private
20493     hideMenu : function(){
20494         clearTimeout(this.showTimer);
20495         delete this.showTimer;
20496         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20497             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20498         }
20499     },
20500
20501     // private
20502     deferHide : function(){
20503         delete this.hideTimer;
20504         this.menu.hide();
20505     }
20506 });/*
20507  * Based on:
20508  * Ext JS Library 1.1.1
20509  * Copyright(c) 2006-2007, Ext JS, LLC.
20510  *
20511  * Originally Released Under LGPL - original licence link has changed is not relivant.
20512  *
20513  * Fork - LGPL
20514  * <script type="text/javascript">
20515  */
20516  
20517 /**
20518  * @class Roo.menu.CheckItem
20519  * @extends Roo.menu.Item
20520  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20521  * @constructor
20522  * Creates a new CheckItem
20523  * @param {Object} config Configuration options
20524  */
20525 Roo.menu.CheckItem = function(config){
20526     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20527     this.addEvents({
20528         /**
20529          * @event beforecheckchange
20530          * Fires before the checked value is set, providing an opportunity to cancel if needed
20531          * @param {Roo.menu.CheckItem} this
20532          * @param {Boolean} checked The new checked value that will be set
20533          */
20534         "beforecheckchange" : true,
20535         /**
20536          * @event checkchange
20537          * Fires after the checked value has been set
20538          * @param {Roo.menu.CheckItem} this
20539          * @param {Boolean} checked The checked value that was set
20540          */
20541         "checkchange" : true
20542     });
20543     if(this.checkHandler){
20544         this.on('checkchange', this.checkHandler, this.scope);
20545     }
20546 };
20547 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20548     /**
20549      * @cfg {String} group
20550      * All check items with the same group name will automatically be grouped into a single-select
20551      * radio button group (defaults to '')
20552      */
20553     /**
20554      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20555      */
20556     itemCls : "x-menu-item x-menu-check-item",
20557     /**
20558      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20559      */
20560     groupClass : "x-menu-group-item",
20561
20562     /**
20563      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20564      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20565      * initialized with checked = true will be rendered as checked.
20566      */
20567     checked: false,
20568
20569     // private
20570     ctype: "Roo.menu.CheckItem",
20571
20572     // private
20573     onRender : function(c){
20574         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20575         if(this.group){
20576             this.el.addClass(this.groupClass);
20577         }
20578         Roo.menu.MenuMgr.registerCheckable(this);
20579         if(this.checked){
20580             this.checked = false;
20581             this.setChecked(true, true);
20582         }
20583     },
20584
20585     // private
20586     destroy : function(){
20587         if(this.rendered){
20588             Roo.menu.MenuMgr.unregisterCheckable(this);
20589         }
20590         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20591     },
20592
20593     /**
20594      * Set the checked state of this item
20595      * @param {Boolean} checked The new checked value
20596      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20597      */
20598     setChecked : function(state, suppressEvent){
20599         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20600             if(this.container){
20601                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20602             }
20603             this.checked = state;
20604             if(suppressEvent !== true){
20605                 this.fireEvent("checkchange", this, state);
20606             }
20607         }
20608     },
20609
20610     // private
20611     handleClick : function(e){
20612        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20613            this.setChecked(!this.checked);
20614        }
20615        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20616     }
20617 });/*
20618  * Based on:
20619  * Ext JS Library 1.1.1
20620  * Copyright(c) 2006-2007, Ext JS, LLC.
20621  *
20622  * Originally Released Under LGPL - original licence link has changed is not relivant.
20623  *
20624  * Fork - LGPL
20625  * <script type="text/javascript">
20626  */
20627  
20628 /**
20629  * @class Roo.menu.DateItem
20630  * @extends Roo.menu.Adapter
20631  * A menu item that wraps the {@link Roo.DatPicker} component.
20632  * @constructor
20633  * Creates a new DateItem
20634  * @param {Object} config Configuration options
20635  */
20636 Roo.menu.DateItem = function(config){
20637     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20638     /** The Roo.DatePicker object @type Roo.DatePicker */
20639     this.picker = this.component;
20640     this.addEvents({select: true});
20641     
20642     this.picker.on("render", function(picker){
20643         picker.getEl().swallowEvent("click");
20644         picker.container.addClass("x-menu-date-item");
20645     });
20646
20647     this.picker.on("select", this.onSelect, this);
20648 };
20649
20650 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20651     // private
20652     onSelect : function(picker, date){
20653         this.fireEvent("select", this, date, picker);
20654         Roo.menu.DateItem.superclass.handleClick.call(this);
20655     }
20656 });/*
20657  * Based on:
20658  * Ext JS Library 1.1.1
20659  * Copyright(c) 2006-2007, Ext JS, LLC.
20660  *
20661  * Originally Released Under LGPL - original licence link has changed is not relivant.
20662  *
20663  * Fork - LGPL
20664  * <script type="text/javascript">
20665  */
20666  
20667 /**
20668  * @class Roo.menu.ColorItem
20669  * @extends Roo.menu.Adapter
20670  * A menu item that wraps the {@link Roo.ColorPalette} component.
20671  * @constructor
20672  * Creates a new ColorItem
20673  * @param {Object} config Configuration options
20674  */
20675 Roo.menu.ColorItem = function(config){
20676     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20677     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20678     this.palette = this.component;
20679     this.relayEvents(this.palette, ["select"]);
20680     if(this.selectHandler){
20681         this.on('select', this.selectHandler, this.scope);
20682     }
20683 };
20684 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20685  * Based on:
20686  * Ext JS Library 1.1.1
20687  * Copyright(c) 2006-2007, Ext JS, LLC.
20688  *
20689  * Originally Released Under LGPL - original licence link has changed is not relivant.
20690  *
20691  * Fork - LGPL
20692  * <script type="text/javascript">
20693  */
20694  
20695
20696 /**
20697  * @class Roo.menu.DateMenu
20698  * @extends Roo.menu.Menu
20699  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20700  * @constructor
20701  * Creates a new DateMenu
20702  * @param {Object} config Configuration options
20703  */
20704 Roo.menu.DateMenu = function(config){
20705     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20706     this.plain = true;
20707     var di = new Roo.menu.DateItem(config);
20708     this.add(di);
20709     /**
20710      * The {@link Roo.DatePicker} instance for this DateMenu
20711      * @type DatePicker
20712      */
20713     this.picker = di.picker;
20714     /**
20715      * @event select
20716      * @param {DatePicker} picker
20717      * @param {Date} date
20718      */
20719     this.relayEvents(di, ["select"]);
20720
20721     this.on('beforeshow', function(){
20722         if(this.picker){
20723             this.picker.hideMonthPicker(true);
20724         }
20725     }, this);
20726 };
20727 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20728     cls:'x-date-menu'
20729 });/*
20730  * Based on:
20731  * Ext JS Library 1.1.1
20732  * Copyright(c) 2006-2007, Ext JS, LLC.
20733  *
20734  * Originally Released Under LGPL - original licence link has changed is not relivant.
20735  *
20736  * Fork - LGPL
20737  * <script type="text/javascript">
20738  */
20739  
20740
20741 /**
20742  * @class Roo.menu.ColorMenu
20743  * @extends Roo.menu.Menu
20744  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20745  * @constructor
20746  * Creates a new ColorMenu
20747  * @param {Object} config Configuration options
20748  */
20749 Roo.menu.ColorMenu = function(config){
20750     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20751     this.plain = true;
20752     var ci = new Roo.menu.ColorItem(config);
20753     this.add(ci);
20754     /**
20755      * The {@link Roo.ColorPalette} instance for this ColorMenu
20756      * @type ColorPalette
20757      */
20758     this.palette = ci.palette;
20759     /**
20760      * @event select
20761      * @param {ColorPalette} palette
20762      * @param {String} color
20763      */
20764     this.relayEvents(ci, ["select"]);
20765 };
20766 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20767  * Based on:
20768  * Ext JS Library 1.1.1
20769  * Copyright(c) 2006-2007, Ext JS, LLC.
20770  *
20771  * Originally Released Under LGPL - original licence link has changed is not relivant.
20772  *
20773  * Fork - LGPL
20774  * <script type="text/javascript">
20775  */
20776  
20777 /**
20778  * @class Roo.form.Field
20779  * @extends Roo.BoxComponent
20780  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20781  * @constructor
20782  * Creates a new Field
20783  * @param {Object} config Configuration options
20784  */
20785 Roo.form.Field = function(config){
20786     Roo.form.Field.superclass.constructor.call(this, config);
20787 };
20788
20789 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20790     /**
20791      * @cfg {String} fieldLabel Label to use when rendering a form.
20792      */
20793        /**
20794      * @cfg {String} qtip Mouse over tip
20795      */
20796      
20797     /**
20798      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20799      */
20800     invalidClass : "x-form-invalid",
20801     /**
20802      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20803      */
20804     invalidText : "The value in this field is invalid",
20805     /**
20806      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20807      */
20808     focusClass : "x-form-focus",
20809     /**
20810      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20811       automatic validation (defaults to "keyup").
20812      */
20813     validationEvent : "keyup",
20814     /**
20815      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20816      */
20817     validateOnBlur : true,
20818     /**
20819      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20820      */
20821     validationDelay : 250,
20822     /**
20823      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20824      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20825      */
20826     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20827     /**
20828      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20829      */
20830     fieldClass : "x-form-field",
20831     /**
20832      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20833      *<pre>
20834 Value         Description
20835 -----------   ----------------------------------------------------------------------
20836 qtip          Display a quick tip when the user hovers over the field
20837 title         Display a default browser title attribute popup
20838 under         Add a block div beneath the field containing the error text
20839 side          Add an error icon to the right of the field with a popup on hover
20840 [element id]  Add the error text directly to the innerHTML of the specified element
20841 </pre>
20842      */
20843     msgTarget : 'qtip',
20844     /**
20845      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20846      */
20847     msgFx : 'normal',
20848
20849     /**
20850      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20851      */
20852     readOnly : false,
20853
20854     /**
20855      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20856      */
20857     disabled : false,
20858
20859     /**
20860      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20861      */
20862     inputType : undefined,
20863     
20864     /**
20865      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20866          */
20867         tabIndex : undefined,
20868         
20869     // private
20870     isFormField : true,
20871
20872     // private
20873     hasFocus : false,
20874     /**
20875      * @property {Roo.Element} fieldEl
20876      * Element Containing the rendered Field (with label etc.)
20877      */
20878     /**
20879      * @cfg {Mixed} value A value to initialize this field with.
20880      */
20881     value : undefined,
20882
20883     /**
20884      * @cfg {String} name The field's HTML name attribute.
20885      */
20886     /**
20887      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20888      */
20889
20890         // private ??
20891         initComponent : function(){
20892         Roo.form.Field.superclass.initComponent.call(this);
20893         this.addEvents({
20894             /**
20895              * @event focus
20896              * Fires when this field receives input focus.
20897              * @param {Roo.form.Field} this
20898              */
20899             focus : true,
20900             /**
20901              * @event blur
20902              * Fires when this field loses input focus.
20903              * @param {Roo.form.Field} this
20904              */
20905             blur : true,
20906             /**
20907              * @event specialkey
20908              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20909              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20910              * @param {Roo.form.Field} this
20911              * @param {Roo.EventObject} e The event object
20912              */
20913             specialkey : true,
20914             /**
20915              * @event change
20916              * Fires just before the field blurs if the field value has changed.
20917              * @param {Roo.form.Field} this
20918              * @param {Mixed} newValue The new value
20919              * @param {Mixed} oldValue The original value
20920              */
20921             change : true,
20922             /**
20923              * @event invalid
20924              * Fires after the field has been marked as invalid.
20925              * @param {Roo.form.Field} this
20926              * @param {String} msg The validation message
20927              */
20928             invalid : true,
20929             /**
20930              * @event valid
20931              * Fires after the field has been validated with no errors.
20932              * @param {Roo.form.Field} this
20933              */
20934             valid : true,
20935              /**
20936              * @event keyup
20937              * Fires after the key up
20938              * @param {Roo.form.Field} this
20939              * @param {Roo.EventObject}  e The event Object
20940              */
20941             keyup : true
20942         });
20943     },
20944
20945     /**
20946      * Returns the name attribute of the field if available
20947      * @return {String} name The field name
20948      */
20949     getName: function(){
20950          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20951     },
20952
20953     // private
20954     onRender : function(ct, position){
20955         Roo.form.Field.superclass.onRender.call(this, ct, position);
20956         if(!this.el){
20957             var cfg = this.getAutoCreate();
20958             if(!cfg.name){
20959                 cfg.name = this.name || this.id;
20960             }
20961             if(this.inputType){
20962                 cfg.type = this.inputType;
20963             }
20964             this.el = ct.createChild(cfg, position);
20965         }
20966         var type = this.el.dom.type;
20967         if(type){
20968             if(type == 'password'){
20969                 type = 'text';
20970             }
20971             this.el.addClass('x-form-'+type);
20972         }
20973         if(this.readOnly){
20974             this.el.dom.readOnly = true;
20975         }
20976         if(this.tabIndex !== undefined){
20977             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20978         }
20979
20980         this.el.addClass([this.fieldClass, this.cls]);
20981         this.initValue();
20982     },
20983
20984     /**
20985      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20986      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20987      * @return {Roo.form.Field} this
20988      */
20989     applyTo : function(target){
20990         this.allowDomMove = false;
20991         this.el = Roo.get(target);
20992         this.render(this.el.dom.parentNode);
20993         return this;
20994     },
20995
20996     // private
20997     initValue : function(){
20998         if(this.value !== undefined){
20999             this.setValue(this.value);
21000         }else if(this.el.dom.value.length > 0){
21001             this.setValue(this.el.dom.value);
21002         }
21003     },
21004
21005     /**
21006      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21007      */
21008     isDirty : function() {
21009         if(this.disabled) {
21010             return false;
21011         }
21012         return String(this.getValue()) !== String(this.originalValue);
21013     },
21014
21015     // private
21016     afterRender : function(){
21017         Roo.form.Field.superclass.afterRender.call(this);
21018         this.initEvents();
21019     },
21020
21021     // private
21022     fireKey : function(e){
21023         //Roo.log('field ' + e.getKey());
21024         if(e.isNavKeyPress()){
21025             this.fireEvent("specialkey", this, e);
21026         }
21027     },
21028
21029     /**
21030      * Resets the current field value to the originally loaded value and clears any validation messages
21031      */
21032     reset : function(){
21033         this.setValue(this.originalValue);
21034         this.clearInvalid();
21035     },
21036
21037     // private
21038     initEvents : function(){
21039         // safari killled keypress - so keydown is now used..
21040         this.el.on("keydown" , this.fireKey,  this);
21041         this.el.on("focus", this.onFocus,  this);
21042         this.el.on("blur", this.onBlur,  this);
21043         this.el.relayEvent('keyup', this);
21044
21045         // reference to original value for reset
21046         this.originalValue = this.getValue();
21047     },
21048
21049     // private
21050     onFocus : function(){
21051         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21052             this.el.addClass(this.focusClass);
21053         }
21054         if(!this.hasFocus){
21055             this.hasFocus = true;
21056             this.startValue = this.getValue();
21057             this.fireEvent("focus", this);
21058         }
21059     },
21060
21061     beforeBlur : Roo.emptyFn,
21062
21063     // private
21064     onBlur : function(){
21065         this.beforeBlur();
21066         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21067             this.el.removeClass(this.focusClass);
21068         }
21069         this.hasFocus = false;
21070         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21071             this.validate();
21072         }
21073         var v = this.getValue();
21074         if(String(v) !== String(this.startValue)){
21075             this.fireEvent('change', this, v, this.startValue);
21076         }
21077         this.fireEvent("blur", this);
21078     },
21079
21080     /**
21081      * Returns whether or not the field value is currently valid
21082      * @param {Boolean} preventMark True to disable marking the field invalid
21083      * @return {Boolean} True if the value is valid, else false
21084      */
21085     isValid : function(preventMark){
21086         if(this.disabled){
21087             return true;
21088         }
21089         var restore = this.preventMark;
21090         this.preventMark = preventMark === true;
21091         var v = this.validateValue(this.processValue(this.getRawValue()));
21092         this.preventMark = restore;
21093         return v;
21094     },
21095
21096     /**
21097      * Validates the field value
21098      * @return {Boolean} True if the value is valid, else false
21099      */
21100     validate : function(){
21101         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21102             this.clearInvalid();
21103             return true;
21104         }
21105         return false;
21106     },
21107
21108     processValue : function(value){
21109         return value;
21110     },
21111
21112     // private
21113     // Subclasses should provide the validation implementation by overriding this
21114     validateValue : function(value){
21115         return true;
21116     },
21117
21118     /**
21119      * Mark this field as invalid
21120      * @param {String} msg The validation message
21121      */
21122     markInvalid : function(msg){
21123         if(!this.rendered || this.preventMark){ // not rendered
21124             return;
21125         }
21126         this.el.addClass(this.invalidClass);
21127         msg = msg || this.invalidText;
21128         switch(this.msgTarget){
21129             case 'qtip':
21130                 this.el.dom.qtip = msg;
21131                 this.el.dom.qclass = 'x-form-invalid-tip';
21132                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21133                     Roo.QuickTips.enable();
21134                 }
21135                 break;
21136             case 'title':
21137                 this.el.dom.title = msg;
21138                 break;
21139             case 'under':
21140                 if(!this.errorEl){
21141                     var elp = this.el.findParent('.x-form-element', 5, true);
21142                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21143                     this.errorEl.setWidth(elp.getWidth(true)-20);
21144                 }
21145                 this.errorEl.update(msg);
21146                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21147                 break;
21148             case 'side':
21149                 if(!this.errorIcon){
21150                     var elp = this.el.findParent('.x-form-element', 5, true);
21151                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21152                 }
21153                 this.alignErrorIcon();
21154                 this.errorIcon.dom.qtip = msg;
21155                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21156                 this.errorIcon.show();
21157                 this.on('resize', this.alignErrorIcon, this);
21158                 break;
21159             default:
21160                 var t = Roo.getDom(this.msgTarget);
21161                 t.innerHTML = msg;
21162                 t.style.display = this.msgDisplay;
21163                 break;
21164         }
21165         this.fireEvent('invalid', this, msg);
21166     },
21167
21168     // private
21169     alignErrorIcon : function(){
21170         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21171     },
21172
21173     /**
21174      * Clear any invalid styles/messages for this field
21175      */
21176     clearInvalid : function(){
21177         if(!this.rendered || this.preventMark){ // not rendered
21178             return;
21179         }
21180         this.el.removeClass(this.invalidClass);
21181         switch(this.msgTarget){
21182             case 'qtip':
21183                 this.el.dom.qtip = '';
21184                 break;
21185             case 'title':
21186                 this.el.dom.title = '';
21187                 break;
21188             case 'under':
21189                 if(this.errorEl){
21190                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21191                 }
21192                 break;
21193             case 'side':
21194                 if(this.errorIcon){
21195                     this.errorIcon.dom.qtip = '';
21196                     this.errorIcon.hide();
21197                     this.un('resize', this.alignErrorIcon, this);
21198                 }
21199                 break;
21200             default:
21201                 var t = Roo.getDom(this.msgTarget);
21202                 t.innerHTML = '';
21203                 t.style.display = 'none';
21204                 break;
21205         }
21206         this.fireEvent('valid', this);
21207     },
21208
21209     /**
21210      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21211      * @return {Mixed} value The field value
21212      */
21213     getRawValue : function(){
21214         var v = this.el.getValue();
21215         if(v === this.emptyText){
21216             v = '';
21217         }
21218         return v;
21219     },
21220
21221     /**
21222      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21223      * @return {Mixed} value The field value
21224      */
21225     getValue : function(){
21226         var v = this.el.getValue();
21227         if(v === this.emptyText || v === undefined){
21228             v = '';
21229         }
21230         return v;
21231     },
21232
21233     /**
21234      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21235      * @param {Mixed} value The value to set
21236      */
21237     setRawValue : function(v){
21238         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21239     },
21240
21241     /**
21242      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21243      * @param {Mixed} value The value to set
21244      */
21245     setValue : function(v){
21246         this.value = v;
21247         if(this.rendered){
21248             this.el.dom.value = (v === null || v === undefined ? '' : v);
21249             this.validate();
21250         }
21251     },
21252
21253     adjustSize : function(w, h){
21254         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21255         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21256         return s;
21257     },
21258
21259     adjustWidth : function(tag, w){
21260         tag = tag.toLowerCase();
21261         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21262             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21263                 if(tag == 'input'){
21264                     return w + 2;
21265                 }
21266                 if(tag = 'textarea'){
21267                     return w-2;
21268                 }
21269             }else if(Roo.isOpera){
21270                 if(tag == 'input'){
21271                     return w + 2;
21272                 }
21273                 if(tag = 'textarea'){
21274                     return w-2;
21275                 }
21276             }
21277         }
21278         return w;
21279     }
21280 });
21281
21282
21283 // anything other than normal should be considered experimental
21284 Roo.form.Field.msgFx = {
21285     normal : {
21286         show: function(msgEl, f){
21287             msgEl.setDisplayed('block');
21288         },
21289
21290         hide : function(msgEl, f){
21291             msgEl.setDisplayed(false).update('');
21292         }
21293     },
21294
21295     slide : {
21296         show: function(msgEl, f){
21297             msgEl.slideIn('t', {stopFx:true});
21298         },
21299
21300         hide : function(msgEl, f){
21301             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21302         }
21303     },
21304
21305     slideRight : {
21306         show: function(msgEl, f){
21307             msgEl.fixDisplay();
21308             msgEl.alignTo(f.el, 'tl-tr');
21309             msgEl.slideIn('l', {stopFx:true});
21310         },
21311
21312         hide : function(msgEl, f){
21313             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21314         }
21315     }
21316 };/*
21317  * Based on:
21318  * Ext JS Library 1.1.1
21319  * Copyright(c) 2006-2007, Ext JS, LLC.
21320  *
21321  * Originally Released Under LGPL - original licence link has changed is not relivant.
21322  *
21323  * Fork - LGPL
21324  * <script type="text/javascript">
21325  */
21326  
21327
21328 /**
21329  * @class Roo.form.TextField
21330  * @extends Roo.form.Field
21331  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21332  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21333  * @constructor
21334  * Creates a new TextField
21335  * @param {Object} config Configuration options
21336  */
21337 Roo.form.TextField = function(config){
21338     Roo.form.TextField.superclass.constructor.call(this, config);
21339     this.addEvents({
21340         /**
21341          * @event autosize
21342          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21343          * according to the default logic, but this event provides a hook for the developer to apply additional
21344          * logic at runtime to resize the field if needed.
21345              * @param {Roo.form.Field} this This text field
21346              * @param {Number} width The new field width
21347              */
21348         autosize : true
21349     });
21350 };
21351
21352 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21353     /**
21354      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21355      */
21356     grow : false,
21357     /**
21358      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21359      */
21360     growMin : 30,
21361     /**
21362      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21363      */
21364     growMax : 800,
21365     /**
21366      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21367      */
21368     vtype : null,
21369     /**
21370      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21371      */
21372     maskRe : null,
21373     /**
21374      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21375      */
21376     disableKeyFilter : false,
21377     /**
21378      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21379      */
21380     allowBlank : true,
21381     /**
21382      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21383      */
21384     minLength : 0,
21385     /**
21386      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21387      */
21388     maxLength : Number.MAX_VALUE,
21389     /**
21390      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21391      */
21392     minLengthText : "The minimum length for this field is {0}",
21393     /**
21394      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21395      */
21396     maxLengthText : "The maximum length for this field is {0}",
21397     /**
21398      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21399      */
21400     selectOnFocus : false,
21401     /**
21402      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21403      */
21404     blankText : "This field is required",
21405     /**
21406      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21407      * If available, this function will be called only after the basic validators all return true, and will be passed the
21408      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21409      */
21410     validator : null,
21411     /**
21412      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21413      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21414      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21415      */
21416     regex : null,
21417     /**
21418      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21419      */
21420     regexText : "",
21421     /**
21422      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21423      */
21424     emptyText : null,
21425     /**
21426      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21427      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21428      */
21429     emptyClass : 'x-form-empty-field',
21430
21431     // private
21432     initEvents : function(){
21433         Roo.form.TextField.superclass.initEvents.call(this);
21434         if(this.validationEvent == 'keyup'){
21435             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21436             this.el.on('keyup', this.filterValidation, this);
21437         }
21438         else if(this.validationEvent !== false){
21439             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21440         }
21441         if(this.selectOnFocus || this.emptyText){
21442             this.on("focus", this.preFocus, this);
21443             if(this.emptyText){
21444                 this.on('blur', this.postBlur, this);
21445                 this.applyEmptyText();
21446             }
21447         }
21448         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21449             this.el.on("keypress", this.filterKeys, this);
21450         }
21451         if(this.grow){
21452             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21453             this.el.on("click", this.autoSize,  this);
21454         }
21455     },
21456
21457     processValue : function(value){
21458         if(this.stripCharsRe){
21459             var newValue = value.replace(this.stripCharsRe, '');
21460             if(newValue !== value){
21461                 this.setRawValue(newValue);
21462                 return newValue;
21463             }
21464         }
21465         return value;
21466     },
21467
21468     filterValidation : function(e){
21469         if(!e.isNavKeyPress()){
21470             this.validationTask.delay(this.validationDelay);
21471         }
21472     },
21473
21474     // private
21475     onKeyUp : function(e){
21476         if(!e.isNavKeyPress()){
21477             this.autoSize();
21478         }
21479     },
21480
21481     /**
21482      * Resets the current field value to the originally-loaded value and clears any validation messages.
21483      * Also adds emptyText and emptyClass if the original value was blank.
21484      */
21485     reset : function(){
21486         Roo.form.TextField.superclass.reset.call(this);
21487         this.applyEmptyText();
21488     },
21489
21490     applyEmptyText : function(){
21491         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21492             this.setRawValue(this.emptyText);
21493             this.el.addClass(this.emptyClass);
21494         }
21495     },
21496
21497     // private
21498     preFocus : function(){
21499         if(this.emptyText){
21500             if(this.el.dom.value == this.emptyText){
21501                 this.setRawValue('');
21502             }
21503             this.el.removeClass(this.emptyClass);
21504         }
21505         if(this.selectOnFocus){
21506             this.el.dom.select();
21507         }
21508     },
21509
21510     // private
21511     postBlur : function(){
21512         this.applyEmptyText();
21513     },
21514
21515     // private
21516     filterKeys : function(e){
21517         var k = e.getKey();
21518         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21519             return;
21520         }
21521         var c = e.getCharCode(), cc = String.fromCharCode(c);
21522         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21523             return;
21524         }
21525         if(!this.maskRe.test(cc)){
21526             e.stopEvent();
21527         }
21528     },
21529
21530     setValue : function(v){
21531         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21532             this.el.removeClass(this.emptyClass);
21533         }
21534         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21535         this.applyEmptyText();
21536         this.autoSize();
21537     },
21538
21539     /**
21540      * Validates a value according to the field's validation rules and marks the field as invalid
21541      * if the validation fails
21542      * @param {Mixed} value The value to validate
21543      * @return {Boolean} True if the value is valid, else false
21544      */
21545     validateValue : function(value){
21546         if(value.length < 1 || value === this.emptyText){ // if it's blank
21547              if(this.allowBlank){
21548                 this.clearInvalid();
21549                 return true;
21550              }else{
21551                 this.markInvalid(this.blankText);
21552                 return false;
21553              }
21554         }
21555         if(value.length < this.minLength){
21556             this.markInvalid(String.format(this.minLengthText, this.minLength));
21557             return false;
21558         }
21559         if(value.length > this.maxLength){
21560             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21561             return false;
21562         }
21563         if(this.vtype){
21564             var vt = Roo.form.VTypes;
21565             if(!vt[this.vtype](value, this)){
21566                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21567                 return false;
21568             }
21569         }
21570         if(typeof this.validator == "function"){
21571             var msg = this.validator(value);
21572             if(msg !== true){
21573                 this.markInvalid(msg);
21574                 return false;
21575             }
21576         }
21577         if(this.regex && !this.regex.test(value)){
21578             this.markInvalid(this.regexText);
21579             return false;
21580         }
21581         return true;
21582     },
21583
21584     /**
21585      * Selects text in this field
21586      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21587      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21588      */
21589     selectText : function(start, end){
21590         var v = this.getRawValue();
21591         if(v.length > 0){
21592             start = start === undefined ? 0 : start;
21593             end = end === undefined ? v.length : end;
21594             var d = this.el.dom;
21595             if(d.setSelectionRange){
21596                 d.setSelectionRange(start, end);
21597             }else if(d.createTextRange){
21598                 var range = d.createTextRange();
21599                 range.moveStart("character", start);
21600                 range.moveEnd("character", v.length-end);
21601                 range.select();
21602             }
21603         }
21604     },
21605
21606     /**
21607      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21608      * This only takes effect if grow = true, and fires the autosize event.
21609      */
21610     autoSize : function(){
21611         if(!this.grow || !this.rendered){
21612             return;
21613         }
21614         if(!this.metrics){
21615             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21616         }
21617         var el = this.el;
21618         var v = el.dom.value;
21619         var d = document.createElement('div');
21620         d.appendChild(document.createTextNode(v));
21621         v = d.innerHTML;
21622         d = null;
21623         v += "&#160;";
21624         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21625         this.el.setWidth(w);
21626         this.fireEvent("autosize", this, w);
21627     }
21628 });