roojs-ui-debug.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13
14 /*
15  * These classes are derivatives of the similarly named classes in the YUI Library.
16  * The original license:
17  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18  * Code licensed under the BSD License:
19  * http://developer.yahoo.net/yui/license.txt
20  */
21
22 (function() {
23
24 var Event=Roo.EventManager;
25 var Dom=Roo.lib.Dom;
26
27 /**
28  * @class Roo.dd.DragDrop
29  * Defines the interface and base operation of items that that can be
30  * dragged or can be drop targets.  It was designed to be extended, overriding
31  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
32  * Up to three html elements can be associated with a DragDrop instance:
33  * <ul>
34  * <li>linked element: the element that is passed into the constructor.
35  * This is the element which defines the boundaries for interaction with
36  * other DragDrop objects.</li>
37  * <li>handle element(s): The drag operation only occurs if the element that
38  * was clicked matches a handle element.  By default this is the linked
39  * element, but there are times that you will want only a portion of the
40  * linked element to initiate the drag operation, and the setHandleElId()
41  * method provides a way to define this.</li>
42  * <li>drag element: this represents the element that would be moved along
43  * with the cursor during a drag operation.  By default, this is the linked
44  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
45  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
46  * </li>
47  * </ul>
48  * This class should not be instantiated until the onload event to ensure that
49  * the associated elements are available.
50  * The following would define a DragDrop obj that would interact with any
51  * other DragDrop obj in the "group1" group:
52  * <pre>
53  *  dd = new Roo.dd.DragDrop("div1", "group1");
54  * </pre>
55  * Since none of the event handlers have been implemented, nothing would
56  * actually happen if you were to run the code above.  Normally you would
57  * override this class or one of the default implementations, but you can
58  * also override the methods you want on an instance of the class...
59  * <pre>
60  *  dd.onDragDrop = function(e, id) {
61  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
62  *  }
63  * </pre>
64  * @constructor
65  * @param {String} id of the element that is linked to this instance
66  * @param {String} sGroup the group of related DragDrop objects
67  * @param {object} config an object containing configurable attributes
68  *                Valid properties for DragDrop:
69  *                    padding, isTarget, maintainOffset, primaryButtonOnly
70  */
71 Roo.dd.DragDrop = function(id, sGroup, config) {
72     if (id) {
73         this.init(id, sGroup, config);
74     }
75     if (config.listeners || config.events) { 
76         Roo.BasicLayoutRegion.superclass.constructor.call(this,  { 
77             listeners : config.listeners || {}, 
78             events : config.events || {} 
79         });    
80     }
81 };
82
83 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
84
85     /**
86      * The id of the element associated with this object.  This is what we
87      * refer to as the "linked element" because the size and position of
88      * this element is used to determine when the drag and drop objects have
89      * interacted.
90      * @property id
91      * @type String
92      */
93     id: null,
94
95     /**
96      * Configuration attributes passed into the constructor
97      * @property config
98      * @type object
99      */
100     config: null,
101
102     /**
103      * The id of the element that will be dragged.  By default this is same
104      * as the linked element , but could be changed to another element. Ex:
105      * Roo.dd.DDProxy
106      * @property dragElId
107      * @type String
108      * @private
109      */
110     dragElId: null,
111
112     /**
113      * the id of the element that initiates the drag operation.  By default
114      * this is the linked element, but could be changed to be a child of this
115      * element.  This lets us do things like only starting the drag when the
116      * header element within the linked html element is clicked.
117      * @property handleElId
118      * @type String
119      * @private
120      */
121     handleElId: null,
122
123     /**
124      * An associative array of HTML tags that will be ignored if clicked.
125      * @property invalidHandleTypes
126      * @type {string: string}
127      */
128     invalidHandleTypes: null,
129
130     /**
131      * An associative array of ids for elements that will be ignored if clicked
132      * @property invalidHandleIds
133      * @type {string: string}
134      */
135     invalidHandleIds: null,
136
137     /**
138      * An indexted array of css class names for elements that will be ignored
139      * if clicked.
140      * @property invalidHandleClasses
141      * @type string[]
142      */
143     invalidHandleClasses: null,
144
145     /**
146      * The linked element's absolute X position at the time the drag was
147      * started
148      * @property startPageX
149      * @type int
150      * @private
151      */
152     startPageX: 0,
153
154     /**
155      * The linked element's absolute X position at the time the drag was
156      * started
157      * @property startPageY
158      * @type int
159      * @private
160      */
161     startPageY: 0,
162
163     /**
164      * The group defines a logical collection of DragDrop objects that are
165      * related.  Instances only get events when interacting with other
166      * DragDrop object in the same group.  This lets us define multiple
167      * groups using a single DragDrop subclass if we want.
168      * @property groups
169      * @type {string: string}
170      */
171     groups: null,
172
173     /**
174      * Individual drag/drop instances can be locked.  This will prevent
175      * onmousedown start drag.
176      * @property locked
177      * @type boolean
178      * @private
179      */
180     locked: false,
181
182     /**
183      * Lock this instance
184      * @method lock
185      */
186     lock: function() { this.locked = true; },
187
188     /**
189      * Unlock this instace
190      * @method unlock
191      */
192     unlock: function() { this.locked = false; },
193
194     /**
195      * By default, all insances can be a drop target.  This can be disabled by
196      * setting isTarget to false.
197      * @method isTarget
198      * @type boolean
199      */
200     isTarget: true,
201
202     /**
203      * The padding configured for this drag and drop object for calculating
204      * the drop zone intersection with this object.
205      * @method padding
206      * @type int[]
207      */
208     padding: null,
209
210     /**
211      * Cached reference to the linked element
212      * @property _domRef
213      * @private
214      */
215     _domRef: null,
216
217     /**
218      * Internal typeof flag
219      * @property __ygDragDrop
220      * @private
221      */
222     __ygDragDrop: true,
223
224     /**
225      * Set to true when horizontal contraints are applied
226      * @property constrainX
227      * @type boolean
228      * @private
229      */
230     constrainX: false,
231
232     /**
233      * Set to true when vertical contraints are applied
234      * @property constrainY
235      * @type boolean
236      * @private
237      */
238     constrainY: false,
239
240     /**
241      * The left constraint
242      * @property minX
243      * @type int
244      * @private
245      */
246     minX: 0,
247
248     /**
249      * The right constraint
250      * @property maxX
251      * @type int
252      * @private
253      */
254     maxX: 0,
255
256     /**
257      * The up constraint
258      * @property minY
259      * @type int
260      * @type int
261      * @private
262      */
263     minY: 0,
264
265     /**
266      * The down constraint
267      * @property maxY
268      * @type int
269      * @private
270      */
271     maxY: 0,
272
273     /**
274      * Maintain offsets when we resetconstraints.  Set to true when you want
275      * the position of the element relative to its parent to stay the same
276      * when the page changes
277      *
278      * @property maintainOffset
279      * @type boolean
280      */
281     maintainOffset: false,
282
283     /**
284      * Array of pixel locations the element will snap to if we specified a
285      * horizontal graduation/interval.  This array is generated automatically
286      * when you define a tick interval.
287      * @property xTicks
288      * @type int[]
289      */
290     xTicks: null,
291
292     /**
293      * Array of pixel locations the element will snap to if we specified a
294      * vertical graduation/interval.  This array is generated automatically
295      * when you define a tick interval.
296      * @property yTicks
297      * @type int[]
298      */
299     yTicks: null,
300
301     /**
302      * By default the drag and drop instance will only respond to the primary
303      * button click (left button for a right-handed mouse).  Set to true to
304      * allow drag and drop to start with any mouse click that is propogated
305      * by the browser
306      * @property primaryButtonOnly
307      * @type boolean
308      */
309     primaryButtonOnly: true,
310
311     /**
312      * The availabe property is false until the linked dom element is accessible.
313      * @property available
314      * @type boolean
315      */
316     available: false,
317
318     /**
319      * By default, drags can only be initiated if the mousedown occurs in the
320      * region the linked element is.  This is done in part to work around a
321      * bug in some browsers that mis-report the mousedown if the previous
322      * mouseup happened outside of the window.  This property is set to true
323      * if outer handles are defined.
324      *
325      * @property hasOuterHandles
326      * @type boolean
327      * @default false
328      */
329     hasOuterHandles: false,
330
331     /**
332      * Code that executes immediately before the startDrag event
333      * @method b4StartDrag
334      * @private
335      */
336     b4StartDrag: function(x, y) { },
337
338     /**
339      * Abstract method called after a drag/drop object is clicked
340      * and the drag or mousedown time thresholds have beeen met.
341      * @method startDrag
342      * @param {int} X click location
343      * @param {int} Y click location
344      */
345     startDrag: function(x, y) { /* override this */ },
346
347     /**
348      * Code that executes immediately before the onDrag event
349      * @method b4Drag
350      * @private
351      */
352     b4Drag: function(e) { },
353
354     /**
355      * Abstract method called during the onMouseMove event while dragging an
356      * object.
357      * @method onDrag
358      * @param {Event} e the mousemove event
359      */
360     onDrag: function(e) { /* override this */ },
361
362     /**
363      * Abstract method called when this element fist begins hovering over
364      * another DragDrop obj
365      * @method onDragEnter
366      * @param {Event} e the mousemove event
367      * @param {String|DragDrop[]} id In POINT mode, the element
368      * id this is hovering over.  In INTERSECT mode, an array of one or more
369      * dragdrop items being hovered over.
370      */
371     onDragEnter: function(e, id) { /* override this */ },
372
373     /**
374      * Code that executes immediately before the onDragOver event
375      * @method b4DragOver
376      * @private
377      */
378     b4DragOver: function(e) { },
379
380     /**
381      * Abstract method called when this element is hovering over another
382      * DragDrop obj
383      * @method onDragOver
384      * @param {Event} e the mousemove event
385      * @param {String|DragDrop[]} id In POINT mode, the element
386      * id this is hovering over.  In INTERSECT mode, an array of dd items
387      * being hovered over.
388      */
389     onDragOver: function(e, id) { /* override this */ },
390
391     /**
392      * Code that executes immediately before the onDragOut event
393      * @method b4DragOut
394      * @private
395      */
396     b4DragOut: function(e) { },
397
398     /**
399      * Abstract method called when we are no longer hovering over an element
400      * @method onDragOut
401      * @param {Event} e the mousemove event
402      * @param {String|DragDrop[]} id In POINT mode, the element
403      * id this was hovering over.  In INTERSECT mode, an array of dd items
404      * that the mouse is no longer over.
405      */
406     onDragOut: function(e, id) { /* override this */ },
407
408     /**
409      * Code that executes immediately before the onDragDrop event
410      * @method b4DragDrop
411      * @private
412      */
413     b4DragDrop: function(e) { },
414
415     /**
416      * Abstract method called when this item is dropped on another DragDrop
417      * obj
418      * @method onDragDrop
419      * @param {Event} e the mouseup event
420      * @param {String|DragDrop[]} id In POINT mode, the element
421      * id this was dropped on.  In INTERSECT mode, an array of dd items this
422      * was dropped on.
423      */
424     onDragDrop: function(e, id) { /* override this */ },
425
426     /**
427      * Abstract method called when this item is dropped on an area with no
428      * drop target
429      * @method onInvalidDrop
430      * @param {Event} e the mouseup event
431      */
432     onInvalidDrop: function(e) { /* override this */ },
433
434     /**
435      * Code that executes immediately before the endDrag event
436      * @method b4EndDrag
437      * @private
438      */
439     b4EndDrag: function(e) { },
440
441     /**
442      * Fired when we are done dragging the object
443      * @method endDrag
444      * @param {Event} e the mouseup event
445      */
446     endDrag: function(e) { /* override this */ },
447
448     /**
449      * Code executed immediately before the onMouseDown event
450      * @method b4MouseDown
451      * @param {Event} e the mousedown event
452      * @private
453      */
454     b4MouseDown: function(e) {  },
455
456     /**
457      * Event handler that fires when a drag/drop obj gets a mousedown
458      * @method onMouseDown
459      * @param {Event} e the mousedown event
460      */
461     onMouseDown: function(e) { /* override this */ },
462
463     /**
464      * Event handler that fires when a drag/drop obj gets a mouseup
465      * @method onMouseUp
466      * @param {Event} e the mouseup event
467      */
468     onMouseUp: function(e) { /* override this */ },
469
470     /**
471      * Override the onAvailable method to do what is needed after the initial
472      * position was determined.
473      * @method onAvailable
474      */
475     onAvailable: function () {
476     },
477
478     /*
479      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
480      * @type Object
481      */
482     defaultPadding : {left:0, right:0, top:0, bottom:0},
483
484     /*
485      * Initializes the drag drop object's constraints to restrict movement to a certain element.
486  *
487  * Usage:
488  <pre><code>
489  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
490                 { dragElId: "existingProxyDiv" });
491  dd.startDrag = function(){
492      this.constrainTo("parent-id");
493  };
494  </code></pre>
495  * Or you can initalize it using the {@link Roo.Element} object:
496  <pre><code>
497  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
498      startDrag : function(){
499          this.constrainTo("parent-id");
500      }
501  });
502  </code></pre>
503      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
504      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
505      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
506      * an object containing the sides to pad. For example: {right:10, bottom:10}
507      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
508      */
509     constrainTo : function(constrainTo, pad, inContent){
510         if(typeof pad == "number"){
511             pad = {left: pad, right:pad, top:pad, bottom:pad};
512         }
513         pad = pad || this.defaultPadding;
514         var b = Roo.get(this.getEl()).getBox();
515         var ce = Roo.get(constrainTo);
516         var s = ce.getScroll();
517         var c, cd = ce.dom;
518         if(cd == document.body){
519             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
520         }else{
521             xy = ce.getXY();
522             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
523         }
524
525
526         var topSpace = b.y - c.y;
527         var leftSpace = b.x - c.x;
528
529         this.resetConstraints();
530         this.setXConstraint(leftSpace - (pad.left||0), // left
531                 c.width - leftSpace - b.width - (pad.right||0) //right
532         );
533         this.setYConstraint(topSpace - (pad.top||0), //top
534                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
535         );
536     },
537
538     /**
539      * Returns a reference to the linked element
540      * @method getEl
541      * @return {HTMLElement} the html element
542      */
543     getEl: function() {
544         if (!this._domRef) {
545             this._domRef = Roo.getDom(this.id);
546         }
547
548         return this._domRef;
549     },
550
551     /**
552      * Returns a reference to the actual element to drag.  By default this is
553      * the same as the html element, but it can be assigned to another
554      * element. An example of this can be found in Roo.dd.DDProxy
555      * @method getDragEl
556      * @return {HTMLElement} the html element
557      */
558     getDragEl: function() {
559         return Roo.getDom(this.dragElId);
560     },
561
562     /**
563      * Sets up the DragDrop object.  Must be called in the constructor of any
564      * Roo.dd.DragDrop subclass
565      * @method init
566      * @param id the id of the linked element
567      * @param {String} sGroup the group of related items
568      * @param {object} config configuration attributes
569      */
570     init: function(id, sGroup, config) {
571         this.initTarget(id, sGroup, config);
572         Event.on(this.id, "mousedown", this.handleMouseDown, this);
573         // Event.on(this.id, "selectstart", Event.preventDefault);
574     },
575
576     /**
577      * Initializes Targeting functionality only... the object does not
578      * get a mousedown handler.
579      * @method initTarget
580      * @param id the id of the linked element
581      * @param {String} sGroup the group of related items
582      * @param {object} config configuration attributes
583      */
584     initTarget: function(id, sGroup, config) {
585
586         // configuration attributes
587         this.config = config || {};
588
589         // create a local reference to the drag and drop manager
590         this.DDM = Roo.dd.DDM;
591         // initialize the groups array
592         this.groups = {};
593
594         // assume that we have an element reference instead of an id if the
595         // parameter is not a string
596         if (typeof id !== "string") {
597             id = Roo.id(id);
598         }
599
600         // set the id
601         this.id = id;
602
603         // add to an interaction group
604         this.addToGroup((sGroup) ? sGroup : "default");
605
606         // We don't want to register this as the handle with the manager
607         // so we just set the id rather than calling the setter.
608         this.handleElId = id;
609
610         // the linked element is the element that gets dragged by default
611         this.setDragElId(id);
612
613         // by default, clicked anchors will not start drag operations.
614         this.invalidHandleTypes = { A: "A" };
615         this.invalidHandleIds = {};
616         this.invalidHandleClasses = [];
617
618         this.applyConfig();
619
620         this.handleOnAvailable();
621     },
622
623     /**
624      * Applies the configuration parameters that were passed into the constructor.
625      * This is supposed to happen at each level through the inheritance chain.  So
626      * a DDProxy implentation will execute apply config on DDProxy, DD, and
627      * DragDrop in order to get all of the parameters that are available in
628      * each object.
629      * @method applyConfig
630      */
631     applyConfig: function() {
632
633         // configurable properties:
634         //    padding, isTarget, maintainOffset, primaryButtonOnly
635         this.padding           = this.config.padding || [0, 0, 0, 0];
636         this.isTarget          = (this.config.isTarget !== false);
637         this.maintainOffset    = (this.config.maintainOffset);
638         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
639
640     },
641
642     /**
643      * Executed when the linked element is available
644      * @method handleOnAvailable
645      * @private
646      */
647     handleOnAvailable: function() {
648         this.available = true;
649         this.resetConstraints();
650         this.onAvailable();
651     },
652
653      /**
654      * Configures the padding for the target zone in px.  Effectively expands
655      * (or reduces) the virtual object size for targeting calculations.
656      * Supports css-style shorthand; if only one parameter is passed, all sides
657      * will have that padding, and if only two are passed, the top and bottom
658      * will have the first param, the left and right the second.
659      * @method setPadding
660      * @param {int} iTop    Top pad
661      * @param {int} iRight  Right pad
662      * @param {int} iBot    Bot pad
663      * @param {int} iLeft   Left pad
664      */
665     setPadding: function(iTop, iRight, iBot, iLeft) {
666         // this.padding = [iLeft, iRight, iTop, iBot];
667         if (!iRight && 0 !== iRight) {
668             this.padding = [iTop, iTop, iTop, iTop];
669         } else if (!iBot && 0 !== iBot) {
670             this.padding = [iTop, iRight, iTop, iRight];
671         } else {
672             this.padding = [iTop, iRight, iBot, iLeft];
673         }
674     },
675
676     /**
677      * Stores the initial placement of the linked element.
678      * @method setInitialPosition
679      * @param {int} diffX   the X offset, default 0
680      * @param {int} diffY   the Y offset, default 0
681      */
682     setInitPosition: function(diffX, diffY) {
683         var el = this.getEl();
684
685         if (!this.DDM.verifyEl(el)) {
686             return;
687         }
688
689         var dx = diffX || 0;
690         var dy = diffY || 0;
691
692         var p = Dom.getXY( el );
693
694         this.initPageX = p[0] - dx;
695         this.initPageY = p[1] - dy;
696
697         this.lastPageX = p[0];
698         this.lastPageY = p[1];
699
700
701         this.setStartPosition(p);
702     },
703
704     /**
705      * Sets the start position of the element.  This is set when the obj
706      * is initialized, the reset when a drag is started.
707      * @method setStartPosition
708      * @param pos current position (from previous lookup)
709      * @private
710      */
711     setStartPosition: function(pos) {
712         var p = pos || Dom.getXY( this.getEl() );
713         this.deltaSetXY = null;
714
715         this.startPageX = p[0];
716         this.startPageY = p[1];
717     },
718
719     /**
720      * Add this instance to a group of related drag/drop objects.  All
721      * instances belong to at least one group, and can belong to as many
722      * groups as needed.
723      * @method addToGroup
724      * @param sGroup {string} the name of the group
725      */
726     addToGroup: function(sGroup) {
727         this.groups[sGroup] = true;
728         this.DDM.regDragDrop(this, sGroup);
729     },
730
731     /**
732      * Remove's this instance from the supplied interaction group
733      * @method removeFromGroup
734      * @param {string}  sGroup  The group to drop
735      */
736     removeFromGroup: function(sGroup) {
737         if (this.groups[sGroup]) {
738             delete this.groups[sGroup];
739         }
740
741         this.DDM.removeDDFromGroup(this, sGroup);
742     },
743
744     /**
745      * Allows you to specify that an element other than the linked element
746      * will be moved with the cursor during a drag
747      * @method setDragElId
748      * @param id {string} the id of the element that will be used to initiate the drag
749      */
750     setDragElId: function(id) {
751         this.dragElId = id;
752     },
753
754     /**
755      * Allows you to specify a child of the linked element that should be
756      * used to initiate the drag operation.  An example of this would be if
757      * you have a content div with text and links.  Clicking anywhere in the
758      * content area would normally start the drag operation.  Use this method
759      * to specify that an element inside of the content div is the element
760      * that starts the drag operation.
761      * @method setHandleElId
762      * @param id {string} the id of the element that will be used to
763      * initiate the drag.
764      */
765     setHandleElId: function(id) {
766         if (typeof id !== "string") {
767             id = Roo.id(id);
768         }
769         this.handleElId = id;
770         this.DDM.regHandle(this.id, id);
771     },
772
773     /**
774      * Allows you to set an element outside of the linked element as a drag
775      * handle
776      * @method setOuterHandleElId
777      * @param id the id of the element that will be used to initiate the drag
778      */
779     setOuterHandleElId: function(id) {
780         if (typeof id !== "string") {
781             id = Roo.id(id);
782         }
783         Event.on(id, "mousedown",
784                 this.handleMouseDown, this);
785         this.setHandleElId(id);
786
787         this.hasOuterHandles = true;
788     },
789
790     /**
791      * Remove all drag and drop hooks for this element
792      * @method unreg
793      */
794     unreg: function() {
795         Event.un(this.id, "mousedown",
796                 this.handleMouseDown);
797         this._domRef = null;
798         this.DDM._remove(this);
799     },
800
801     destroy : function(){
802         this.unreg();
803     },
804
805     /**
806      * Returns true if this instance is locked, or the drag drop mgr is locked
807      * (meaning that all drag/drop is disabled on the page.)
808      * @method isLocked
809      * @return {boolean} true if this obj or all drag/drop is locked, else
810      * false
811      */
812     isLocked: function() {
813         return (this.DDM.isLocked() || this.locked);
814     },
815
816     /**
817      * Fired when this object is clicked
818      * @method handleMouseDown
819      * @param {Event} e
820      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
821      * @private
822      */
823     handleMouseDown: function(e, oDD){
824         if (this.primaryButtonOnly && e.button != 0) {
825             return;
826         }
827
828         if (this.isLocked()) {
829             return;
830         }
831
832         this.DDM.refreshCache(this.groups);
833
834         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
835         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
836         } else {
837             if (this.clickValidator(e)) {
838
839                 // set the initial element position
840                 this.setStartPosition();
841
842
843                 this.b4MouseDown(e);
844                 this.onMouseDown(e);
845
846                 this.DDM.handleMouseDown(e, this);
847
848                 this.DDM.stopEvent(e);
849             } else {
850
851
852             }
853         }
854     },
855
856     clickValidator: function(e) {
857         var target = e.getTarget();
858         return ( this.isValidHandleChild(target) &&
859                     (this.id == this.handleElId ||
860                         this.DDM.handleWasClicked(target, this.id)) );
861     },
862
863     /**
864      * Allows you to specify a tag name that should not start a drag operation
865      * when clicked.  This is designed to facilitate embedding links within a
866      * drag handle that do something other than start the drag.
867      * @method addInvalidHandleType
868      * @param {string} tagName the type of element to exclude
869      */
870     addInvalidHandleType: function(tagName) {
871         var type = tagName.toUpperCase();
872         this.invalidHandleTypes[type] = type;
873     },
874
875     /**
876      * Lets you to specify an element id for a child of a drag handle
877      * that should not initiate a drag
878      * @method addInvalidHandleId
879      * @param {string} id the element id of the element you wish to ignore
880      */
881     addInvalidHandleId: function(id) {
882         if (typeof id !== "string") {
883             id = Roo.id(id);
884         }
885         this.invalidHandleIds[id] = id;
886     },
887
888     /**
889      * Lets you specify a css class of elements that will not initiate a drag
890      * @method addInvalidHandleClass
891      * @param {string} cssClass the class of the elements you wish to ignore
892      */
893     addInvalidHandleClass: function(cssClass) {
894         this.invalidHandleClasses.push(cssClass);
895     },
896
897     /**
898      * Unsets an excluded tag name set by addInvalidHandleType
899      * @method removeInvalidHandleType
900      * @param {string} tagName the type of element to unexclude
901      */
902     removeInvalidHandleType: function(tagName) {
903         var type = tagName.toUpperCase();
904         // this.invalidHandleTypes[type] = null;
905         delete this.invalidHandleTypes[type];
906     },
907
908     /**
909      * Unsets an invalid handle id
910      * @method removeInvalidHandleId
911      * @param {string} id the id of the element to re-enable
912      */
913     removeInvalidHandleId: function(id) {
914         if (typeof id !== "string") {
915             id = Roo.id(id);
916         }
917         delete this.invalidHandleIds[id];
918     },
919
920     /**
921      * Unsets an invalid css class
922      * @method removeInvalidHandleClass
923      * @param {string} cssClass the class of the element(s) you wish to
924      * re-enable
925      */
926     removeInvalidHandleClass: function(cssClass) {
927         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
928             if (this.invalidHandleClasses[i] == cssClass) {
929                 delete this.invalidHandleClasses[i];
930             }
931         }
932     },
933
934     /**
935      * Checks the tag exclusion list to see if this click should be ignored
936      * @method isValidHandleChild
937      * @param {HTMLElement} node the HTMLElement to evaluate
938      * @return {boolean} true if this is a valid tag type, false if not
939      */
940     isValidHandleChild: function(node) {
941
942         var valid = true;
943         // var n = (node.nodeName == "#text") ? node.parentNode : node;
944         var nodeName;
945         try {
946             nodeName = node.nodeName.toUpperCase();
947         } catch(e) {
948             nodeName = node.nodeName;
949         }
950         valid = valid && !this.invalidHandleTypes[nodeName];
951         valid = valid && !this.invalidHandleIds[node.id];
952
953         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
954             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
955         }
956
957
958         return valid;
959
960     },
961
962     /**
963      * Create the array of horizontal tick marks if an interval was specified
964      * in setXConstraint().
965      * @method setXTicks
966      * @private
967      */
968     setXTicks: function(iStartX, iTickSize) {
969         this.xTicks = [];
970         this.xTickSize = iTickSize;
971
972         var tickMap = {};
973
974         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
975             if (!tickMap[i]) {
976                 this.xTicks[this.xTicks.length] = i;
977                 tickMap[i] = true;
978             }
979         }
980
981         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
982             if (!tickMap[i]) {
983                 this.xTicks[this.xTicks.length] = i;
984                 tickMap[i] = true;
985             }
986         }
987
988         this.xTicks.sort(this.DDM.numericSort) ;
989     },
990
991     /**
992      * Create the array of vertical tick marks if an interval was specified in
993      * setYConstraint().
994      * @method setYTicks
995      * @private
996      */
997     setYTicks: function(iStartY, iTickSize) {
998         this.yTicks = [];
999         this.yTickSize = iTickSize;
1000
1001         var tickMap = {};
1002
1003         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1004             if (!tickMap[i]) {
1005                 this.yTicks[this.yTicks.length] = i;
1006                 tickMap[i] = true;
1007             }
1008         }
1009
1010         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1011             if (!tickMap[i]) {
1012                 this.yTicks[this.yTicks.length] = i;
1013                 tickMap[i] = true;
1014             }
1015         }
1016
1017         this.yTicks.sort(this.DDM.numericSort) ;
1018     },
1019
1020     /**
1021      * By default, the element can be dragged any place on the screen.  Use
1022      * this method to limit the horizontal travel of the element.  Pass in
1023      * 0,0 for the parameters if you want to lock the drag to the y axis.
1024      * @method setXConstraint
1025      * @param {int} iLeft the number of pixels the element can move to the left
1026      * @param {int} iRight the number of pixels the element can move to the
1027      * right
1028      * @param {int} iTickSize optional parameter for specifying that the
1029      * element
1030      * should move iTickSize pixels at a time.
1031      */
1032     setXConstraint: function(iLeft, iRight, iTickSize) {
1033         this.leftConstraint = iLeft;
1034         this.rightConstraint = iRight;
1035
1036         this.minX = this.initPageX - iLeft;
1037         this.maxX = this.initPageX + iRight;
1038         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1039
1040         this.constrainX = true;
1041     },
1042
1043     /**
1044      * Clears any constraints applied to this instance.  Also clears ticks
1045      * since they can't exist independent of a constraint at this time.
1046      * @method clearConstraints
1047      */
1048     clearConstraints: function() {
1049         this.constrainX = false;
1050         this.constrainY = false;
1051         this.clearTicks();
1052     },
1053
1054     /**
1055      * Clears any tick interval defined for this instance
1056      * @method clearTicks
1057      */
1058     clearTicks: function() {
1059         this.xTicks = null;
1060         this.yTicks = null;
1061         this.xTickSize = 0;
1062         this.yTickSize = 0;
1063     },
1064
1065     /**
1066      * By default, the element can be dragged any place on the screen.  Set
1067      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1068      * parameters if you want to lock the drag to the x axis.
1069      * @method setYConstraint
1070      * @param {int} iUp the number of pixels the element can move up
1071      * @param {int} iDown the number of pixels the element can move down
1072      * @param {int} iTickSize optional parameter for specifying that the
1073      * element should move iTickSize pixels at a time.
1074      */
1075     setYConstraint: function(iUp, iDown, iTickSize) {
1076         this.topConstraint = iUp;
1077         this.bottomConstraint = iDown;
1078
1079         this.minY = this.initPageY - iUp;
1080         this.maxY = this.initPageY + iDown;
1081         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1082
1083         this.constrainY = true;
1084
1085     },
1086
1087     /**
1088      * resetConstraints must be called if you manually reposition a dd element.
1089      * @method resetConstraints
1090      * @param {boolean} maintainOffset
1091      */
1092     resetConstraints: function() {
1093
1094
1095         // Maintain offsets if necessary
1096         if (this.initPageX || this.initPageX === 0) {
1097             // figure out how much this thing has moved
1098             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1099             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1100
1101             this.setInitPosition(dx, dy);
1102
1103         // This is the first time we have detected the element's position
1104         } else {
1105             this.setInitPosition();
1106         }
1107
1108         if (this.constrainX) {
1109             this.setXConstraint( this.leftConstraint,
1110                                  this.rightConstraint,
1111                                  this.xTickSize        );
1112         }
1113
1114         if (this.constrainY) {
1115             this.setYConstraint( this.topConstraint,
1116                                  this.bottomConstraint,
1117                                  this.yTickSize         );
1118         }
1119     },
1120
1121     /**
1122      * Normally the drag element is moved pixel by pixel, but we can specify
1123      * that it move a number of pixels at a time.  This method resolves the
1124      * location when we have it set up like this.
1125      * @method getTick
1126      * @param {int} val where we want to place the object
1127      * @param {int[]} tickArray sorted array of valid points
1128      * @return {int} the closest tick
1129      * @private
1130      */
1131     getTick: function(val, tickArray) {
1132
1133         if (!tickArray) {
1134             // If tick interval is not defined, it is effectively 1 pixel,
1135             // so we return the value passed to us.
1136             return val;
1137         } else if (tickArray[0] >= val) {
1138             // The value is lower than the first tick, so we return the first
1139             // tick.
1140             return tickArray[0];
1141         } else {
1142             for (var i=0, len=tickArray.length; i<len; ++i) {
1143                 var next = i + 1;
1144                 if (tickArray[next] && tickArray[next] >= val) {
1145                     var diff1 = val - tickArray[i];
1146                     var diff2 = tickArray[next] - val;
1147                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1148                 }
1149             }
1150
1151             // The value is larger than the last tick, so we return the last
1152             // tick.
1153             return tickArray[tickArray.length - 1];
1154         }
1155     },
1156
1157     /**
1158      * toString method
1159      * @method toString
1160      * @return {string} string representation of the dd obj
1161      */
1162     toString: function() {
1163         return ("DragDrop " + this.id);
1164     }
1165
1166 });
1167
1168 })();
1169 /*
1170  * Based on:
1171  * Ext JS Library 1.1.1
1172  * Copyright(c) 2006-2007, Ext JS, LLC.
1173  *
1174  * Originally Released Under LGPL - original licence link has changed is not relivant.
1175  *
1176  * Fork - LGPL
1177  * <script type="text/javascript">
1178  */
1179
1180
1181 /**
1182  * The drag and drop utility provides a framework for building drag and drop
1183  * applications.  In addition to enabling drag and drop for specific elements,
1184  * the drag and drop elements are tracked by the manager class, and the
1185  * interactions between the various elements are tracked during the drag and
1186  * the implementing code is notified about these important moments.
1187  */
1188
1189 // Only load the library once.  Rewriting the manager class would orphan
1190 // existing drag and drop instances.
1191 if (!Roo.dd.DragDropMgr) {
1192
1193 /**
1194  * @class Roo.dd.DragDropMgr
1195  * DragDropMgr is a singleton that tracks the element interaction for
1196  * all DragDrop items in the window.  Generally, you will not call
1197  * this class directly, but it does have helper methods that could
1198  * be useful in your DragDrop implementations.
1199  * @singleton
1200  */
1201 Roo.dd.DragDropMgr = function() {
1202
1203     var Event = Roo.EventManager;
1204
1205     return {
1206
1207         /**
1208          * Two dimensional Array of registered DragDrop objects.  The first
1209          * dimension is the DragDrop item group, the second the DragDrop
1210          * object.
1211          * @property ids
1212          * @type {string: string}
1213          * @private
1214          * @static
1215          */
1216         ids: {},
1217
1218         /**
1219          * Array of element ids defined as drag handles.  Used to determine
1220          * if the element that generated the mousedown event is actually the
1221          * handle and not the html element itself.
1222          * @property handleIds
1223          * @type {string: string}
1224          * @private
1225          * @static
1226          */
1227         handleIds: {},
1228
1229         /**
1230          * the DragDrop object that is currently being dragged
1231          * @property dragCurrent
1232          * @type DragDrop
1233          * @private
1234          * @static
1235          **/
1236         dragCurrent: null,
1237
1238         /**
1239          * the DragDrop object(s) that are being hovered over
1240          * @property dragOvers
1241          * @type Array
1242          * @private
1243          * @static
1244          */
1245         dragOvers: {},
1246
1247         /**
1248          * the X distance between the cursor and the object being dragged
1249          * @property deltaX
1250          * @type int
1251          * @private
1252          * @static
1253          */
1254         deltaX: 0,
1255
1256         /**
1257          * the Y distance between the cursor and the object being dragged
1258          * @property deltaY
1259          * @type int
1260          * @private
1261          * @static
1262          */
1263         deltaY: 0,
1264
1265         /**
1266          * Flag to determine if we should prevent the default behavior of the
1267          * events we define. By default this is true, but this can be set to
1268          * false if you need the default behavior (not recommended)
1269          * @property preventDefault
1270          * @type boolean
1271          * @static
1272          */
1273         preventDefault: true,
1274
1275         /**
1276          * Flag to determine if we should stop the propagation of the events
1277          * we generate. This is true by default but you may want to set it to
1278          * false if the html element contains other features that require the
1279          * mouse click.
1280          * @property stopPropagation
1281          * @type boolean
1282          * @static
1283          */
1284         stopPropagation: true,
1285
1286         /**
1287          * Internal flag that is set to true when drag and drop has been
1288          * intialized
1289          * @property initialized
1290          * @private
1291          * @static
1292          */
1293         initalized: false,
1294
1295         /**
1296          * All drag and drop can be disabled.
1297          * @property locked
1298          * @private
1299          * @static
1300          */
1301         locked: false,
1302
1303         /**
1304          * Called the first time an element is registered.
1305          * @method init
1306          * @private
1307          * @static
1308          */
1309         init: function() {
1310             this.initialized = true;
1311         },
1312
1313         /**
1314          * In point mode, drag and drop interaction is defined by the
1315          * location of the cursor during the drag/drop
1316          * @property POINT
1317          * @type int
1318          * @static
1319          */
1320         POINT: 0,
1321
1322         /**
1323          * In intersect mode, drag and drop interactio nis defined by the
1324          * overlap of two or more drag and drop objects.
1325          * @property INTERSECT
1326          * @type int
1327          * @static
1328          */
1329         INTERSECT: 1,
1330
1331         /**
1332          * The current drag and drop mode.  Default: POINT
1333          * @property mode
1334          * @type int
1335          * @static
1336          */
1337         mode: 0,
1338
1339         /**
1340          * Runs method on all drag and drop objects
1341          * @method _execOnAll
1342          * @private
1343          * @static
1344          */
1345         _execOnAll: function(sMethod, args) {
1346             for (var i in this.ids) {
1347                 for (var j in this.ids[i]) {
1348                     var oDD = this.ids[i][j];
1349                     if (! this.isTypeOfDD(oDD)) {
1350                         continue;
1351                     }
1352                     oDD[sMethod].apply(oDD, args);
1353                 }
1354             }
1355         },
1356
1357         /**
1358          * Drag and drop initialization.  Sets up the global event handlers
1359          * @method _onLoad
1360          * @private
1361          * @static
1362          */
1363         _onLoad: function() {
1364
1365             this.init();
1366
1367
1368             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1369             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1370             Event.on(window,   "unload",    this._onUnload, this, true);
1371             Event.on(window,   "resize",    this._onResize, this, true);
1372             // Event.on(window,   "mouseout",    this._test);
1373
1374         },
1375
1376         /**
1377          * Reset constraints on all drag and drop objs
1378          * @method _onResize
1379          * @private
1380          * @static
1381          */
1382         _onResize: function(e) {
1383             this._execOnAll("resetConstraints", []);
1384         },
1385
1386         /**
1387          * Lock all drag and drop functionality
1388          * @method lock
1389          * @static
1390          */
1391         lock: function() { this.locked = true; },
1392
1393         /**
1394          * Unlock all drag and drop functionality
1395          * @method unlock
1396          * @static
1397          */
1398         unlock: function() { this.locked = false; },
1399
1400         /**
1401          * Is drag and drop locked?
1402          * @method isLocked
1403          * @return {boolean} True if drag and drop is locked, false otherwise.
1404          * @static
1405          */
1406         isLocked: function() { return this.locked; },
1407
1408         /**
1409          * Location cache that is set for all drag drop objects when a drag is
1410          * initiated, cleared when the drag is finished.
1411          * @property locationCache
1412          * @private
1413          * @static
1414          */
1415         locationCache: {},
1416
1417         /**
1418          * Set useCache to false if you want to force object the lookup of each
1419          * drag and drop linked element constantly during a drag.
1420          * @property useCache
1421          * @type boolean
1422          * @static
1423          */
1424         useCache: true,
1425
1426         /**
1427          * The number of pixels that the mouse needs to move after the
1428          * mousedown before the drag is initiated.  Default=3;
1429          * @property clickPixelThresh
1430          * @type int
1431          * @static
1432          */
1433         clickPixelThresh: 3,
1434
1435         /**
1436          * The number of milliseconds after the mousedown event to initiate the
1437          * drag if we don't get a mouseup event. Default=1000
1438          * @property clickTimeThresh
1439          * @type int
1440          * @static
1441          */
1442         clickTimeThresh: 350,
1443
1444         /**
1445          * Flag that indicates that either the drag pixel threshold or the
1446          * mousdown time threshold has been met
1447          * @property dragThreshMet
1448          * @type boolean
1449          * @private
1450          * @static
1451          */
1452         dragThreshMet: false,
1453
1454         /**
1455          * Timeout used for the click time threshold
1456          * @property clickTimeout
1457          * @type Object
1458          * @private
1459          * @static
1460          */
1461         clickTimeout: null,
1462
1463         /**
1464          * The X position of the mousedown event stored for later use when a
1465          * drag threshold is met.
1466          * @property startX
1467          * @type int
1468          * @private
1469          * @static
1470          */
1471         startX: 0,
1472
1473         /**
1474          * The Y position of the mousedown event stored for later use when a
1475          * drag threshold is met.
1476          * @property startY
1477          * @type int
1478          * @private
1479          * @static
1480          */
1481         startY: 0,
1482
1483         /**
1484          * Each DragDrop instance must be registered with the DragDropMgr.
1485          * This is executed in DragDrop.init()
1486          * @method regDragDrop
1487          * @param {DragDrop} oDD the DragDrop object to register
1488          * @param {String} sGroup the name of the group this element belongs to
1489          * @static
1490          */
1491         regDragDrop: function(oDD, sGroup) {
1492             if (!this.initialized) { this.init(); }
1493
1494             if (!this.ids[sGroup]) {
1495                 this.ids[sGroup] = {};
1496             }
1497             this.ids[sGroup][oDD.id] = oDD;
1498         },
1499
1500         /**
1501          * Removes the supplied dd instance from the supplied group. Executed
1502          * by DragDrop.removeFromGroup, so don't call this function directly.
1503          * @method removeDDFromGroup
1504          * @private
1505          * @static
1506          */
1507         removeDDFromGroup: function(oDD, sGroup) {
1508             if (!this.ids[sGroup]) {
1509                 this.ids[sGroup] = {};
1510             }
1511
1512             var obj = this.ids[sGroup];
1513             if (obj && obj[oDD.id]) {
1514                 delete obj[oDD.id];
1515             }
1516         },
1517
1518         /**
1519          * Unregisters a drag and drop item.  This is executed in
1520          * DragDrop.unreg, use that method instead of calling this directly.
1521          * @method _remove
1522          * @private
1523          * @static
1524          */
1525         _remove: function(oDD) {
1526             for (var g in oDD.groups) {
1527                 if (g && this.ids[g][oDD.id]) {
1528                     delete this.ids[g][oDD.id];
1529                 }
1530             }
1531             delete this.handleIds[oDD.id];
1532         },
1533
1534         /**
1535          * Each DragDrop handle element must be registered.  This is done
1536          * automatically when executing DragDrop.setHandleElId()
1537          * @method regHandle
1538          * @param {String} sDDId the DragDrop id this element is a handle for
1539          * @param {String} sHandleId the id of the element that is the drag
1540          * handle
1541          * @static
1542          */
1543         regHandle: function(sDDId, sHandleId) {
1544             if (!this.handleIds[sDDId]) {
1545                 this.handleIds[sDDId] = {};
1546             }
1547             this.handleIds[sDDId][sHandleId] = sHandleId;
1548         },
1549
1550         /**
1551          * Utility function to determine if a given element has been
1552          * registered as a drag drop item.
1553          * @method isDragDrop
1554          * @param {String} id the element id to check
1555          * @return {boolean} true if this element is a DragDrop item,
1556          * false otherwise
1557          * @static
1558          */
1559         isDragDrop: function(id) {
1560             return ( this.getDDById(id) ) ? true : false;
1561         },
1562
1563         /**
1564          * Returns the drag and drop instances that are in all groups the
1565          * passed in instance belongs to.
1566          * @method getRelated
1567          * @param {DragDrop} p_oDD the obj to get related data for
1568          * @param {boolean} bTargetsOnly if true, only return targetable objs
1569          * @return {DragDrop[]} the related instances
1570          * @static
1571          */
1572         getRelated: function(p_oDD, bTargetsOnly) {
1573             var oDDs = [];
1574             for (var i in p_oDD.groups) {
1575                 for (j in this.ids[i]) {
1576                     var dd = this.ids[i][j];
1577                     if (! this.isTypeOfDD(dd)) {
1578                         continue;
1579                     }
1580                     if (!bTargetsOnly || dd.isTarget) {
1581                         oDDs[oDDs.length] = dd;
1582                     }
1583                 }
1584             }
1585
1586             return oDDs;
1587         },
1588
1589         /**
1590          * Returns true if the specified dd target is a legal target for
1591          * the specifice drag obj
1592          * @method isLegalTarget
1593          * @param {DragDrop} the drag obj
1594          * @param {DragDrop} the target
1595          * @return {boolean} true if the target is a legal target for the
1596          * dd obj
1597          * @static
1598          */
1599         isLegalTarget: function (oDD, oTargetDD) {
1600             var targets = this.getRelated(oDD, true);
1601             for (var i=0, len=targets.length;i<len;++i) {
1602                 if (targets[i].id == oTargetDD.id) {
1603                     return true;
1604                 }
1605             }
1606
1607             return false;
1608         },
1609
1610         /**
1611          * My goal is to be able to transparently determine if an object is
1612          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1613          * returns "object", oDD.constructor.toString() always returns
1614          * "DragDrop" and not the name of the subclass.  So for now it just
1615          * evaluates a well-known variable in DragDrop.
1616          * @method isTypeOfDD
1617          * @param {Object} the object to evaluate
1618          * @return {boolean} true if typeof oDD = DragDrop
1619          * @static
1620          */
1621         isTypeOfDD: function (oDD) {
1622             return (oDD && oDD.__ygDragDrop);
1623         },
1624
1625         /**
1626          * Utility function to determine if a given element has been
1627          * registered as a drag drop handle for the given Drag Drop object.
1628          * @method isHandle
1629          * @param {String} id the element id to check
1630          * @return {boolean} true if this element is a DragDrop handle, false
1631          * otherwise
1632          * @static
1633          */
1634         isHandle: function(sDDId, sHandleId) {
1635             return ( this.handleIds[sDDId] &&
1636                             this.handleIds[sDDId][sHandleId] );
1637         },
1638
1639         /**
1640          * Returns the DragDrop instance for a given id
1641          * @method getDDById
1642          * @param {String} id the id of the DragDrop object
1643          * @return {DragDrop} the drag drop object, null if it is not found
1644          * @static
1645          */
1646         getDDById: function(id) {
1647             for (var i in this.ids) {
1648                 if (this.ids[i][id]) {
1649                     return this.ids[i][id];
1650                 }
1651             }
1652             return null;
1653         },
1654
1655         /**
1656          * Fired after a registered DragDrop object gets the mousedown event.
1657          * Sets up the events required to track the object being dragged
1658          * @method handleMouseDown
1659          * @param {Event} e the event
1660          * @param oDD the DragDrop object being dragged
1661          * @private
1662          * @static
1663          */
1664         handleMouseDown: function(e, oDD) {
1665             if(Roo.QuickTips){
1666                 Roo.QuickTips.disable();
1667             }
1668             this.currentTarget = e.getTarget();
1669
1670             this.dragCurrent = oDD;
1671
1672             var el = oDD.getEl();
1673
1674             // track start position
1675             this.startX = e.getPageX();
1676             this.startY = e.getPageY();
1677
1678             this.deltaX = this.startX - el.offsetLeft;
1679             this.deltaY = this.startY - el.offsetTop;
1680
1681             this.dragThreshMet = false;
1682
1683             this.clickTimeout = setTimeout(
1684                     function() {
1685                         var DDM = Roo.dd.DDM;
1686                         DDM.startDrag(DDM.startX, DDM.startY);
1687                     },
1688                     this.clickTimeThresh );
1689         },
1690
1691         /**
1692          * Fired when either the drag pixel threshol or the mousedown hold
1693          * time threshold has been met.
1694          * @method startDrag
1695          * @param x {int} the X position of the original mousedown
1696          * @param y {int} the Y position of the original mousedown
1697          * @static
1698          */
1699         startDrag: function(x, y) {
1700             clearTimeout(this.clickTimeout);
1701             if (this.dragCurrent) {
1702                 this.dragCurrent.b4StartDrag(x, y);
1703                 this.dragCurrent.startDrag(x, y);
1704             }
1705             this.dragThreshMet = true;
1706         },
1707
1708         /**
1709          * Internal function to handle the mouseup event.  Will be invoked
1710          * from the context of the document.
1711          * @method handleMouseUp
1712          * @param {Event} e the event
1713          * @private
1714          * @static
1715          */
1716         handleMouseUp: function(e) {
1717
1718             if(Roo.QuickTips){
1719                 Roo.QuickTips.enable();
1720             }
1721             if (! this.dragCurrent) {
1722                 return;
1723             }
1724
1725             clearTimeout(this.clickTimeout);
1726
1727             if (this.dragThreshMet) {
1728                 this.fireEvents(e, true);
1729             } else {
1730             }
1731
1732             this.stopDrag(e);
1733
1734             this.stopEvent(e);
1735         },
1736
1737         /**
1738          * Utility to stop event propagation and event default, if these
1739          * features are turned on.
1740          * @method stopEvent
1741          * @param {Event} e the event as returned by this.getEvent()
1742          * @static
1743          */
1744         stopEvent: function(e){
1745             if(this.stopPropagation) {
1746                 e.stopPropagation();
1747             }
1748
1749             if (this.preventDefault) {
1750                 e.preventDefault();
1751             }
1752         },
1753
1754         /**
1755          * Internal function to clean up event handlers after the drag
1756          * operation is complete
1757          * @method stopDrag
1758          * @param {Event} e the event
1759          * @private
1760          * @static
1761          */
1762         stopDrag: function(e) {
1763             // Fire the drag end event for the item that was dragged
1764             if (this.dragCurrent) {
1765                 if (this.dragThreshMet) {
1766                     this.dragCurrent.b4EndDrag(e);
1767                     this.dragCurrent.endDrag(e);
1768                 }
1769
1770                 this.dragCurrent.onMouseUp(e);
1771             }
1772
1773             this.dragCurrent = null;
1774             this.dragOvers = {};
1775         },
1776
1777         /**
1778          * Internal function to handle the mousemove event.  Will be invoked
1779          * from the context of the html element.
1780          *
1781          * @TODO figure out what we can do about mouse events lost when the
1782          * user drags objects beyond the window boundary.  Currently we can
1783          * detect this in internet explorer by verifying that the mouse is
1784          * down during the mousemove event.  Firefox doesn't give us the
1785          * button state on the mousemove event.
1786          * @method handleMouseMove
1787          * @param {Event} e the event
1788          * @private
1789          * @static
1790          */
1791         handleMouseMove: function(e) {
1792             if (! this.dragCurrent) {
1793                 return true;
1794             }
1795
1796             // var button = e.which || e.button;
1797
1798             // check for IE mouseup outside of page boundary
1799             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1800                 this.stopEvent(e);
1801                 return this.handleMouseUp(e);
1802             }
1803
1804             if (!this.dragThreshMet) {
1805                 var diffX = Math.abs(this.startX - e.getPageX());
1806                 var diffY = Math.abs(this.startY - e.getPageY());
1807                 if (diffX > this.clickPixelThresh ||
1808                             diffY > this.clickPixelThresh) {
1809                     this.startDrag(this.startX, this.startY);
1810                 }
1811             }
1812
1813             if (this.dragThreshMet) {
1814                 this.dragCurrent.b4Drag(e);
1815                 this.dragCurrent.onDrag(e);
1816                 if(!this.dragCurrent.moveOnly){
1817                     this.fireEvents(e, false);
1818                 }
1819             }
1820
1821             this.stopEvent(e);
1822
1823             return true;
1824         },
1825
1826         /**
1827          * Iterates over all of the DragDrop elements to find ones we are
1828          * hovering over or dropping on
1829          * @method fireEvents
1830          * @param {Event} e the event
1831          * @param {boolean} isDrop is this a drop op or a mouseover op?
1832          * @private
1833          * @static
1834          */
1835         fireEvents: function(e, isDrop) {
1836             var dc = this.dragCurrent;
1837
1838             // If the user did the mouse up outside of the window, we could
1839             // get here even though we have ended the drag.
1840             if (!dc || dc.isLocked()) {
1841                 return;
1842             }
1843
1844             var pt = e.getPoint();
1845
1846             // cache the previous dragOver array
1847             var oldOvers = [];
1848
1849             var outEvts   = [];
1850             var overEvts  = [];
1851             var dropEvts  = [];
1852             var enterEvts = [];
1853
1854             // Check to see if the object(s) we were hovering over is no longer
1855             // being hovered over so we can fire the onDragOut event
1856             for (var i in this.dragOvers) {
1857
1858                 var ddo = this.dragOvers[i];
1859
1860                 if (! this.isTypeOfDD(ddo)) {
1861                     continue;
1862                 }
1863
1864                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1865                     outEvts.push( ddo );
1866                 }
1867
1868                 oldOvers[i] = true;
1869                 delete this.dragOvers[i];
1870             }
1871
1872             for (var sGroup in dc.groups) {
1873
1874                 if ("string" != typeof sGroup) {
1875                     continue;
1876                 }
1877
1878                 for (i in this.ids[sGroup]) {
1879                     var oDD = this.ids[sGroup][i];
1880                     if (! this.isTypeOfDD(oDD)) {
1881                         continue;
1882                     }
1883
1884                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1885                         if (this.isOverTarget(pt, oDD, this.mode)) {
1886                             // look for drop interactions
1887                             if (isDrop) {
1888                                 dropEvts.push( oDD );
1889                             // look for drag enter and drag over interactions
1890                             } else {
1891
1892                                 // initial drag over: dragEnter fires
1893                                 if (!oldOvers[oDD.id]) {
1894                                     enterEvts.push( oDD );
1895                                 // subsequent drag overs: dragOver fires
1896                                 } else {
1897                                     overEvts.push( oDD );
1898                                 }
1899
1900                                 this.dragOvers[oDD.id] = oDD;
1901                             }
1902                         }
1903                     }
1904                 }
1905             }
1906
1907             if (this.mode) {
1908                 if (outEvts.length) {
1909                     dc.b4DragOut(e, outEvts);
1910                     dc.onDragOut(e, outEvts);
1911                 }
1912
1913                 if (enterEvts.length) {
1914                     dc.onDragEnter(e, enterEvts);
1915                 }
1916
1917                 if (overEvts.length) {
1918                     dc.b4DragOver(e, overEvts);
1919                     dc.onDragOver(e, overEvts);
1920                 }
1921
1922                 if (dropEvts.length) {
1923                     dc.b4DragDrop(e, dropEvts);
1924                     dc.onDragDrop(e, dropEvts);
1925                 }
1926
1927             } else {
1928                 // fire dragout events
1929                 var len = 0;
1930                 for (i=0, len=outEvts.length; i<len; ++i) {
1931                     dc.b4DragOut(e, outEvts[i].id);
1932                     dc.onDragOut(e, outEvts[i].id);
1933                 }
1934
1935                 // fire enter events
1936                 for (i=0,len=enterEvts.length; i<len; ++i) {
1937                     // dc.b4DragEnter(e, oDD.id);
1938                     dc.onDragEnter(e, enterEvts[i].id);
1939                 }
1940
1941                 // fire over events
1942                 for (i=0,len=overEvts.length; i<len; ++i) {
1943                     dc.b4DragOver(e, overEvts[i].id);
1944                     dc.onDragOver(e, overEvts[i].id);
1945                 }
1946
1947                 // fire drop events
1948                 for (i=0, len=dropEvts.length; i<len; ++i) {
1949                     dc.b4DragDrop(e, dropEvts[i].id);
1950                     dc.onDragDrop(e, dropEvts[i].id);
1951                 }
1952
1953             }
1954
1955             // notify about a drop that did not find a target
1956             if (isDrop && !dropEvts.length) {
1957                 dc.onInvalidDrop(e);
1958             }
1959
1960         },
1961
1962         /**
1963          * Helper function for getting the best match from the list of drag
1964          * and drop objects returned by the drag and drop events when we are
1965          * in INTERSECT mode.  It returns either the first object that the
1966          * cursor is over, or the object that has the greatest overlap with
1967          * the dragged element.
1968          * @method getBestMatch
1969          * @param  {DragDrop[]} dds The array of drag and drop objects
1970          * targeted
1971          * @return {DragDrop}       The best single match
1972          * @static
1973          */
1974         getBestMatch: function(dds) {
1975             var winner = null;
1976             // Return null if the input is not what we expect
1977             //if (!dds || !dds.length || dds.length == 0) {
1978                // winner = null;
1979             // If there is only one item, it wins
1980             //} else if (dds.length == 1) {
1981
1982             var len = dds.length;
1983
1984             if (len == 1) {
1985                 winner = dds[0];
1986             } else {
1987                 // Loop through the targeted items
1988                 for (var i=0; i<len; ++i) {
1989                     var dd = dds[i];
1990                     // If the cursor is over the object, it wins.  If the
1991                     // cursor is over multiple matches, the first one we come
1992                     // to wins.
1993                     if (dd.cursorIsOver) {
1994                         winner = dd;
1995                         break;
1996                     // Otherwise the object with the most overlap wins
1997                     } else {
1998                         if (!winner ||
1999                             winner.overlap.getArea() < dd.overlap.getArea()) {
2000                             winner = dd;
2001                         }
2002                     }
2003                 }
2004             }
2005
2006             return winner;
2007         },
2008
2009         /**
2010          * Refreshes the cache of the top-left and bottom-right points of the
2011          * drag and drop objects in the specified group(s).  This is in the
2012          * format that is stored in the drag and drop instance, so typical
2013          * usage is:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2016          * </code>
2017          * Alternatively:
2018          * <code>
2019          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2020          * </code>
2021          * @TODO this really should be an indexed array.  Alternatively this
2022          * method could accept both.
2023          * @method refreshCache
2024          * @param {Object} groups an associative array of groups to refresh
2025          * @static
2026          */
2027         refreshCache: function(groups) {
2028             for (var sGroup in groups) {
2029                 if ("string" != typeof sGroup) {
2030                     continue;
2031                 }
2032                 for (var i in this.ids[sGroup]) {
2033                     var oDD = this.ids[sGroup][i];
2034
2035                     if (this.isTypeOfDD(oDD)) {
2036                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2037                         var loc = this.getLocation(oDD);
2038                         if (loc) {
2039                             this.locationCache[oDD.id] = loc;
2040                         } else {
2041                             delete this.locationCache[oDD.id];
2042                             // this will unregister the drag and drop object if
2043                             // the element is not in a usable state
2044                             // oDD.unreg();
2045                         }
2046                     }
2047                 }
2048             }
2049         },
2050
2051         /**
2052          * This checks to make sure an element exists and is in the DOM.  The
2053          * main purpose is to handle cases where innerHTML is used to remove
2054          * drag and drop objects from the DOM.  IE provides an 'unspecified
2055          * error' when trying to access the offsetParent of such an element
2056          * @method verifyEl
2057          * @param {HTMLElement} el the element to check
2058          * @return {boolean} true if the element looks usable
2059          * @static
2060          */
2061         verifyEl: function(el) {
2062             if (el) {
2063                 var parent;
2064                 if(Roo.isIE){
2065                     try{
2066                         parent = el.offsetParent;
2067                     }catch(e){}
2068                 }else{
2069                     parent = el.offsetParent;
2070                 }
2071                 if (parent) {
2072                     return true;
2073                 }
2074             }
2075
2076             return false;
2077         },
2078
2079         /**
2080          * Returns a Region object containing the drag and drop element's position
2081          * and size, including the padding configured for it
2082          * @method getLocation
2083          * @param {DragDrop} oDD the drag and drop object to get the
2084          *                       location for
2085          * @return {Roo.lib.Region} a Region object representing the total area
2086          *                             the element occupies, including any padding
2087          *                             the instance is configured for.
2088          * @static
2089          */
2090         getLocation: function(oDD) {
2091             if (! this.isTypeOfDD(oDD)) {
2092                 return null;
2093             }
2094
2095             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2096
2097             try {
2098                 pos= Roo.lib.Dom.getXY(el);
2099             } catch (e) { }
2100
2101             if (!pos) {
2102                 return null;
2103             }
2104
2105             x1 = pos[0];
2106             x2 = x1 + el.offsetWidth;
2107             y1 = pos[1];
2108             y2 = y1 + el.offsetHeight;
2109
2110             t = y1 - oDD.padding[0];
2111             r = x2 + oDD.padding[1];
2112             b = y2 + oDD.padding[2];
2113             l = x1 - oDD.padding[3];
2114
2115             return new Roo.lib.Region( t, r, b, l );
2116         },
2117
2118         /**
2119          * Checks the cursor location to see if it over the target
2120          * @method isOverTarget
2121          * @param {Roo.lib.Point} pt The point to evaluate
2122          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2123          * @return {boolean} true if the mouse is over the target
2124          * @private
2125          * @static
2126          */
2127         isOverTarget: function(pt, oTarget, intersect) {
2128             // use cache if available
2129             var loc = this.locationCache[oTarget.id];
2130             if (!loc || !this.useCache) {
2131                 loc = this.getLocation(oTarget);
2132                 this.locationCache[oTarget.id] = loc;
2133
2134             }
2135
2136             if (!loc) {
2137                 return false;
2138             }
2139
2140             oTarget.cursorIsOver = loc.contains( pt );
2141
2142             // DragDrop is using this as a sanity check for the initial mousedown
2143             // in this case we are done.  In POINT mode, if the drag obj has no
2144             // contraints, we are also done. Otherwise we need to evaluate the
2145             // location of the target as related to the actual location of the
2146             // dragged element.
2147             var dc = this.dragCurrent;
2148             if (!dc || !dc.getTargetCoord ||
2149                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2150                 return oTarget.cursorIsOver;
2151             }
2152
2153             oTarget.overlap = null;
2154
2155             // Get the current location of the drag element, this is the
2156             // location of the mouse event less the delta that represents
2157             // where the original mousedown happened on the element.  We
2158             // need to consider constraints and ticks as well.
2159             var pos = dc.getTargetCoord(pt.x, pt.y);
2160
2161             var el = dc.getDragEl();
2162             var curRegion = new Roo.lib.Region( pos.y,
2163                                                    pos.x + el.offsetWidth,
2164                                                    pos.y + el.offsetHeight,
2165                                                    pos.x );
2166
2167             var overlap = curRegion.intersect(loc);
2168
2169             if (overlap) {
2170                 oTarget.overlap = overlap;
2171                 return (intersect) ? true : oTarget.cursorIsOver;
2172             } else {
2173                 return false;
2174             }
2175         },
2176
2177         /**
2178          * unload event handler
2179          * @method _onUnload
2180          * @private
2181          * @static
2182          */
2183         _onUnload: function(e, me) {
2184             Roo.dd.DragDropMgr.unregAll();
2185         },
2186
2187         /**
2188          * Cleans up the drag and drop events and objects.
2189          * @method unregAll
2190          * @private
2191          * @static
2192          */
2193         unregAll: function() {
2194
2195             if (this.dragCurrent) {
2196                 this.stopDrag();
2197                 this.dragCurrent = null;
2198             }
2199
2200             this._execOnAll("unreg", []);
2201
2202             for (i in this.elementCache) {
2203                 delete this.elementCache[i];
2204             }
2205
2206             this.elementCache = {};
2207             this.ids = {};
2208         },
2209
2210         /**
2211          * A cache of DOM elements
2212          * @property elementCache
2213          * @private
2214          * @static
2215          */
2216         elementCache: {},
2217
2218         /**
2219          * Get the wrapper for the DOM element specified
2220          * @method getElWrapper
2221          * @param {String} id the id of the element to get
2222          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2223          * @private
2224          * @deprecated This wrapper isn't that useful
2225          * @static
2226          */
2227         getElWrapper: function(id) {
2228             var oWrapper = this.elementCache[id];
2229             if (!oWrapper || !oWrapper.el) {
2230                 oWrapper = this.elementCache[id] =
2231                     new this.ElementWrapper(Roo.getDom(id));
2232             }
2233             return oWrapper;
2234         },
2235
2236         /**
2237          * Returns the actual DOM element
2238          * @method getElement
2239          * @param {String} id the id of the elment to get
2240          * @return {Object} The element
2241          * @deprecated use Roo.getDom instead
2242          * @static
2243          */
2244         getElement: function(id) {
2245             return Roo.getDom(id);
2246         },
2247
2248         /**
2249          * Returns the style property for the DOM element (i.e.,
2250          * document.getElById(id).style)
2251          * @method getCss
2252          * @param {String} id the id of the elment to get
2253          * @return {Object} The style property of the element
2254          * @deprecated use Roo.getDom instead
2255          * @static
2256          */
2257         getCss: function(id) {
2258             var el = Roo.getDom(id);
2259             return (el) ? el.style : null;
2260         },
2261
2262         /**
2263          * Inner class for cached elements
2264          * @class DragDropMgr.ElementWrapper
2265          * @for DragDropMgr
2266          * @private
2267          * @deprecated
2268          */
2269         ElementWrapper: function(el) {
2270                 /**
2271                  * The element
2272                  * @property el
2273                  */
2274                 this.el = el || null;
2275                 /**
2276                  * The element id
2277                  * @property id
2278                  */
2279                 this.id = this.el && el.id;
2280                 /**
2281                  * A reference to the style property
2282                  * @property css
2283                  */
2284                 this.css = this.el && el.style;
2285             },
2286
2287         /**
2288          * Returns the X position of an html element
2289          * @method getPosX
2290          * @param el the element for which to get the position
2291          * @return {int} the X coordinate
2292          * @for DragDropMgr
2293          * @deprecated use Roo.lib.Dom.getX instead
2294          * @static
2295          */
2296         getPosX: function(el) {
2297             return Roo.lib.Dom.getX(el);
2298         },
2299
2300         /**
2301          * Returns the Y position of an html element
2302          * @method getPosY
2303          * @param el the element for which to get the position
2304          * @return {int} the Y coordinate
2305          * @deprecated use Roo.lib.Dom.getY instead
2306          * @static
2307          */
2308         getPosY: function(el) {
2309             return Roo.lib.Dom.getY(el);
2310         },
2311
2312         /**
2313          * Swap two nodes.  In IE, we use the native method, for others we
2314          * emulate the IE behavior
2315          * @method swapNode
2316          * @param n1 the first node to swap
2317          * @param n2 the other node to swap
2318          * @static
2319          */
2320         swapNode: function(n1, n2) {
2321             if (n1.swapNode) {
2322                 n1.swapNode(n2);
2323             } else {
2324                 var p = n2.parentNode;
2325                 var s = n2.nextSibling;
2326
2327                 if (s == n1) {
2328                     p.insertBefore(n1, n2);
2329                 } else if (n2 == n1.nextSibling) {
2330                     p.insertBefore(n2, n1);
2331                 } else {
2332                     n1.parentNode.replaceChild(n2, n1);
2333                     p.insertBefore(n1, s);
2334                 }
2335             }
2336         },
2337
2338         /**
2339          * Returns the current scroll position
2340          * @method getScroll
2341          * @private
2342          * @static
2343          */
2344         getScroll: function () {
2345             var t, l, dde=document.documentElement, db=document.body;
2346             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2347                 t = dde.scrollTop;
2348                 l = dde.scrollLeft;
2349             } else if (db) {
2350                 t = db.scrollTop;
2351                 l = db.scrollLeft;
2352             } else {
2353
2354             }
2355             return { top: t, left: l };
2356         },
2357
2358         /**
2359          * Returns the specified element style property
2360          * @method getStyle
2361          * @param {HTMLElement} el          the element
2362          * @param {string}      styleProp   the style property
2363          * @return {string} The value of the style property
2364          * @deprecated use Roo.lib.Dom.getStyle
2365          * @static
2366          */
2367         getStyle: function(el, styleProp) {
2368             return Roo.fly(el).getStyle(styleProp);
2369         },
2370
2371         /**
2372          * Gets the scrollTop
2373          * @method getScrollTop
2374          * @return {int} the document's scrollTop
2375          * @static
2376          */
2377         getScrollTop: function () { return this.getScroll().top; },
2378
2379         /**
2380          * Gets the scrollLeft
2381          * @method getScrollLeft
2382          * @return {int} the document's scrollTop
2383          * @static
2384          */
2385         getScrollLeft: function () { return this.getScroll().left; },
2386
2387         /**
2388          * Sets the x/y position of an element to the location of the
2389          * target element.
2390          * @method moveToEl
2391          * @param {HTMLElement} moveEl      The element to move
2392          * @param {HTMLElement} targetEl    The position reference element
2393          * @static
2394          */
2395         moveToEl: function (moveEl, targetEl) {
2396             var aCoord = Roo.lib.Dom.getXY(targetEl);
2397             Roo.lib.Dom.setXY(moveEl, aCoord);
2398         },
2399
2400         /**
2401          * Numeric array sort function
2402          * @method numericSort
2403          * @static
2404          */
2405         numericSort: function(a, b) { return (a - b); },
2406
2407         /**
2408          * Internal counter
2409          * @property _timeoutCount
2410          * @private
2411          * @static
2412          */
2413         _timeoutCount: 0,
2414
2415         /**
2416          * Trying to make the load order less important.  Without this we get
2417          * an error if this file is loaded before the Event Utility.
2418          * @method _addListeners
2419          * @private
2420          * @static
2421          */
2422         _addListeners: function() {
2423             var DDM = Roo.dd.DDM;
2424             if ( Roo.lib.Event && document ) {
2425                 DDM._onLoad();
2426             } else {
2427                 if (DDM._timeoutCount > 2000) {
2428                 } else {
2429                     setTimeout(DDM._addListeners, 10);
2430                     if (document && document.body) {
2431                         DDM._timeoutCount += 1;
2432                     }
2433                 }
2434             }
2435         },
2436
2437         /**
2438          * Recursively searches the immediate parent and all child nodes for
2439          * the handle element in order to determine wheter or not it was
2440          * clicked.
2441          * @method handleWasClicked
2442          * @param node the html element to inspect
2443          * @static
2444          */
2445         handleWasClicked: function(node, id) {
2446             if (this.isHandle(id, node.id)) {
2447                 return true;
2448             } else {
2449                 // check to see if this is a text node child of the one we want
2450                 var p = node.parentNode;
2451
2452                 while (p) {
2453                     if (this.isHandle(id, p.id)) {
2454                         return true;
2455                     } else {
2456                         p = p.parentNode;
2457                     }
2458                 }
2459             }
2460
2461             return false;
2462         }
2463
2464     };
2465
2466 }();
2467
2468 // shorter alias, save a few bytes
2469 Roo.dd.DDM = Roo.dd.DragDropMgr;
2470 Roo.dd.DDM._addListeners();
2471
2472 }/*
2473  * Based on:
2474  * Ext JS Library 1.1.1
2475  * Copyright(c) 2006-2007, Ext JS, LLC.
2476  *
2477  * Originally Released Under LGPL - original licence link has changed is not relivant.
2478  *
2479  * Fork - LGPL
2480  * <script type="text/javascript">
2481  */
2482
2483 /**
2484  * @class Roo.dd.DD
2485  * A DragDrop implementation where the linked element follows the
2486  * mouse cursor during a drag.
2487  * @extends Roo.dd.DragDrop
2488  * @constructor
2489  * @param {String} id the id of the linked element
2490  * @param {String} sGroup the group of related DragDrop items
2491  * @param {object} config an object containing configurable attributes
2492  *                Valid properties for DD:
2493  *                    scroll
2494  */
2495 Roo.dd.DD = function(id, sGroup, config) {
2496     if (id) {
2497         this.init(id, sGroup, config);
2498     }
2499 };
2500
2501 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2502
2503     /**
2504      * When set to true, the utility automatically tries to scroll the browser
2505      * window wehn a drag and drop element is dragged near the viewport boundary.
2506      * Defaults to true.
2507      * @property scroll
2508      * @type boolean
2509      */
2510     scroll: true,
2511
2512     /**
2513      * Sets the pointer offset to the distance between the linked element's top
2514      * left corner and the location the element was clicked
2515      * @method autoOffset
2516      * @param {int} iPageX the X coordinate of the click
2517      * @param {int} iPageY the Y coordinate of the click
2518      */
2519     autoOffset: function(iPageX, iPageY) {
2520         var x = iPageX - this.startPageX;
2521         var y = iPageY - this.startPageY;
2522         this.setDelta(x, y);
2523     },
2524
2525     /**
2526      * Sets the pointer offset.  You can call this directly to force the
2527      * offset to be in a particular location (e.g., pass in 0,0 to set it
2528      * to the center of the object)
2529      * @method setDelta
2530      * @param {int} iDeltaX the distance from the left
2531      * @param {int} iDeltaY the distance from the top
2532      */
2533     setDelta: function(iDeltaX, iDeltaY) {
2534         this.deltaX = iDeltaX;
2535         this.deltaY = iDeltaY;
2536     },
2537
2538     /**
2539      * Sets the drag element to the location of the mousedown or click event,
2540      * maintaining the cursor location relative to the location on the element
2541      * that was clicked.  Override this if you want to place the element in a
2542      * location other than where the cursor is.
2543      * @method setDragElPos
2544      * @param {int} iPageX the X coordinate of the mousedown or drag event
2545      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2546      */
2547     setDragElPos: function(iPageX, iPageY) {
2548         // the first time we do this, we are going to check to make sure
2549         // the element has css positioning
2550
2551         var el = this.getDragEl();
2552         this.alignElWithMouse(el, iPageX, iPageY);
2553     },
2554
2555     /**
2556      * Sets the element to the location of the mousedown or click event,
2557      * maintaining the cursor location relative to the location on the element
2558      * that was clicked.  Override this if you want to place the element in a
2559      * location other than where the cursor is.
2560      * @method alignElWithMouse
2561      * @param {HTMLElement} el the element to move
2562      * @param {int} iPageX the X coordinate of the mousedown or drag event
2563      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2564      */
2565     alignElWithMouse: function(el, iPageX, iPageY) {
2566         var oCoord = this.getTargetCoord(iPageX, iPageY);
2567         var fly = el.dom ? el : Roo.fly(el);
2568         if (!this.deltaSetXY) {
2569             var aCoord = [oCoord.x, oCoord.y];
2570             fly.setXY(aCoord);
2571             var newLeft = fly.getLeft(true);
2572             var newTop  = fly.getTop(true);
2573             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2574         } else {
2575             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2576         }
2577
2578         this.cachePosition(oCoord.x, oCoord.y);
2579         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2580         return oCoord;
2581     },
2582
2583     /**
2584      * Saves the most recent position so that we can reset the constraints and
2585      * tick marks on-demand.  We need to know this so that we can calculate the
2586      * number of pixels the element is offset from its original position.
2587      * @method cachePosition
2588      * @param iPageX the current x position (optional, this just makes it so we
2589      * don't have to look it up again)
2590      * @param iPageY the current y position (optional, this just makes it so we
2591      * don't have to look it up again)
2592      */
2593     cachePosition: function(iPageX, iPageY) {
2594         if (iPageX) {
2595             this.lastPageX = iPageX;
2596             this.lastPageY = iPageY;
2597         } else {
2598             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2599             this.lastPageX = aCoord[0];
2600             this.lastPageY = aCoord[1];
2601         }
2602     },
2603
2604     /**
2605      * Auto-scroll the window if the dragged object has been moved beyond the
2606      * visible window boundary.
2607      * @method autoScroll
2608      * @param {int} x the drag element's x position
2609      * @param {int} y the drag element's y position
2610      * @param {int} h the height of the drag element
2611      * @param {int} w the width of the drag element
2612      * @private
2613      */
2614     autoScroll: function(x, y, h, w) {
2615
2616         if (this.scroll) {
2617             // The client height
2618             var clientH = Roo.lib.Dom.getViewWidth();
2619
2620             // The client width
2621             var clientW = Roo.lib.Dom.getViewHeight();
2622
2623             // The amt scrolled down
2624             var st = this.DDM.getScrollTop();
2625
2626             // The amt scrolled right
2627             var sl = this.DDM.getScrollLeft();
2628
2629             // Location of the bottom of the element
2630             var bot = h + y;
2631
2632             // Location of the right of the element
2633             var right = w + x;
2634
2635             // The distance from the cursor to the bottom of the visible area,
2636             // adjusted so that we don't scroll if the cursor is beyond the
2637             // element drag constraints
2638             var toBot = (clientH + st - y - this.deltaY);
2639
2640             // The distance from the cursor to the right of the visible area
2641             var toRight = (clientW + sl - x - this.deltaX);
2642
2643
2644             // How close to the edge the cursor must be before we scroll
2645             // var thresh = (document.all) ? 100 : 40;
2646             var thresh = 40;
2647
2648             // How many pixels to scroll per autoscroll op.  This helps to reduce
2649             // clunky scrolling. IE is more sensitive about this ... it needs this
2650             // value to be higher.
2651             var scrAmt = (document.all) ? 80 : 30;
2652
2653             // Scroll down if we are near the bottom of the visible page and the
2654             // obj extends below the crease
2655             if ( bot > clientH && toBot < thresh ) {
2656                 window.scrollTo(sl, st + scrAmt);
2657             }
2658
2659             // Scroll up if the window is scrolled down and the top of the object
2660             // goes above the top border
2661             if ( y < st && st > 0 && y - st < thresh ) {
2662                 window.scrollTo(sl, st - scrAmt);
2663             }
2664
2665             // Scroll right if the obj is beyond the right border and the cursor is
2666             // near the border.
2667             if ( right > clientW && toRight < thresh ) {
2668                 window.scrollTo(sl + scrAmt, st);
2669             }
2670
2671             // Scroll left if the window has been scrolled to the right and the obj
2672             // extends past the left border
2673             if ( x < sl && sl > 0 && x - sl < thresh ) {
2674                 window.scrollTo(sl - scrAmt, st);
2675             }
2676         }
2677     },
2678
2679     /**
2680      * Finds the location the element should be placed if we want to move
2681      * it to where the mouse location less the click offset would place us.
2682      * @method getTargetCoord
2683      * @param {int} iPageX the X coordinate of the click
2684      * @param {int} iPageY the Y coordinate of the click
2685      * @return an object that contains the coordinates (Object.x and Object.y)
2686      * @private
2687      */
2688     getTargetCoord: function(iPageX, iPageY) {
2689
2690
2691         var x = iPageX - this.deltaX;
2692         var y = iPageY - this.deltaY;
2693
2694         if (this.constrainX) {
2695             if (x < this.minX) { x = this.minX; }
2696             if (x > this.maxX) { x = this.maxX; }
2697         }
2698
2699         if (this.constrainY) {
2700             if (y < this.minY) { y = this.minY; }
2701             if (y > this.maxY) { y = this.maxY; }
2702         }
2703
2704         x = this.getTick(x, this.xTicks);
2705         y = this.getTick(y, this.yTicks);
2706
2707
2708         return {x:x, y:y};
2709     },
2710
2711     /*
2712      * Sets up config options specific to this class. Overrides
2713      * Roo.dd.DragDrop, but all versions of this method through the
2714      * inheritance chain are called
2715      */
2716     applyConfig: function() {
2717         Roo.dd.DD.superclass.applyConfig.call(this);
2718         this.scroll = (this.config.scroll !== false);
2719     },
2720
2721     /*
2722      * Event that fires prior to the onMouseDown event.  Overrides
2723      * Roo.dd.DragDrop.
2724      */
2725     b4MouseDown: function(e) {
2726         // this.resetConstraints();
2727         this.autoOffset(e.getPageX(),
2728                             e.getPageY());
2729     },
2730
2731     /*
2732      * Event that fires prior to the onDrag event.  Overrides
2733      * Roo.dd.DragDrop.
2734      */
2735     b4Drag: function(e) {
2736         this.setDragElPos(e.getPageX(),
2737                             e.getPageY());
2738     },
2739
2740     toString: function() {
2741         return ("DD " + this.id);
2742     }
2743
2744     //////////////////////////////////////////////////////////////////////////
2745     // Debugging ygDragDrop events that can be overridden
2746     //////////////////////////////////////////////////////////////////////////
2747     /*
2748     startDrag: function(x, y) {
2749     },
2750
2751     onDrag: function(e) {
2752     },
2753
2754     onDragEnter: function(e, id) {
2755     },
2756
2757     onDragOver: function(e, id) {
2758     },
2759
2760     onDragOut: function(e, id) {
2761     },
2762
2763     onDragDrop: function(e, id) {
2764     },
2765
2766     endDrag: function(e) {
2767     }
2768
2769     */
2770
2771 });/*
2772  * Based on:
2773  * Ext JS Library 1.1.1
2774  * Copyright(c) 2006-2007, Ext JS, LLC.
2775  *
2776  * Originally Released Under LGPL - original licence link has changed is not relivant.
2777  *
2778  * Fork - LGPL
2779  * <script type="text/javascript">
2780  */
2781
2782 /**
2783  * @class Roo.dd.DDProxy
2784  * A DragDrop implementation that inserts an empty, bordered div into
2785  * the document that follows the cursor during drag operations.  At the time of
2786  * the click, the frame div is resized to the dimensions of the linked html
2787  * element, and moved to the exact location of the linked element.
2788  *
2789  * References to the "frame" element refer to the single proxy element that
2790  * was created to be dragged in place of all DDProxy elements on the
2791  * page.
2792  *
2793  * @extends Roo.dd.DD
2794  * @constructor
2795  * @param {String} id the id of the linked html element
2796  * @param {String} sGroup the group of related DragDrop objects
2797  * @param {object} config an object containing configurable attributes
2798  *                Valid properties for DDProxy in addition to those in DragDrop:
2799  *                   resizeFrame, centerFrame, dragElId
2800  */
2801 Roo.dd.DDProxy = function(id, sGroup, config) {
2802     if (id) {
2803         this.init(id, sGroup, config);
2804         this.initFrame();
2805     }
2806 };
2807
2808 /**
2809  * The default drag frame div id
2810  * @property Roo.dd.DDProxy.dragElId
2811  * @type String
2812  * @static
2813  */
2814 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2815
2816 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2817
2818     /**
2819      * By default we resize the drag frame to be the same size as the element
2820      * we want to drag (this is to get the frame effect).  We can turn it off
2821      * if we want a different behavior.
2822      * @property resizeFrame
2823      * @type boolean
2824      */
2825     resizeFrame: true,
2826
2827     /**
2828      * By default the frame is positioned exactly where the drag element is, so
2829      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2830      * you do not have constraints on the obj is to have the drag frame centered
2831      * around the cursor.  Set centerFrame to true for this effect.
2832      * @property centerFrame
2833      * @type boolean
2834      */
2835     centerFrame: false,
2836
2837     /**
2838      * Creates the proxy element if it does not yet exist
2839      * @method createFrame
2840      */
2841     createFrame: function() {
2842         var self = this;
2843         var body = document.body;
2844
2845         if (!body || !body.firstChild) {
2846             setTimeout( function() { self.createFrame(); }, 50 );
2847             return;
2848         }
2849
2850         var div = this.getDragEl();
2851
2852         if (!div) {
2853             div    = document.createElement("div");
2854             div.id = this.dragElId;
2855             var s  = div.style;
2856
2857             s.position   = "absolute";
2858             s.visibility = "hidden";
2859             s.cursor     = "move";
2860             s.border     = "2px solid #aaa";
2861             s.zIndex     = 999;
2862
2863             // appendChild can blow up IE if invoked prior to the window load event
2864             // while rendering a table.  It is possible there are other scenarios
2865             // that would cause this to happen as well.
2866             body.insertBefore(div, body.firstChild);
2867         }
2868     },
2869
2870     /**
2871      * Initialization for the drag frame element.  Must be called in the
2872      * constructor of all subclasses
2873      * @method initFrame
2874      */
2875     initFrame: function() {
2876         this.createFrame();
2877     },
2878
2879     applyConfig: function() {
2880         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2881
2882         this.resizeFrame = (this.config.resizeFrame !== false);
2883         this.centerFrame = (this.config.centerFrame);
2884         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2885     },
2886
2887     /**
2888      * Resizes the drag frame to the dimensions of the clicked object, positions
2889      * it over the object, and finally displays it
2890      * @method showFrame
2891      * @param {int} iPageX X click position
2892      * @param {int} iPageY Y click position
2893      * @private
2894      */
2895     showFrame: function(iPageX, iPageY) {
2896         var el = this.getEl();
2897         var dragEl = this.getDragEl();
2898         var s = dragEl.style;
2899
2900         this._resizeProxy();
2901
2902         if (this.centerFrame) {
2903             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2904                            Math.round(parseInt(s.height, 10)/2) );
2905         }
2906
2907         this.setDragElPos(iPageX, iPageY);
2908
2909         Roo.fly(dragEl).show();
2910     },
2911
2912     /**
2913      * The proxy is automatically resized to the dimensions of the linked
2914      * element when a drag is initiated, unless resizeFrame is set to false
2915      * @method _resizeProxy
2916      * @private
2917      */
2918     _resizeProxy: function() {
2919         if (this.resizeFrame) {
2920             var el = this.getEl();
2921             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2922         }
2923     },
2924
2925     // overrides Roo.dd.DragDrop
2926     b4MouseDown: function(e) {
2927         var x = e.getPageX();
2928         var y = e.getPageY();
2929         this.autoOffset(x, y);
2930         this.setDragElPos(x, y);
2931     },
2932
2933     // overrides Roo.dd.DragDrop
2934     b4StartDrag: function(x, y) {
2935         // show the drag frame
2936         this.showFrame(x, y);
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     b4EndDrag: function(e) {
2941         Roo.fly(this.getDragEl()).hide();
2942     },
2943
2944     // overrides Roo.dd.DragDrop
2945     // By default we try to move the element to the last location of the frame.
2946     // This is so that the default behavior mirrors that of Roo.dd.DD.
2947     endDrag: function(e) {
2948
2949         var lel = this.getEl();
2950         var del = this.getDragEl();
2951
2952         // Show the drag frame briefly so we can get its position
2953         del.style.visibility = "";
2954
2955         this.beforeMove();
2956         // Hide the linked element before the move to get around a Safari
2957         // rendering bug.
2958         lel.style.visibility = "hidden";
2959         Roo.dd.DDM.moveToEl(lel, del);
2960         del.style.visibility = "hidden";
2961         lel.style.visibility = "";
2962
2963         this.afterDrag();
2964     },
2965
2966     beforeMove : function(){
2967
2968     },
2969
2970     afterDrag : function(){
2971
2972     },
2973
2974     toString: function() {
2975         return ("DDProxy " + this.id);
2976     }
2977
2978 });
2979 /*
2980  * Based on:
2981  * Ext JS Library 1.1.1
2982  * Copyright(c) 2006-2007, Ext JS, LLC.
2983  *
2984  * Originally Released Under LGPL - original licence link has changed is not relivant.
2985  *
2986  * Fork - LGPL
2987  * <script type="text/javascript">
2988  */
2989
2990  /**
2991  * @class Roo.dd.DDTarget
2992  * A DragDrop implementation that does not move, but can be a drop
2993  * target.  You would get the same result by simply omitting implementation
2994  * for the event callbacks, but this way we reduce the processing cost of the
2995  * event listener and the callbacks.
2996  * @extends Roo.dd.DragDrop
2997  * @constructor
2998  * @param {String} id the id of the element that is a drop target
2999  * @param {String} sGroup the group of related DragDrop objects
3000  * @param {object} config an object containing configurable attributes
3001  *                 Valid properties for DDTarget in addition to those in
3002  *                 DragDrop:
3003  *                    none
3004  */
3005 Roo.dd.DDTarget = function(id, sGroup, config) {
3006     if (id) {
3007         this.initTarget(id, sGroup, config);
3008     }
3009 };
3010
3011 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3012 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3013     toString: function() {
3014         return ("DDTarget " + this.id);
3015     }
3016 });
3017 /*
3018  * Based on:
3019  * Ext JS Library 1.1.1
3020  * Copyright(c) 2006-2007, Ext JS, LLC.
3021  *
3022  * Originally Released Under LGPL - original licence link has changed is not relivant.
3023  *
3024  * Fork - LGPL
3025  * <script type="text/javascript">
3026  */
3027  
3028
3029 /**
3030  * @class Roo.dd.ScrollManager
3031  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3032  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3033  * @singleton
3034  */
3035 Roo.dd.ScrollManager = function(){
3036     var ddm = Roo.dd.DragDropMgr;
3037     var els = {};
3038     var dragEl = null;
3039     var proc = {};
3040     
3041     var onStop = function(e){
3042         dragEl = null;
3043         clearProc();
3044     };
3045     
3046     var triggerRefresh = function(){
3047         if(ddm.dragCurrent){
3048              ddm.refreshCache(ddm.dragCurrent.groups);
3049         }
3050     };
3051     
3052     var doScroll = function(){
3053         if(ddm.dragCurrent){
3054             var dds = Roo.dd.ScrollManager;
3055             if(!dds.animate){
3056                 if(proc.el.scroll(proc.dir, dds.increment)){
3057                     triggerRefresh();
3058                 }
3059             }else{
3060                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3061             }
3062         }
3063     };
3064     
3065     var clearProc = function(){
3066         if(proc.id){
3067             clearInterval(proc.id);
3068         }
3069         proc.id = 0;
3070         proc.el = null;
3071         proc.dir = "";
3072     };
3073     
3074     var startProc = function(el, dir){
3075         clearProc();
3076         proc.el = el;
3077         proc.dir = dir;
3078         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3079     };
3080     
3081     var onFire = function(e, isDrop){
3082         if(isDrop || !ddm.dragCurrent){ return; }
3083         var dds = Roo.dd.ScrollManager;
3084         if(!dragEl || dragEl != ddm.dragCurrent){
3085             dragEl = ddm.dragCurrent;
3086             // refresh regions on drag start
3087             dds.refreshCache();
3088         }
3089         
3090         var xy = Roo.lib.Event.getXY(e);
3091         var pt = new Roo.lib.Point(xy[0], xy[1]);
3092         for(var id in els){
3093             var el = els[id], r = el._region;
3094             if(r && r.contains(pt) && el.isScrollable()){
3095                 if(r.bottom - pt.y <= dds.thresh){
3096                     if(proc.el != el){
3097                         startProc(el, "down");
3098                     }
3099                     return;
3100                 }else if(r.right - pt.x <= dds.thresh){
3101                     if(proc.el != el){
3102                         startProc(el, "left");
3103                     }
3104                     return;
3105                 }else if(pt.y - r.top <= dds.thresh){
3106                     if(proc.el != el){
3107                         startProc(el, "up");
3108                     }
3109                     return;
3110                 }else if(pt.x - r.left <= dds.thresh){
3111                     if(proc.el != el){
3112                         startProc(el, "right");
3113                     }
3114                     return;
3115                 }
3116             }
3117         }
3118         clearProc();
3119     };
3120     
3121     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3122     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3123     
3124     return {
3125         /**
3126          * Registers new overflow element(s) to auto scroll
3127          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3128          */
3129         register : function(el){
3130             if(el instanceof Array){
3131                 for(var i = 0, len = el.length; i < len; i++) {
3132                         this.register(el[i]);
3133                 }
3134             }else{
3135                 el = Roo.get(el);
3136                 els[el.id] = el;
3137             }
3138         },
3139         
3140         /**
3141          * Unregisters overflow element(s) so they are no longer scrolled
3142          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3143          */
3144         unregister : function(el){
3145             if(el instanceof Array){
3146                 for(var i = 0, len = el.length; i < len; i++) {
3147                         this.unregister(el[i]);
3148                 }
3149             }else{
3150                 el = Roo.get(el);
3151                 delete els[el.id];
3152             }
3153         },
3154         
3155         /**
3156          * The number of pixels from the edge of a container the pointer needs to be to 
3157          * trigger scrolling (defaults to 25)
3158          * @type Number
3159          */
3160         thresh : 25,
3161         
3162         /**
3163          * The number of pixels to scroll in each scroll increment (defaults to 50)
3164          * @type Number
3165          */
3166         increment : 100,
3167         
3168         /**
3169          * The frequency of scrolls in milliseconds (defaults to 500)
3170          * @type Number
3171          */
3172         frequency : 500,
3173         
3174         /**
3175          * True to animate the scroll (defaults to true)
3176          * @type Boolean
3177          */
3178         animate: true,
3179         
3180         /**
3181          * The animation duration in seconds - 
3182          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3183          * @type Number
3184          */
3185         animDuration: .4,
3186         
3187         /**
3188          * Manually trigger a cache refresh.
3189          */
3190         refreshCache : function(){
3191             for(var id in els){
3192                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3193                     els[id]._region = els[id].getRegion();
3194                 }
3195             }
3196         }
3197     };
3198 }();/*
3199  * Based on:
3200  * Ext JS Library 1.1.1
3201  * Copyright(c) 2006-2007, Ext JS, LLC.
3202  *
3203  * Originally Released Under LGPL - original licence link has changed is not relivant.
3204  *
3205  * Fork - LGPL
3206  * <script type="text/javascript">
3207  */
3208  
3209
3210 /**
3211  * @class Roo.dd.Registry
3212  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3213  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3214  * @singleton
3215  */
3216 Roo.dd.Registry = function(){
3217     var elements = {}; 
3218     var handles = {}; 
3219     var autoIdSeed = 0;
3220
3221     var getId = function(el, autogen){
3222         if(typeof el == "string"){
3223             return el;
3224         }
3225         var id = el.id;
3226         if(!id && autogen !== false){
3227             id = "roodd-" + (++autoIdSeed);
3228             el.id = id;
3229         }
3230         return id;
3231     };
3232     
3233     return {
3234     /**
3235      * Register a drag drop element
3236      * @param {String|HTMLElement} element The id or DOM node to register
3237      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3238      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3239      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3240      * populated in the data object (if applicable):
3241      * <pre>
3242 Value      Description<br />
3243 ---------  ------------------------------------------<br />
3244 handles    Array of DOM nodes that trigger dragging<br />
3245            for the element being registered<br />
3246 isHandle   True if the element passed in triggers<br />
3247            dragging itself, else false
3248 </pre>
3249      */
3250         register : function(el, data){
3251             data = data || {};
3252             if(typeof el == "string"){
3253                 el = document.getElementById(el);
3254             }
3255             data.ddel = el;
3256             elements[getId(el)] = data;
3257             if(data.isHandle !== false){
3258                 handles[data.ddel.id] = data;
3259             }
3260             if(data.handles){
3261                 var hs = data.handles;
3262                 for(var i = 0, len = hs.length; i < len; i++){
3263                         handles[getId(hs[i])] = data;
3264                 }
3265             }
3266         },
3267
3268     /**
3269      * Unregister a drag drop element
3270      * @param {String|HTMLElement}  element The id or DOM node to unregister
3271      */
3272         unregister : function(el){
3273             var id = getId(el, false);
3274             var data = elements[id];
3275             if(data){
3276                 delete elements[id];
3277                 if(data.handles){
3278                     var hs = data.handles;
3279                     for(var i = 0, len = hs.length; i < len; i++){
3280                         delete handles[getId(hs[i], false)];
3281                     }
3282                 }
3283             }
3284         },
3285
3286     /**
3287      * Returns the handle registered for a DOM Node by id
3288      * @param {String|HTMLElement} id The DOM node or id to look up
3289      * @return {Object} handle The custom handle data
3290      */
3291         getHandle : function(id){
3292             if(typeof id != "string"){ // must be element?
3293                 id = id.id;
3294             }
3295             return handles[id];
3296         },
3297
3298     /**
3299      * Returns the handle that is registered for the DOM node that is the target of the event
3300      * @param {Event} e The event
3301      * @return {Object} handle The custom handle data
3302      */
3303         getHandleFromEvent : function(e){
3304             var t = Roo.lib.Event.getTarget(e);
3305             return t ? handles[t.id] : null;
3306         },
3307
3308     /**
3309      * Returns a custom data object that is registered for a DOM node by id
3310      * @param {String|HTMLElement} id The DOM node or id to look up
3311      * @return {Object} data The custom data
3312      */
3313         getTarget : function(id){
3314             if(typeof id != "string"){ // must be element?
3315                 id = id.id;
3316             }
3317             return elements[id];
3318         },
3319
3320     /**
3321      * Returns a custom data object that is registered for the DOM node that is the target of the event
3322      * @param {Event} e The event
3323      * @return {Object} data The custom data
3324      */
3325         getTargetFromEvent : function(e){
3326             var t = Roo.lib.Event.getTarget(e);
3327             return t ? elements[t.id] || handles[t.id] : null;
3328         }
3329     };
3330 }();/*
3331  * Based on:
3332  * Ext JS Library 1.1.1
3333  * Copyright(c) 2006-2007, Ext JS, LLC.
3334  *
3335  * Originally Released Under LGPL - original licence link has changed is not relivant.
3336  *
3337  * Fork - LGPL
3338  * <script type="text/javascript">
3339  */
3340  
3341
3342 /**
3343  * @class Roo.dd.StatusProxy
3344  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3345  * default drag proxy used by all Roo.dd components.
3346  * @constructor
3347  * @param {Object} config
3348  */
3349 Roo.dd.StatusProxy = function(config){
3350     Roo.apply(this, config);
3351     this.id = this.id || Roo.id();
3352     this.el = new Roo.Layer({
3353         dh: {
3354             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3355                 {tag: "div", cls: "x-dd-drop-icon"},
3356                 {tag: "div", cls: "x-dd-drag-ghost"}
3357             ]
3358         }, 
3359         shadow: !config || config.shadow !== false
3360     });
3361     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3362     this.dropStatus = this.dropNotAllowed;
3363 };
3364
3365 Roo.dd.StatusProxy.prototype = {
3366     /**
3367      * @cfg {String} dropAllowed
3368      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3369      */
3370     dropAllowed : "x-dd-drop-ok",
3371     /**
3372      * @cfg {String} dropNotAllowed
3373      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3374      */
3375     dropNotAllowed : "x-dd-drop-nodrop",
3376
3377     /**
3378      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3379      * over the current target element.
3380      * @param {String} cssClass The css class for the new drop status indicator image
3381      */
3382     setStatus : function(cssClass){
3383         cssClass = cssClass || this.dropNotAllowed;
3384         if(this.dropStatus != cssClass){
3385             this.el.replaceClass(this.dropStatus, cssClass);
3386             this.dropStatus = cssClass;
3387         }
3388     },
3389
3390     /**
3391      * Resets the status indicator to the default dropNotAllowed value
3392      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3393      */
3394     reset : function(clearGhost){
3395         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3396         this.dropStatus = this.dropNotAllowed;
3397         if(clearGhost){
3398             this.ghost.update("");
3399         }
3400     },
3401
3402     /**
3403      * Updates the contents of the ghost element
3404      * @param {String} html The html that will replace the current innerHTML of the ghost element
3405      */
3406     update : function(html){
3407         if(typeof html == "string"){
3408             this.ghost.update(html);
3409         }else{
3410             this.ghost.update("");
3411             html.style.margin = "0";
3412             this.ghost.dom.appendChild(html);
3413         }
3414         // ensure float = none set?? cant remember why though.
3415         var el = this.ghost.dom.firstChild;
3416                 if(el){
3417                         Roo.fly(el).setStyle('float', 'none');
3418                 }
3419     },
3420     
3421     /**
3422      * Returns the underlying proxy {@link Roo.Layer}
3423      * @return {Roo.Layer} el
3424     */
3425     getEl : function(){
3426         return this.el;
3427     },
3428
3429     /**
3430      * Returns the ghost element
3431      * @return {Roo.Element} el
3432      */
3433     getGhost : function(){
3434         return this.ghost;
3435     },
3436
3437     /**
3438      * Hides the proxy
3439      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3440      */
3441     hide : function(clear){
3442         this.el.hide();
3443         if(clear){
3444             this.reset(true);
3445         }
3446     },
3447
3448     /**
3449      * Stops the repair animation if it's currently running
3450      */
3451     stop : function(){
3452         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3453             this.anim.stop();
3454         }
3455     },
3456
3457     /**
3458      * Displays this proxy
3459      */
3460     show : function(){
3461         this.el.show();
3462     },
3463
3464     /**
3465      * Force the Layer to sync its shadow and shim positions to the element
3466      */
3467     sync : function(){
3468         this.el.sync();
3469     },
3470
3471     /**
3472      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3473      * invalid drop operation by the item being dragged.
3474      * @param {Array} xy The XY position of the element ([x, y])
3475      * @param {Function} callback The function to call after the repair is complete
3476      * @param {Object} scope The scope in which to execute the callback
3477      */
3478     repair : function(xy, callback, scope){
3479         this.callback = callback;
3480         this.scope = scope;
3481         if(xy && this.animRepair !== false){
3482             this.el.addClass("x-dd-drag-repair");
3483             this.el.hideUnders(true);
3484             this.anim = this.el.shift({
3485                 duration: this.repairDuration || .5,
3486                 easing: 'easeOut',
3487                 xy: xy,
3488                 stopFx: true,
3489                 callback: this.afterRepair,
3490                 scope: this
3491             });
3492         }else{
3493             this.afterRepair();
3494         }
3495     },
3496
3497     // private
3498     afterRepair : function(){
3499         this.hide(true);
3500         if(typeof this.callback == "function"){
3501             this.callback.call(this.scope || this);
3502         }
3503         this.callback = null;
3504         this.scope = null;
3505     }
3506 };/*
3507  * Based on:
3508  * Ext JS Library 1.1.1
3509  * Copyright(c) 2006-2007, Ext JS, LLC.
3510  *
3511  * Originally Released Under LGPL - original licence link has changed is not relivant.
3512  *
3513  * Fork - LGPL
3514  * <script type="text/javascript">
3515  */
3516
3517 /**
3518  * @class Roo.dd.DragSource
3519  * @extends Roo.dd.DDProxy
3520  * A simple class that provides the basic implementation needed to make any element draggable.
3521  * @constructor
3522  * @param {String/HTMLElement/Element} el The container element
3523  * @param {Object} config
3524  */
3525 Roo.dd.DragSource = function(el, config){
3526     this.el = Roo.get(el);
3527     this.dragData = {};
3528     
3529     Roo.apply(this, config);
3530     
3531     if(!this.proxy){
3532         this.proxy = new Roo.dd.StatusProxy();
3533     }
3534
3535     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3536           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3537     
3538     this.dragging = false;
3539 };
3540
3541 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3542     /**
3543      * @cfg {String} dropAllowed
3544      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3545      */
3546     dropAllowed : "x-dd-drop-ok",
3547     /**
3548      * @cfg {String} dropNotAllowed
3549      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3550      */
3551     dropNotAllowed : "x-dd-drop-nodrop",
3552
3553     /**
3554      * Returns the data object associated with this drag source
3555      * @return {Object} data An object containing arbitrary data
3556      */
3557     getDragData : function(e){
3558         return this.dragData;
3559     },
3560
3561     // private
3562     onDragEnter : function(e, id){
3563         var target = Roo.dd.DragDropMgr.getDDById(id);
3564         this.cachedTarget = target;
3565         if(this.beforeDragEnter(target, e, id) !== false){
3566             if(target.isNotifyTarget){
3567                 var status = target.notifyEnter(this, e, this.dragData);
3568                 this.proxy.setStatus(status);
3569             }else{
3570                 this.proxy.setStatus(this.dropAllowed);
3571             }
3572             
3573             if(this.afterDragEnter){
3574                 /**
3575                  * An empty function by default, but provided so that you can perform a custom action
3576                  * when the dragged item enters the drop target by providing an implementation.
3577                  * @param {Roo.dd.DragDrop} target The drop target
3578                  * @param {Event} e The event object
3579                  * @param {String} id The id of the dragged element
3580                  * @method afterDragEnter
3581                  */
3582                 this.afterDragEnter(target, e, id);
3583             }
3584         }
3585     },
3586
3587     /**
3588      * An empty function by default, but provided so that you can perform a custom action
3589      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3590      * @param {Roo.dd.DragDrop} target The drop target
3591      * @param {Event} e The event object
3592      * @param {String} id The id of the dragged element
3593      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3594      */
3595     beforeDragEnter : function(target, e, id){
3596         return true;
3597     },
3598
3599     // private
3600     alignElWithMouse: function() {
3601         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3602         this.proxy.sync();
3603     },
3604
3605     // private
3606     onDragOver : function(e, id){
3607         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3608         if(this.beforeDragOver(target, e, id) !== false){
3609             if(target.isNotifyTarget){
3610                 var status = target.notifyOver(this, e, this.dragData);
3611                 this.proxy.setStatus(status);
3612             }
3613
3614             if(this.afterDragOver){
3615                 /**
3616                  * An empty function by default, but provided so that you can perform a custom action
3617                  * while the dragged item is over the drop target by providing an implementation.
3618                  * @param {Roo.dd.DragDrop} target The drop target
3619                  * @param {Event} e The event object
3620                  * @param {String} id The id of the dragged element
3621                  * @method afterDragOver
3622                  */
3623                 this.afterDragOver(target, e, id);
3624             }
3625         }
3626     },
3627
3628     /**
3629      * An empty function by default, but provided so that you can perform a custom action
3630      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3631      * @param {Roo.dd.DragDrop} target The drop target
3632      * @param {Event} e The event object
3633      * @param {String} id The id of the dragged element
3634      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3635      */
3636     beforeDragOver : function(target, e, id){
3637         return true;
3638     },
3639
3640     // private
3641     onDragOut : function(e, id){
3642         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3643         if(this.beforeDragOut(target, e, id) !== false){
3644             if(target.isNotifyTarget){
3645                 target.notifyOut(this, e, this.dragData);
3646             }
3647             this.proxy.reset();
3648             if(this.afterDragOut){
3649                 /**
3650                  * An empty function by default, but provided so that you can perform a custom action
3651                  * after the dragged item is dragged out of the target without dropping.
3652                  * @param {Roo.dd.DragDrop} target The drop target
3653                  * @param {Event} e The event object
3654                  * @param {String} id The id of the dragged element
3655                  * @method afterDragOut
3656                  */
3657                 this.afterDragOut(target, e, id);
3658             }
3659         }
3660         this.cachedTarget = null;
3661     },
3662
3663     /**
3664      * An empty function by default, but provided so that you can perform a custom action before the dragged
3665      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3666      * @param {Roo.dd.DragDrop} target The drop target
3667      * @param {Event} e The event object
3668      * @param {String} id The id of the dragged element
3669      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3670      */
3671     beforeDragOut : function(target, e, id){
3672         return true;
3673     },
3674     
3675     // private
3676     onDragDrop : function(e, id){
3677         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3678         if(this.beforeDragDrop(target, e, id) !== false){
3679             if(target.isNotifyTarget){
3680                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3681                     this.onValidDrop(target, e, id);
3682                 }else{
3683                     this.onInvalidDrop(target, e, id);
3684                 }
3685             }else{
3686                 this.onValidDrop(target, e, id);
3687             }
3688             
3689             if(this.afterDragDrop){
3690                 /**
3691                  * An empty function by default, but provided so that you can perform a custom action
3692                  * after a valid drag drop has occurred by providing an implementation.
3693                  * @param {Roo.dd.DragDrop} target The drop target
3694                  * @param {Event} e The event object
3695                  * @param {String} id The id of the dropped element
3696                  * @method afterDragDrop
3697                  */
3698                 this.afterDragDrop(target, e, id);
3699             }
3700         }
3701         delete this.cachedTarget;
3702     },
3703
3704     /**
3705      * An empty function by default, but provided so that you can perform a custom action before the dragged
3706      * item is dropped onto the target and optionally cancel the onDragDrop.
3707      * @param {Roo.dd.DragDrop} target The drop target
3708      * @param {Event} e The event object
3709      * @param {String} id The id of the dragged element
3710      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3711      */
3712     beforeDragDrop : function(target, e, id){
3713         return true;
3714     },
3715
3716     // private
3717     onValidDrop : function(target, e, id){
3718         this.hideProxy();
3719         if(this.afterValidDrop){
3720             /**
3721              * An empty function by default, but provided so that you can perform a custom action
3722              * after a valid drop has occurred by providing an implementation.
3723              * @param {Object} target The target DD 
3724              * @param {Event} e The event object
3725              * @param {String} id The id of the dropped element
3726              * @method afterInvalidDrop
3727              */
3728             this.afterValidDrop(target, e, id);
3729         }
3730     },
3731
3732     // private
3733     getRepairXY : function(e, data){
3734         return this.el.getXY();  
3735     },
3736
3737     // private
3738     onInvalidDrop : function(target, e, id){
3739         this.beforeInvalidDrop(target, e, id);
3740         if(this.cachedTarget){
3741             if(this.cachedTarget.isNotifyTarget){
3742                 this.cachedTarget.notifyOut(this, e, this.dragData);
3743             }
3744             this.cacheTarget = null;
3745         }
3746         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3747
3748         if(this.afterInvalidDrop){
3749             /**
3750              * An empty function by default, but provided so that you can perform a custom action
3751              * after an invalid drop has occurred by providing an implementation.
3752              * @param {Event} e The event object
3753              * @param {String} id The id of the dropped element
3754              * @method afterInvalidDrop
3755              */
3756             this.afterInvalidDrop(e, id);
3757         }
3758     },
3759
3760     // private
3761     afterRepair : function(){
3762         if(Roo.enableFx){
3763             this.el.highlight(this.hlColor || "c3daf9");
3764         }
3765         this.dragging = false;
3766     },
3767
3768     /**
3769      * An empty function by default, but provided so that you can perform a custom action after an invalid
3770      * drop has occurred.
3771      * @param {Roo.dd.DragDrop} target The drop target
3772      * @param {Event} e The event object
3773      * @param {String} id The id of the dragged element
3774      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3775      */
3776     beforeInvalidDrop : function(target, e, id){
3777         return true;
3778     },
3779
3780     // private
3781     handleMouseDown : function(e){
3782         if(this.dragging) {
3783             return;
3784         }
3785         var data = this.getDragData(e);
3786         if(data && this.onBeforeDrag(data, e) !== false){
3787             this.dragData = data;
3788             this.proxy.stop();
3789             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3790         } 
3791     },
3792
3793     /**
3794      * An empty function by default, but provided so that you can perform a custom action before the initial
3795      * drag event begins and optionally cancel it.
3796      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3797      * @param {Event} e The event object
3798      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3799      */
3800     onBeforeDrag : function(data, e){
3801         return true;
3802     },
3803
3804     /**
3805      * An empty function by default, but provided so that you can perform a custom action once the initial
3806      * drag event has begun.  The drag cannot be canceled from this function.
3807      * @param {Number} x The x position of the click on the dragged object
3808      * @param {Number} y The y position of the click on the dragged object
3809      */
3810     onStartDrag : Roo.emptyFn,
3811
3812     // private - YUI override
3813     startDrag : function(x, y){
3814         this.proxy.reset();
3815         this.dragging = true;
3816         this.proxy.update("");
3817         this.onInitDrag(x, y);
3818         this.proxy.show();
3819     },
3820
3821     // private
3822     onInitDrag : function(x, y){
3823         var clone = this.el.dom.cloneNode(true);
3824         clone.id = Roo.id(); // prevent duplicate ids
3825         this.proxy.update(clone);
3826         this.onStartDrag(x, y);
3827         return true;
3828     },
3829
3830     /**
3831      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3832      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3833      */
3834     getProxy : function(){
3835         return this.proxy;  
3836     },
3837
3838     /**
3839      * Hides the drag source's {@link Roo.dd.StatusProxy}
3840      */
3841     hideProxy : function(){
3842         this.proxy.hide();  
3843         this.proxy.reset(true);
3844         this.dragging = false;
3845     },
3846
3847     // private
3848     triggerCacheRefresh : function(){
3849         Roo.dd.DDM.refreshCache(this.groups);
3850     },
3851
3852     // private - override to prevent hiding
3853     b4EndDrag: function(e) {
3854     },
3855
3856     // private - override to prevent moving
3857     endDrag : function(e){
3858         this.onEndDrag(this.dragData, e);
3859     },
3860
3861     // private
3862     onEndDrag : function(data, e){
3863     },
3864     
3865     // private - pin to cursor
3866     autoOffset : function(x, y) {
3867         this.setDelta(-12, -20);
3868     }    
3869 });/*
3870  * Based on:
3871  * Ext JS Library 1.1.1
3872  * Copyright(c) 2006-2007, Ext JS, LLC.
3873  *
3874  * Originally Released Under LGPL - original licence link has changed is not relivant.
3875  *
3876  * Fork - LGPL
3877  * <script type="text/javascript">
3878  */
3879
3880
3881 /**
3882  * @class Roo.dd.DropTarget
3883  * @extends Roo.dd.DDTarget
3884  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3885  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3886  * @constructor
3887  * @param {String/HTMLElement/Element} el The container element
3888  * @param {Object} config
3889  */
3890 Roo.dd.DropTarget = function(el, config){
3891     this.el = Roo.get(el);
3892     
3893     Roo.apply(this, config);
3894     
3895     if(this.containerScroll){
3896         Roo.dd.ScrollManager.register(this.el);
3897     }
3898     
3899      
3900     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3901         this.el.dom, 
3902         this.ddGroup || this.group,
3903         {
3904             isTarget: true,
3905             events : {
3906                  /**
3907                  * @scope Roo.dd.DropTarget
3908                  */
3909                  
3910                  /**
3911                  * @event enter
3912                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913                  * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914                  * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915                  * 
3916                  * IMPORTANT : it should set this.overClass and this.dropAllowed
3917                  * 
3918                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919                  * @param {Event} e The event
3920                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3921                  */
3922                 "enter" : true,
3923                 
3924                  /**
3925                  * @event over
3926                  * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927                  * This method will be called on every mouse movement while the drag source is over the drop target.
3928                  * This default implementation simply returns the dropAllowed config value.
3929                  * 
3930                  * IMPORTANT : it should set this.dropAllowed
3931                  * 
3932                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933                  * @param {Event} e The event
3934                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3935                  
3936                  */
3937                 "over" : true,
3938                 /**
3939                  * @event out
3940                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941                  * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942                  * overClass (if any) from the drop element.
3943                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944                  * @param {Event} e The event
3945                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3946                  */
3947                  "out" : true,
3948                  
3949                 /**
3950                  * @event drop
3951                  * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952                  * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953                  * implementation that does something to process the drop event and returns true so that the drag source's
3954                  * repair action does not run.
3955                  * 
3956                  * IMPORTANT : it should set this.success
3957                  * 
3958                  * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959                  * @param {Event} e The event
3960                  * @param {Object} data An object containing arbitrary data supplied by the drag source
3961                 */
3962                  "drop" : true
3963             }
3964                 
3965         
3966         }
3967     );
3968
3969 };
3970
3971 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3972     /**
3973      * @cfg {String} overClass
3974      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3975      */
3976     /**
3977      * @cfg {String} dropAllowed
3978      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3979      */
3980     dropAllowed : "x-dd-drop-ok",
3981     /**
3982      * @cfg {String} dropNotAllowed
3983      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3984      */
3985     dropNotAllowed : "x-dd-drop-nodrop",
3986     /**
3987      * @cfg {boolean} success
3988      * set this after drop listener.. 
3989      */
3990     success : false,
3991     /**
3992      * @cfg {boolean} valid
3993      * if the drop point is valid for over/enter..
3994      */
3995     valid : false,
3996     // private
3997     isTarget : true,
3998
3999     // private
4000     isNotifyTarget : true,
4001     
4002     // private
4003
4004     notifyEnter : function(dd, e, data){
4005         this.valid = true;
4006         this.fireEvent('enter', this, dd, e, data);
4007         if(this.overClass){
4008             this.el.addClass(this.overClass);
4009         }
4010         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4011     },
4012
4013     // private
4014
4015     notifyOver : function(dd, e, data){
4016         this.valid = true;
4017         this.fireEvent('over', this, dd, e, data);
4018         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4019     },
4020
4021     
4022     notifyOut : function(dd, e, data){
4023         this.fireEvent('out', this, dd, e, data);
4024         if(this.overClass){
4025             this.el.removeClass(this.overClass);
4026         }
4027     },
4028
4029     
4030     notifyDrop : function(dd, e, data){
4031         this.success = false;
4032         this.fireEvent('drop', this, dd, e, data);
4033         return this.success;
4034     }
4035 });/*
4036  * Based on:
4037  * Ext JS Library 1.1.1
4038  * Copyright(c) 2006-2007, Ext JS, LLC.
4039  *
4040  * Originally Released Under LGPL - original licence link has changed is not relivant.
4041  *
4042  * Fork - LGPL
4043  * <script type="text/javascript">
4044  */
4045
4046
4047 /**
4048  * @class Roo.dd.DragZone
4049  * @extends Roo.dd.DragSource
4050  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4051  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4052  * @constructor
4053  * @param {String/HTMLElement/Element} el The container element
4054  * @param {Object} config
4055  */
4056 Roo.dd.DragZone = function(el, config){
4057     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4058     if(this.containerScroll){
4059         Roo.dd.ScrollManager.register(this.el);
4060     }
4061 };
4062
4063 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4064     /**
4065      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4066      * for auto scrolling during drag operations.
4067      */
4068     /**
4069      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4070      * method after a failed drop (defaults to "c3daf9" - light blue)
4071      */
4072
4073     /**
4074      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4075      * for a valid target to drag based on the mouse down. Override this method
4076      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4077      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4078      * @param {EventObject} e The mouse down event
4079      * @return {Object} The dragData
4080      */
4081     getDragData : function(e){
4082         return Roo.dd.Registry.getHandleFromEvent(e);
4083     },
4084     
4085     /**
4086      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4087      * this.dragData.ddel
4088      * @param {Number} x The x position of the click on the dragged object
4089      * @param {Number} y The y position of the click on the dragged object
4090      * @return {Boolean} true to continue the drag, false to cancel
4091      */
4092     onInitDrag : function(x, y){
4093         this.proxy.update(this.dragData.ddel.cloneNode(true));
4094         this.onStartDrag(x, y);
4095         return true;
4096     },
4097     
4098     /**
4099      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4100      */
4101     afterRepair : function(){
4102         if(Roo.enableFx){
4103             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4104         }
4105         this.dragging = false;
4106     },
4107
4108     /**
4109      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4110      * the XY of this.dragData.ddel
4111      * @param {EventObject} e The mouse up event
4112      * @return {Array} The xy location (e.g. [100, 200])
4113      */
4114     getRepairXY : function(e){
4115         return Roo.Element.fly(this.dragData.ddel).getXY();  
4116     }
4117 });/*
4118  * Based on:
4119  * Ext JS Library 1.1.1
4120  * Copyright(c) 2006-2007, Ext JS, LLC.
4121  *
4122  * Originally Released Under LGPL - original licence link has changed is not relivant.
4123  *
4124  * Fork - LGPL
4125  * <script type="text/javascript">
4126  */
4127 /**
4128  * @class Roo.dd.DropZone
4129  * @extends Roo.dd.DropTarget
4130  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4131  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4132  * @constructor
4133  * @param {String/HTMLElement/Element} el The container element
4134  * @param {Object} config
4135  */
4136 Roo.dd.DropZone = function(el, config){
4137     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4138 };
4139
4140 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4141     /**
4142      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4143      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4144      * provide your own custom lookup.
4145      * @param {Event} e The event
4146      * @return {Object} data The custom data
4147      */
4148     getTargetFromEvent : function(e){
4149         return Roo.dd.Registry.getTargetFromEvent(e);
4150     },
4151
4152     /**
4153      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4154      * that it has registered.  This method has no default implementation and should be overridden to provide
4155      * node-specific processing if necessary.
4156      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4157      * {@link #getTargetFromEvent} for this node)
4158      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4159      * @param {Event} e The event
4160      * @param {Object} data An object containing arbitrary data supplied by the drag source
4161      */
4162     onNodeEnter : function(n, dd, e, data){
4163         
4164     },
4165
4166     /**
4167      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4168      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4169      * overridden to provide the proper feedback.
4170      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4171      * {@link #getTargetFromEvent} for this node)
4172      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4173      * @param {Event} e The event
4174      * @param {Object} data An object containing arbitrary data supplied by the drag source
4175      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4176      * underlying {@link Roo.dd.StatusProxy} can be updated
4177      */
4178     onNodeOver : function(n, dd, e, data){
4179         return this.dropAllowed;
4180     },
4181
4182     /**
4183      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4184      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4185      * node-specific processing if necessary.
4186      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4187      * {@link #getTargetFromEvent} for this node)
4188      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4189      * @param {Event} e The event
4190      * @param {Object} data An object containing arbitrary data supplied by the drag source
4191      */
4192     onNodeOut : function(n, dd, e, data){
4193         
4194     },
4195
4196     /**
4197      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4198      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4199      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4200      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4201      * {@link #getTargetFromEvent} for this node)
4202      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4203      * @param {Event} e The event
4204      * @param {Object} data An object containing arbitrary data supplied by the drag source
4205      * @return {Boolean} True if the drop was valid, else false
4206      */
4207     onNodeDrop : function(n, dd, e, data){
4208         return false;
4209     },
4210
4211     /**
4212      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4213      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4214      * it should be overridden to provide the proper feedback if necessary.
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4219      * underlying {@link Roo.dd.StatusProxy} can be updated
4220      */
4221     onContainerOver : function(dd, e, data){
4222         return this.dropNotAllowed;
4223     },
4224
4225     /**
4226      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4227      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4228      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4229      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4230      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4231      * @param {Event} e The event
4232      * @param {Object} data An object containing arbitrary data supplied by the drag source
4233      * @return {Boolean} True if the drop was valid, else false
4234      */
4235     onContainerDrop : function(dd, e, data){
4236         return false;
4237     },
4238
4239     /**
4240      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4241      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4242      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4243      * you should override this method and provide a custom implementation.
4244      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4245      * @param {Event} e The event
4246      * @param {Object} data An object containing arbitrary data supplied by the drag source
4247      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4248      * underlying {@link Roo.dd.StatusProxy} can be updated
4249      */
4250     notifyEnter : function(dd, e, data){
4251         return this.dropNotAllowed;
4252     },
4253
4254     /**
4255      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4256      * This method will be called on every mouse movement while the drag source is over the drop zone.
4257      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4258      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4259      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4260      * registered node, it will call {@link #onContainerOver}.
4261      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4262      * @param {Event} e The event
4263      * @param {Object} data An object containing arbitrary data supplied by the drag source
4264      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4265      * underlying {@link Roo.dd.StatusProxy} can be updated
4266      */
4267     notifyOver : function(dd, e, data){
4268         var n = this.getTargetFromEvent(e);
4269         if(!n){ // not over valid drop target
4270             if(this.lastOverNode){
4271                 this.onNodeOut(this.lastOverNode, dd, e, data);
4272                 this.lastOverNode = null;
4273             }
4274             return this.onContainerOver(dd, e, data);
4275         }
4276         if(this.lastOverNode != n){
4277             if(this.lastOverNode){
4278                 this.onNodeOut(this.lastOverNode, dd, e, data);
4279             }
4280             this.onNodeEnter(n, dd, e, data);
4281             this.lastOverNode = n;
4282         }
4283         return this.onNodeOver(n, dd, e, data);
4284     },
4285
4286     /**
4287      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4288      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4289      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4290      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4291      * @param {Event} e The event
4292      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4293      */
4294     notifyOut : function(dd, e, data){
4295         if(this.lastOverNode){
4296             this.onNodeOut(this.lastOverNode, dd, e, data);
4297             this.lastOverNode = null;
4298         }
4299     },
4300
4301     /**
4302      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4303      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4304      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4305      * otherwise it will call {@link #onContainerDrop}.
4306      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4307      * @param {Event} e The event
4308      * @param {Object} data An object containing arbitrary data supplied by the drag source
4309      * @return {Boolean} True if the drop was valid, else false
4310      */
4311     notifyDrop : function(dd, e, data){
4312         if(this.lastOverNode){
4313             this.onNodeOut(this.lastOverNode, dd, e, data);
4314             this.lastOverNode = null;
4315         }
4316         var n = this.getTargetFromEvent(e);
4317         return n ?
4318             this.onNodeDrop(n, dd, e, data) :
4319             this.onContainerDrop(dd, e, data);
4320     },
4321
4322     // private
4323     triggerCacheRefresh : function(){
4324         Roo.dd.DDM.refreshCache(this.groups);
4325     }  
4326 });/*
4327  * Based on:
4328  * Ext JS Library 1.1.1
4329  * Copyright(c) 2006-2007, Ext JS, LLC.
4330  *
4331  * Originally Released Under LGPL - original licence link has changed is not relivant.
4332  *
4333  * Fork - LGPL
4334  * <script type="text/javascript">
4335  */
4336
4337
4338 /**
4339  * @class Roo.data.SortTypes
4340  * @singleton
4341  * Defines the default sorting (casting?) comparison functions used when sorting data.
4342  */
4343 Roo.data.SortTypes = {
4344     /**
4345      * Default sort that does nothing
4346      * @param {Mixed} s The value being converted
4347      * @return {Mixed} The comparison value
4348      */
4349     none : function(s){
4350         return s;
4351     },
4352     
4353     /**
4354      * The regular expression used to strip tags
4355      * @type {RegExp}
4356      * @property
4357      */
4358     stripTagsRE : /<\/?[^>]+>/gi,
4359     
4360     /**
4361      * Strips all HTML tags to sort on text only
4362      * @param {Mixed} s The value being converted
4363      * @return {String} The comparison value
4364      */
4365     asText : function(s){
4366         return String(s).replace(this.stripTagsRE, "");
4367     },
4368     
4369     /**
4370      * Strips all HTML tags to sort on text only - Case insensitive
4371      * @param {Mixed} s The value being converted
4372      * @return {String} The comparison value
4373      */
4374     asUCText : function(s){
4375         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4376     },
4377     
4378     /**
4379      * Case insensitive string
4380      * @param {Mixed} s The value being converted
4381      * @return {String} The comparison value
4382      */
4383     asUCString : function(s) {
4384         return String(s).toUpperCase();
4385     },
4386     
4387     /**
4388      * Date sorting
4389      * @param {Mixed} s The value being converted
4390      * @return {Number} The comparison value
4391      */
4392     asDate : function(s) {
4393         if(!s){
4394             return 0;
4395         }
4396         if(s instanceof Date){
4397             return s.getTime();
4398         }
4399         return Date.parse(String(s));
4400     },
4401     
4402     /**
4403      * Float sorting
4404      * @param {Mixed} s The value being converted
4405      * @return {Float} The comparison value
4406      */
4407     asFloat : function(s) {
4408         var val = parseFloat(String(s).replace(/,/g, ""));
4409         if(isNaN(val)) val = 0;
4410         return val;
4411     },
4412     
4413     /**
4414      * Integer sorting
4415      * @param {Mixed} s The value being converted
4416      * @return {Number} The comparison value
4417      */
4418     asInt : function(s) {
4419         var val = parseInt(String(s).replace(/,/g, ""));
4420         if(isNaN(val)) val = 0;
4421         return val;
4422     }
4423 };/*
4424  * Based on:
4425  * Ext JS Library 1.1.1
4426  * Copyright(c) 2006-2007, Ext JS, LLC.
4427  *
4428  * Originally Released Under LGPL - original licence link has changed is not relivant.
4429  *
4430  * Fork - LGPL
4431  * <script type="text/javascript">
4432  */
4433
4434 /**
4435 * @class Roo.data.Record
4436  * Instances of this class encapsulate both record <em>definition</em> information, and record
4437  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4438  * to access Records cached in an {@link Roo.data.Store} object.<br>
4439  * <p>
4440  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4441  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4442  * objects.<br>
4443  * <p>
4444  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4445  * @constructor
4446  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4447  * {@link #create}. The parameters are the same.
4448  * @param {Array} data An associative Array of data values keyed by the field name.
4449  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4450  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4451  * not specified an integer id is generated.
4452  */
4453 Roo.data.Record = function(data, id){
4454     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4455     this.data = data;
4456 };
4457
4458 /**
4459  * Generate a constructor for a specific record layout.
4460  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4461  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4462  * Each field definition object may contain the following properties: <ul>
4463  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
4464  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4465  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4466  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4467  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4468  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4469  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4470  * this may be omitted.</p></li>
4471  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4472  * <ul><li>auto (Default, implies no conversion)</li>
4473  * <li>string</li>
4474  * <li>int</li>
4475  * <li>float</li>
4476  * <li>boolean</li>
4477  * <li>date</li></ul></p></li>
4478  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4479  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4480  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4481  * by the Reader into an object that will be stored in the Record. It is passed the
4482  * following parameters:<ul>
4483  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4484  * </ul></p></li>
4485  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4486  * </ul>
4487  * <br>usage:<br><pre><code>
4488 var TopicRecord = Roo.data.Record.create(
4489     {name: 'title', mapping: 'topic_title'},
4490     {name: 'author', mapping: 'username'},
4491     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4492     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4493     {name: 'lastPoster', mapping: 'user2'},
4494     {name: 'excerpt', mapping: 'post_text'}
4495 );
4496
4497 var myNewRecord = new TopicRecord({
4498     title: 'Do my job please',
4499     author: 'noobie',
4500     totalPosts: 1,
4501     lastPost: new Date(),
4502     lastPoster: 'Animal',
4503     excerpt: 'No way dude!'
4504 });
4505 myStore.add(myNewRecord);
4506 </code></pre>
4507  * @method create
4508  * @static
4509  */
4510 Roo.data.Record.create = function(o){
4511     var f = function(){
4512         f.superclass.constructor.apply(this, arguments);
4513     };
4514     Roo.extend(f, Roo.data.Record);
4515     var p = f.prototype;
4516     p.fields = new Roo.util.MixedCollection(false, function(field){
4517         return field.name;
4518     });
4519     for(var i = 0, len = o.length; i < len; i++){
4520         p.fields.add(new Roo.data.Field(o[i]));
4521     }
4522     f.getField = function(name){
4523         return p.fields.get(name);  
4524     };
4525     return f;
4526 };
4527
4528 Roo.data.Record.AUTO_ID = 1000;
4529 Roo.data.Record.EDIT = 'edit';
4530 Roo.data.Record.REJECT = 'reject';
4531 Roo.data.Record.COMMIT = 'commit';
4532
4533 Roo.data.Record.prototype = {
4534     /**
4535      * Readonly flag - true if this record has been modified.
4536      * @type Boolean
4537      */
4538     dirty : false,
4539     editing : false,
4540     error: null,
4541     modified: null,
4542
4543     // private
4544     join : function(store){
4545         this.store = store;
4546     },
4547
4548     /**
4549      * Set the named field to the specified value.
4550      * @param {String} name The name of the field to set.
4551      * @param {Object} value The value to set the field to.
4552      */
4553     set : function(name, value){
4554         if(this.data[name] == value){
4555             return;
4556         }
4557         this.dirty = true;
4558         if(!this.modified){
4559             this.modified = {};
4560         }
4561         if(typeof this.modified[name] == 'undefined'){
4562             this.modified[name] = this.data[name];
4563         }
4564         this.data[name] = value;
4565         if(!this.editing){
4566             this.store.afterEdit(this);
4567         }       
4568     },
4569
4570     /**
4571      * Get the value of the named field.
4572      * @param {String} name The name of the field to get the value of.
4573      * @return {Object} The value of the field.
4574      */
4575     get : function(name){
4576         return this.data[name]; 
4577     },
4578
4579     // private
4580     beginEdit : function(){
4581         this.editing = true;
4582         this.modified = {}; 
4583     },
4584
4585     // private
4586     cancelEdit : function(){
4587         this.editing = false;
4588         delete this.modified;
4589     },
4590
4591     // private
4592     endEdit : function(){
4593         this.editing = false;
4594         if(this.dirty && this.store){
4595             this.store.afterEdit(this);
4596         }
4597     },
4598
4599     /**
4600      * Usually called by the {@link Roo.data.Store} which owns the Record.
4601      * Rejects all changes made to the Record since either creation, or the last commit operation.
4602      * Modified fields are reverted to their original values.
4603      * <p>
4604      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4605      * of reject operations.
4606      */
4607     reject : function(){
4608         var m = this.modified;
4609         for(var n in m){
4610             if(typeof m[n] != "function"){
4611                 this.data[n] = m[n];
4612             }
4613         }
4614         this.dirty = false;
4615         delete this.modified;
4616         this.editing = false;
4617         if(this.store){
4618             this.store.afterReject(this);
4619         }
4620     },
4621
4622     /**
4623      * Usually called by the {@link Roo.data.Store} which owns the Record.
4624      * Commits all changes made to the Record since either creation, or the last commit operation.
4625      * <p>
4626      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4627      * of commit operations.
4628      */
4629     commit : function(){
4630         this.dirty = false;
4631         delete this.modified;
4632         this.editing = false;
4633         if(this.store){
4634             this.store.afterCommit(this);
4635         }
4636     },
4637
4638     // private
4639     hasError : function(){
4640         return this.error != null;
4641     },
4642
4643     // private
4644     clearError : function(){
4645         this.error = null;
4646     },
4647
4648     /**
4649      * Creates a copy of this record.
4650      * @param {String} id (optional) A new record id if you don't want to use this record's id
4651      * @return {Record}
4652      */
4653     copy : function(newId) {
4654         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4655     }
4656 };/*
4657  * Based on:
4658  * Ext JS Library 1.1.1
4659  * Copyright(c) 2006-2007, Ext JS, LLC.
4660  *
4661  * Originally Released Under LGPL - original licence link has changed is not relivant.
4662  *
4663  * Fork - LGPL
4664  * <script type="text/javascript">
4665  */
4666
4667
4668
4669 /**
4670  * @class Roo.data.Store
4671  * @extends Roo.util.Observable
4672  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4673  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4674  * <p>
4675  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
4676  * has no knowledge of the format of the data returned by the Proxy.<br>
4677  * <p>
4678  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4679  * instances from the data object. These records are cached and made available through accessor functions.
4680  * @constructor
4681  * Creates a new Store.
4682  * @param {Object} config A config object containing the objects needed for the Store to access data,
4683  * and read the data into Records.
4684  */
4685 Roo.data.Store = function(config){
4686     this.data = new Roo.util.MixedCollection(false);
4687     this.data.getKey = function(o){
4688         return o.id;
4689     };
4690     this.baseParams = {};
4691     // private
4692     this.paramNames = {
4693         "start" : "start",
4694         "limit" : "limit",
4695         "sort" : "sort",
4696         "dir" : "dir"
4697     };
4698
4699     if(config && config.data){
4700         this.inlineData = config.data;
4701         delete config.data;
4702     }
4703
4704     Roo.apply(this, config);
4705     
4706     if(this.reader){ // reader passed
4707         this.reader = Roo.factory(this.reader, Roo.data);
4708         this.reader.xmodule = this.xmodule || false;
4709         if(!this.recordType){
4710             this.recordType = this.reader.recordType;
4711         }
4712         if(this.reader.onMetaChange){
4713             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4714         }
4715     }
4716
4717     if(this.recordType){
4718         this.fields = this.recordType.prototype.fields;
4719     }
4720     this.modified = [];
4721
4722     this.addEvents({
4723         /**
4724          * @event datachanged
4725          * Fires when the data cache has changed, and a widget which is using this Store
4726          * as a Record cache should refresh its view.
4727          * @param {Store} this
4728          */
4729         datachanged : true,
4730         /**
4731          * @event metachange
4732          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4733          * @param {Store} this
4734          * @param {Object} meta The JSON metadata
4735          */
4736         metachange : true,
4737         /**
4738          * @event add
4739          * Fires when Records have been added to the Store
4740          * @param {Store} this
4741          * @param {Roo.data.Record[]} records The array of Records added
4742          * @param {Number} index The index at which the record(s) were added
4743          */
4744         add : true,
4745         /**
4746          * @event remove
4747          * Fires when a Record has been removed from the Store
4748          * @param {Store} this
4749          * @param {Roo.data.Record} record The Record that was removed
4750          * @param {Number} index The index at which the record was removed
4751          */
4752         remove : true,
4753         /**
4754          * @event update
4755          * Fires when a Record has been updated
4756          * @param {Store} this
4757          * @param {Roo.data.Record} record The Record that was updated
4758          * @param {String} operation The update operation being performed.  Value may be one of:
4759          * <pre><code>
4760  Roo.data.Record.EDIT
4761  Roo.data.Record.REJECT
4762  Roo.data.Record.COMMIT
4763          * </code></pre>
4764          */
4765         update : true,
4766         /**
4767          * @event clear
4768          * Fires when the data cache has been cleared.
4769          * @param {Store} this
4770          */
4771         clear : true,
4772         /**
4773          * @event beforeload
4774          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4775          * the load action will be canceled.
4776          * @param {Store} this
4777          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4778          */
4779         beforeload : true,
4780         /**
4781          * @event load
4782          * Fires after a new set of Records has been loaded.
4783          * @param {Store} this
4784          * @param {Roo.data.Record[]} records The Records that were loaded
4785          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4786          */
4787         load : true,
4788         /**
4789          * @event loadexception
4790          * Fires if an exception occurs in the Proxy during loading.
4791          * Called with the signature of the Proxy's "loadexception" event.
4792          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4793          * 
4794          * @param {Proxy} 
4795          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4796          * @param {Object} load options 
4797          * @param {Object} jsonData from your request (normally this contains the Exception)
4798          */
4799         loadexception : true
4800     });
4801     
4802     if(this.proxy){
4803         this.proxy = Roo.factory(this.proxy, Roo.data);
4804         this.proxy.xmodule = this.xmodule || false;
4805         this.relayEvents(this.proxy,  ["loadexception"]);
4806     }
4807     this.sortToggle = {};
4808
4809     Roo.data.Store.superclass.constructor.call(this);
4810
4811     if(this.inlineData){
4812         this.loadData(this.inlineData);
4813         delete this.inlineData;
4814     }
4815 };
4816 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4817      /**
4818     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4819     * without a remote query - used by combo/forms at present.
4820     */
4821     
4822     /**
4823     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4824     */
4825     /**
4826     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4827     */
4828     /**
4829     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4830     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4831     */
4832     /**
4833     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4834     * on any HTTP request
4835     */
4836     /**
4837     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4838     */
4839     /**
4840     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4841     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4842     */
4843     remoteSort : false,
4844
4845     /**
4846     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4847      * loaded or when a record is removed. (defaults to false).
4848     */
4849     pruneModifiedRecords : false,
4850
4851     // private
4852     lastOptions : null,
4853
4854     /**
4855      * Add Records to the Store and fires the add event.
4856      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4857      */
4858     add : function(records){
4859         records = [].concat(records);
4860         for(var i = 0, len = records.length; i < len; i++){
4861             records[i].join(this);
4862         }
4863         var index = this.data.length;
4864         this.data.addAll(records);
4865         this.fireEvent("add", this, records, index);
4866     },
4867
4868     /**
4869      * Remove a Record from the Store and fires the remove event.
4870      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4871      */
4872     remove : function(record){
4873         var index = this.data.indexOf(record);
4874         this.data.removeAt(index);
4875         if(this.pruneModifiedRecords){
4876             this.modified.remove(record);
4877         }
4878         this.fireEvent("remove", this, record, index);
4879     },
4880
4881     /**
4882      * Remove all Records from the Store and fires the clear event.
4883      */
4884     removeAll : function(){
4885         this.data.clear();
4886         if(this.pruneModifiedRecords){
4887             this.modified = [];
4888         }
4889         this.fireEvent("clear", this);
4890     },
4891
4892     /**
4893      * Inserts Records to the Store at the given index and fires the add event.
4894      * @param {Number} index The start index at which to insert the passed Records.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     insert : function(index, records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             this.data.insert(index, records[i]);
4901             records[i].join(this);
4902         }
4903         this.fireEvent("add", this, records, index);
4904     },
4905
4906     /**
4907      * Get the index within the cache of the passed Record.
4908      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4909      * @return {Number} The index of the passed Record. Returns -1 if not found.
4910      */
4911     indexOf : function(record){
4912         return this.data.indexOf(record);
4913     },
4914
4915     /**
4916      * Get the index within the cache of the Record with the passed id.
4917      * @param {String} id The id of the Record to find.
4918      * @return {Number} The index of the Record. Returns -1 if not found.
4919      */
4920     indexOfId : function(id){
4921         return this.data.indexOfKey(id);
4922     },
4923
4924     /**
4925      * Get the Record with the specified id.
4926      * @param {String} id The id of the Record to find.
4927      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4928      */
4929     getById : function(id){
4930         return this.data.key(id);
4931     },
4932
4933     /**
4934      * Get the Record at the specified index.
4935      * @param {Number} index The index of the Record to find.
4936      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4937      */
4938     getAt : function(index){
4939         return this.data.itemAt(index);
4940     },
4941
4942     /**
4943      * Returns a range of Records between specified indices.
4944      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4945      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4946      * @return {Roo.data.Record[]} An array of Records
4947      */
4948     getRange : function(start, end){
4949         return this.data.getRange(start, end);
4950     },
4951
4952     // private
4953     storeOptions : function(o){
4954         o = Roo.apply({}, o);
4955         delete o.callback;
4956         delete o.scope;
4957         this.lastOptions = o;
4958     },
4959
4960     /**
4961      * Loads the Record cache from the configured Proxy using the configured Reader.
4962      * <p>
4963      * If using remote paging, then the first load call must specify the <em>start</em>
4964      * and <em>limit</em> properties in the options.params property to establish the initial
4965      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4966      * <p>
4967      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4968      * and this call will return before the new data has been loaded. Perform any post-processing
4969      * in a callback function, or in a "load" event handler.</strong>
4970      * <p>
4971      * @param {Object} options An object containing properties which control loading options:<ul>
4972      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4973      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4974      * passed the following arguments:<ul>
4975      * <li>r : Roo.data.Record[]</li>
4976      * <li>options: Options object from the load call</li>
4977      * <li>success: Boolean success indicator</li></ul></li>
4978      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4979      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4980      * </ul>
4981      */
4982     load : function(options){
4983         options = options || {};
4984         if(this.fireEvent("beforeload", this, options) !== false){
4985             this.storeOptions(options);
4986             var p = Roo.apply(options.params || {}, this.baseParams);
4987             // if meta was not loaded from remote source.. try requesting it.
4988             if (!this.reader.metaFromRemote) {
4989                 p._requestMeta = 1;
4990             }
4991             if(this.sortInfo && this.remoteSort){
4992                 var pn = this.paramNames;
4993                 p[pn["sort"]] = this.sortInfo.field;
4994                 p[pn["dir"]] = this.sortInfo.direction;
4995             }
4996             this.proxy.load(p, this.reader, this.loadRecords, this, options);
4997         }
4998     },
4999
5000     /**
5001      * Reloads the Record cache from the configured Proxy using the configured Reader and
5002      * the options from the last load operation performed.
5003      * @param {Object} options (optional) An object containing properties which may override the options
5004      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5005      * the most recently used options are reused).
5006      */
5007     reload : function(options){
5008         this.load(Roo.applyIf(options||{}, this.lastOptions));
5009     },
5010
5011     // private
5012     // Called as a callback by the Reader during a load operation.
5013     loadRecords : function(o, options, success){
5014         if(!o || success === false){
5015             if(success !== false){
5016                 this.fireEvent("load", this, [], options);
5017             }
5018             if(options.callback){
5019                 options.callback.call(options.scope || this, [], options, false);
5020             }
5021             return;
5022         }
5023         // if data returned failure - throw an exception.
5024         if (o.success === false) {
5025             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5026             return;
5027         }
5028         var r = o.records, t = o.totalRecords || r.length;
5029         if(!options || options.add !== true){
5030             if(this.pruneModifiedRecords){
5031                 this.modified = [];
5032             }
5033             for(var i = 0, len = r.length; i < len; i++){
5034                 r[i].join(this);
5035             }
5036             if(this.snapshot){
5037                 this.data = this.snapshot;
5038                 delete this.snapshot;
5039             }
5040             this.data.clear();
5041             this.data.addAll(r);
5042             this.totalLength = t;
5043             this.applySort();
5044             this.fireEvent("datachanged", this);
5045         }else{
5046             this.totalLength = Math.max(t, this.data.length+r.length);
5047             this.add(r);
5048         }
5049         this.fireEvent("load", this, r, options);
5050         if(options.callback){
5051             options.callback.call(options.scope || this, r, options, true);
5052         }
5053     },
5054
5055     /**
5056      * Loads data from a passed data block. A Reader which understands the format of the data
5057      * must have been configured in the constructor.
5058      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5059      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5060      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5061      */
5062     loadData : function(o, append){
5063         var r = this.reader.readRecords(o);
5064         this.loadRecords(r, {add: append}, true);
5065     },
5066
5067     /**
5068      * Gets the number of cached records.
5069      * <p>
5070      * <em>If using paging, this may not be the total size of the dataset. If the data object
5071      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5072      * the data set size</em>
5073      */
5074     getCount : function(){
5075         return this.data.length || 0;
5076     },
5077
5078     /**
5079      * Gets the total number of records in the dataset as returned by the server.
5080      * <p>
5081      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5082      * the dataset size</em>
5083      */
5084     getTotalCount : function(){
5085         return this.totalLength || 0;
5086     },
5087
5088     /**
5089      * Returns the sort state of the Store as an object with two properties:
5090      * <pre><code>
5091  field {String} The name of the field by which the Records are sorted
5092  direction {String} The sort order, "ASC" or "DESC"
5093      * </code></pre>
5094      */
5095     getSortState : function(){
5096         return this.sortInfo;
5097     },
5098
5099     // private
5100     applySort : function(){
5101         if(this.sortInfo && !this.remoteSort){
5102             var s = this.sortInfo, f = s.field;
5103             var st = this.fields.get(f).sortType;
5104             var fn = function(r1, r2){
5105                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5106                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5107             };
5108             this.data.sort(s.direction, fn);
5109             if(this.snapshot && this.snapshot != this.data){
5110                 this.snapshot.sort(s.direction, fn);
5111             }
5112         }
5113     },
5114
5115     /**
5116      * Sets the default sort column and order to be used by the next load operation.
5117      * @param {String} fieldName The name of the field to sort by.
5118      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5119      */
5120     setDefaultSort : function(field, dir){
5121         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5122     },
5123
5124     /**
5125      * Sort the Records.
5126      * If remote sorting is used, the sort is performed on the server, and the cache is
5127      * reloaded. If local sorting is used, the cache is sorted internally.
5128      * @param {String} fieldName The name of the field to sort by.
5129      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5130      */
5131     sort : function(fieldName, dir){
5132         var f = this.fields.get(fieldName);
5133         if(!dir){
5134             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5135                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5136             }else{
5137                 dir = f.sortDir;
5138             }
5139         }
5140         this.sortToggle[f.name] = dir;
5141         this.sortInfo = {field: f.name, direction: dir};
5142         if(!this.remoteSort){
5143             this.applySort();
5144             this.fireEvent("datachanged", this);
5145         }else{
5146             this.load(this.lastOptions);
5147         }
5148     },
5149
5150     /**
5151      * Calls the specified function for each of the Records in the cache.
5152      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5153      * Returning <em>false</em> aborts and exits the iteration.
5154      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5155      */
5156     each : function(fn, scope){
5157         this.data.each(fn, scope);
5158     },
5159
5160     /**
5161      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5162      * (e.g., during paging).
5163      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5164      */
5165     getModifiedRecords : function(){
5166         return this.modified;
5167     },
5168
5169     // private
5170     createFilterFn : function(property, value, anyMatch){
5171         if(!value.exec){ // not a regex
5172             value = String(value);
5173             if(value.length == 0){
5174                 return false;
5175             }
5176             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5177         }
5178         return function(r){
5179             return value.test(r.data[property]);
5180         };
5181     },
5182
5183     /**
5184      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5185      * @param {String} property A field on your records
5186      * @param {Number} start The record index to start at (defaults to 0)
5187      * @param {Number} end The last record index to include (defaults to length - 1)
5188      * @return {Number} The sum
5189      */
5190     sum : function(property, start, end){
5191         var rs = this.data.items, v = 0;
5192         start = start || 0;
5193         end = (end || end === 0) ? end : rs.length-1;
5194
5195         for(var i = start; i <= end; i++){
5196             v += (rs[i].data[property] || 0);
5197         }
5198         return v;
5199     },
5200
5201     /**
5202      * Filter the records by a specified property.
5203      * @param {String} field A field on your records
5204      * @param {String/RegExp} value Either a string that the field
5205      * should start with or a RegExp to test against the field
5206      * @param {Boolean} anyMatch True to match any part not just the beginning
5207      */
5208     filter : function(property, value, anyMatch){
5209         var fn = this.createFilterFn(property, value, anyMatch);
5210         return fn ? this.filterBy(fn) : this.clearFilter();
5211     },
5212
5213     /**
5214      * Filter by a function. The specified function will be called with each
5215      * record in this data source. If the function returns true the record is included,
5216      * otherwise it is filtered.
5217      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5218      * @param {Object} scope (optional) The scope of the function (defaults to this)
5219      */
5220     filterBy : function(fn, scope){
5221         this.snapshot = this.snapshot || this.data;
5222         this.data = this.queryBy(fn, scope||this);
5223         this.fireEvent("datachanged", this);
5224     },
5225
5226     /**
5227      * Query the records by a specified property.
5228      * @param {String} field A field on your records
5229      * @param {String/RegExp} value Either a string that the field
5230      * should start with or a RegExp to test against the field
5231      * @param {Boolean} anyMatch True to match any part not just the beginning
5232      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5233      */
5234     query : function(property, value, anyMatch){
5235         var fn = this.createFilterFn(property, value, anyMatch);
5236         return fn ? this.queryBy(fn) : this.data.clone();
5237     },
5238
5239     /**
5240      * Query by a function. The specified function will be called with each
5241      * record in this data source. If the function returns true the record is included
5242      * in the results.
5243      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5244      * @param {Object} scope (optional) The scope of the function (defaults to this)
5245       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5246      **/
5247     queryBy : function(fn, scope){
5248         var data = this.snapshot || this.data;
5249         return data.filterBy(fn, scope||this);
5250     },
5251
5252     /**
5253      * Collects unique values for a particular dataIndex from this store.
5254      * @param {String} dataIndex The property to collect
5255      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5256      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5257      * @return {Array} An array of the unique values
5258      **/
5259     collect : function(dataIndex, allowNull, bypassFilter){
5260         var d = (bypassFilter === true && this.snapshot) ?
5261                 this.snapshot.items : this.data.items;
5262         var v, sv, r = [], l = {};
5263         for(var i = 0, len = d.length; i < len; i++){
5264             v = d[i].data[dataIndex];
5265             sv = String(v);
5266             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5267                 l[sv] = true;
5268                 r[r.length] = v;
5269             }
5270         }
5271         return r;
5272     },
5273
5274     /**
5275      * Revert to a view of the Record cache with no filtering applied.
5276      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5277      */
5278     clearFilter : function(suppressEvent){
5279         if(this.snapshot && this.snapshot != this.data){
5280             this.data = this.snapshot;
5281             delete this.snapshot;
5282             if(suppressEvent !== true){
5283                 this.fireEvent("datachanged", this);
5284             }
5285         }
5286     },
5287
5288     // private
5289     afterEdit : function(record){
5290         if(this.modified.indexOf(record) == -1){
5291             this.modified.push(record);
5292         }
5293         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5294     },
5295
5296     // private
5297     afterReject : function(record){
5298         this.modified.remove(record);
5299         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5300     },
5301
5302     // private
5303     afterCommit : function(record){
5304         this.modified.remove(record);
5305         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5306     },
5307
5308     /**
5309      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5310      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5311      */
5312     commitChanges : function(){
5313         var m = this.modified.slice(0);
5314         this.modified = [];
5315         for(var i = 0, len = m.length; i < len; i++){
5316             m[i].commit();
5317         }
5318     },
5319
5320     /**
5321      * Cancel outstanding changes on all changed records.
5322      */
5323     rejectChanges : function(){
5324         var m = this.modified.slice(0);
5325         this.modified = [];
5326         for(var i = 0, len = m.length; i < len; i++){
5327             m[i].reject();
5328         }
5329     },
5330
5331     onMetaChange : function(meta, rtype, o){
5332         this.recordType = rtype;
5333         this.fields = rtype.prototype.fields;
5334         delete this.snapshot;
5335         this.sortInfo = meta.sortInfo || this.sortInfo;
5336         this.modified = [];
5337         this.fireEvent('metachange', this, this.reader.meta);
5338     }
5339 });/*
5340  * Based on:
5341  * Ext JS Library 1.1.1
5342  * Copyright(c) 2006-2007, Ext JS, LLC.
5343  *
5344  * Originally Released Under LGPL - original licence link has changed is not relivant.
5345  *
5346  * Fork - LGPL
5347  * <script type="text/javascript">
5348  */
5349
5350 /**
5351  * @class Roo.data.SimpleStore
5352  * @extends Roo.data.Store
5353  * Small helper class to make creating Stores from Array data easier.
5354  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5355  * @cfg {Array} fields An array of field definition objects, or field name strings.
5356  * @cfg {Array} data The multi-dimensional array of data
5357  * @constructor
5358  * @param {Object} config
5359  */
5360 Roo.data.SimpleStore = function(config){
5361     Roo.data.SimpleStore.superclass.constructor.call(this, {
5362         isLocal : true,
5363         reader: new Roo.data.ArrayReader({
5364                 id: config.id
5365             },
5366             Roo.data.Record.create(config.fields)
5367         ),
5368         proxy : new Roo.data.MemoryProxy(config.data)
5369     });
5370     this.load();
5371 };
5372 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5373  * Based on:
5374  * Ext JS Library 1.1.1
5375  * Copyright(c) 2006-2007, Ext JS, LLC.
5376  *
5377  * Originally Released Under LGPL - original licence link has changed is not relivant.
5378  *
5379  * Fork - LGPL
5380  * <script type="text/javascript">
5381  */
5382
5383 /**
5384 /**
5385  * @extends Roo.data.Store
5386  * @class Roo.data.JsonStore
5387  * Small helper class to make creating Stores for JSON data easier. <br/>
5388 <pre><code>
5389 var store = new Roo.data.JsonStore({
5390     url: 'get-images.php',
5391     root: 'images',
5392     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5393 });
5394 </code></pre>
5395  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5396  * JsonReader and HttpProxy (unless inline data is provided).</b>
5397  * @cfg {Array} fields An array of field definition objects, or field name strings.
5398  * @constructor
5399  * @param {Object} config
5400  */
5401 Roo.data.JsonStore = function(c){
5402     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5403         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5404         reader: new Roo.data.JsonReader(c, c.fields)
5405     }));
5406 };
5407 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5408  * Based on:
5409  * Ext JS Library 1.1.1
5410  * Copyright(c) 2006-2007, Ext JS, LLC.
5411  *
5412  * Originally Released Under LGPL - original licence link has changed is not relivant.
5413  *
5414  * Fork - LGPL
5415  * <script type="text/javascript">
5416  */
5417
5418  
5419 Roo.data.Field = function(config){
5420     if(typeof config == "string"){
5421         config = {name: config};
5422     }
5423     Roo.apply(this, config);
5424     
5425     if(!this.type){
5426         this.type = "auto";
5427     }
5428     
5429     var st = Roo.data.SortTypes;
5430     // named sortTypes are supported, here we look them up
5431     if(typeof this.sortType == "string"){
5432         this.sortType = st[this.sortType];
5433     }
5434     
5435     // set default sortType for strings and dates
5436     if(!this.sortType){
5437         switch(this.type){
5438             case "string":
5439                 this.sortType = st.asUCString;
5440                 break;
5441             case "date":
5442                 this.sortType = st.asDate;
5443                 break;
5444             default:
5445                 this.sortType = st.none;
5446         }
5447     }
5448
5449     // define once
5450     var stripRe = /[\$,%]/g;
5451
5452     // prebuilt conversion function for this field, instead of
5453     // switching every time we're reading a value
5454     if(!this.convert){
5455         var cv, dateFormat = this.dateFormat;
5456         switch(this.type){
5457             case "":
5458             case "auto":
5459             case undefined:
5460                 cv = function(v){ return v; };
5461                 break;
5462             case "string":
5463                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5464                 break;
5465             case "int":
5466                 cv = function(v){
5467                     return v !== undefined && v !== null && v !== '' ?
5468                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5469                     };
5470                 break;
5471             case "float":
5472                 cv = function(v){
5473                     return v !== undefined && v !== null && v !== '' ?
5474                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5475                     };
5476                 break;
5477             case "bool":
5478             case "boolean":
5479                 cv = function(v){ return v === true || v === "true" || v == 1; };
5480                 break;
5481             case "date":
5482                 cv = function(v){
5483                     if(!v){
5484                         return '';
5485                     }
5486                     if(v instanceof Date){
5487                         return v;
5488                     }
5489                     if(dateFormat){
5490                         if(dateFormat == "timestamp"){
5491                             return new Date(v*1000);
5492                         }
5493                         return Date.parseDate(v, dateFormat);
5494                     }
5495                     var parsed = Date.parse(v);
5496                     return parsed ? new Date(parsed) : null;
5497                 };
5498              break;
5499             
5500         }
5501         this.convert = cv;
5502     }
5503 };
5504
5505 Roo.data.Field.prototype = {
5506     dateFormat: null,
5507     defaultValue: "",
5508     mapping: null,
5509     sortType : null,
5510     sortDir : "ASC"
5511 };/*
5512  * Based on:
5513  * Ext JS Library 1.1.1
5514  * Copyright(c) 2006-2007, Ext JS, LLC.
5515  *
5516  * Originally Released Under LGPL - original licence link has changed is not relivant.
5517  *
5518  * Fork - LGPL
5519  * <script type="text/javascript">
5520  */
5521  
5522 // Base class for reading structured data from a data source.  This class is intended to be
5523 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5524
5525 /**
5526  * @class Roo.data.DataReader
5527  * Base class for reading structured data from a data source.  This class is intended to be
5528  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5529  */
5530
5531 Roo.data.DataReader = function(meta, recordType){
5532     
5533     this.meta = meta;
5534     
5535     this.recordType = recordType instanceof Array ? 
5536         Roo.data.Record.create(recordType) : recordType;
5537 };
5538
5539 Roo.data.DataReader.prototype = {
5540      /**
5541      * Create an empty record
5542      * @param {Object} data (optional) - overlay some values
5543      * @return {Roo.data.Record} record created.
5544      */
5545     newRow :  function(d) {
5546         var da =  {};
5547         this.recordType.prototype.fields.each(function(c) {
5548             switch( c.type) {
5549                 case 'int' : da[c.name] = 0; break;
5550                 case 'date' : da[c.name] = new Date(); break;
5551                 case 'float' : da[c.name] = 0.0; break;
5552                 case 'boolean' : da[c.name] = false; break;
5553                 default : da[c.name] = ""; break;
5554             }
5555             
5556         });
5557         return new this.recordType(Roo.apply(da, d));
5558     }
5559     
5560 };/*
5561  * Based on:
5562  * Ext JS Library 1.1.1
5563  * Copyright(c) 2006-2007, Ext JS, LLC.
5564  *
5565  * Originally Released Under LGPL - original licence link has changed is not relivant.
5566  *
5567  * Fork - LGPL
5568  * <script type="text/javascript">
5569  */
5570
5571 /**
5572  * @class Roo.data.DataProxy
5573  * @extends Roo.data.Observable
5574  * This class is an abstract base class for implementations which provide retrieval of
5575  * unformatted data objects.<br>
5576  * <p>
5577  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5578  * (of the appropriate type which knows how to parse the data object) to provide a block of
5579  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5580  * <p>
5581  * Custom implementations must implement the load method as described in
5582  * {@link Roo.data.HttpProxy#load}.
5583  */
5584 Roo.data.DataProxy = function(){
5585     this.addEvents({
5586         /**
5587          * @event beforeload
5588          * Fires before a network request is made to retrieve a data object.
5589          * @param {Object} This DataProxy object.
5590          * @param {Object} params The params parameter to the load function.
5591          */
5592         beforeload : true,
5593         /**
5594          * @event load
5595          * Fires before the load method's callback is called.
5596          * @param {Object} This DataProxy object.
5597          * @param {Object} o The data object.
5598          * @param {Object} arg The callback argument object passed to the load function.
5599          */
5600         load : true,
5601         /**
5602          * @event loadexception
5603          * Fires if an Exception occurs during data retrieval.
5604          * @param {Object} This DataProxy object.
5605          * @param {Object} o The data object.
5606          * @param {Object} arg The callback argument object passed to the load function.
5607          * @param {Object} e The Exception.
5608          */
5609         loadexception : true
5610     });
5611     Roo.data.DataProxy.superclass.constructor.call(this);
5612 };
5613
5614 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5615
5616     /**
5617      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5618      */
5619 /*
5620  * Based on:
5621  * Ext JS Library 1.1.1
5622  * Copyright(c) 2006-2007, Ext JS, LLC.
5623  *
5624  * Originally Released Under LGPL - original licence link has changed is not relivant.
5625  *
5626  * Fork - LGPL
5627  * <script type="text/javascript">
5628  */
5629 /**
5630  * @class Roo.data.MemoryProxy
5631  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5632  * to the Reader when its load method is called.
5633  * @constructor
5634  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5635  */
5636 Roo.data.MemoryProxy = function(data){
5637     if (data.data) {
5638         data = data.data;
5639     }
5640     Roo.data.MemoryProxy.superclass.constructor.call(this);
5641     this.data = data;
5642 };
5643
5644 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5645     /**
5646      * Load data from the requested source (in this case an in-memory
5647      * data object passed to the constructor), read the data object into
5648      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5649      * process that block using the passed callback.
5650      * @param {Object} params This parameter is not used by the MemoryProxy class.
5651      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5652      * object into a block of Roo.data.Records.
5653      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5654      * The function must be passed <ul>
5655      * <li>The Record block object</li>
5656      * <li>The "arg" argument from the load function</li>
5657      * <li>A boolean success indicator</li>
5658      * </ul>
5659      * @param {Object} scope The scope in which to call the callback
5660      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5661      */
5662     load : function(params, reader, callback, scope, arg){
5663         params = params || {};
5664         var result;
5665         try {
5666             result = reader.readRecords(this.data);
5667         }catch(e){
5668             this.fireEvent("loadexception", this, arg, null, e);
5669             callback.call(scope, null, arg, false);
5670             return;
5671         }
5672         callback.call(scope, result, arg, true);
5673     },
5674     
5675     // private
5676     update : function(params, records){
5677         
5678     }
5679 });/*
5680  * Based on:
5681  * Ext JS Library 1.1.1
5682  * Copyright(c) 2006-2007, Ext JS, LLC.
5683  *
5684  * Originally Released Under LGPL - original licence link has changed is not relivant.
5685  *
5686  * Fork - LGPL
5687  * <script type="text/javascript">
5688  */
5689 /**
5690  * @class Roo.data.HttpProxy
5691  * @extends Roo.data.DataProxy
5692  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5693  * configured to reference a certain URL.<br><br>
5694  * <p>
5695  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5696  * from which the running page was served.<br><br>
5697  * <p>
5698  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5699  * <p>
5700  * Be aware that to enable the browser to parse an XML document, the server must set
5701  * the Content-Type header in the HTTP response to "text/xml".
5702  * @constructor
5703  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5704  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5705  * will be used to make the request.
5706  */
5707 Roo.data.HttpProxy = function(conn){
5708     Roo.data.HttpProxy.superclass.constructor.call(this);
5709     // is conn a conn config or a real conn?
5710     this.conn = conn;
5711     this.useAjax = !conn || !conn.events;
5712   
5713 };
5714
5715 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5716     // thse are take from connection...
5717     
5718     /**
5719      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5720      */
5721     /**
5722      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5723      * extra parameters to each request made by this object. (defaults to undefined)
5724      */
5725     /**
5726      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5727      *  to each request made by this object. (defaults to undefined)
5728      */
5729     /**
5730      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
5731      */
5732     /**
5733      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5734      */
5735      /**
5736      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5737      * @type Boolean
5738      */
5739   
5740
5741     /**
5742      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5743      * @type Boolean
5744      */
5745     /**
5746      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5747      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5748      * a finer-grained basis than the DataProxy events.
5749      */
5750     getConnection : function(){
5751         return this.useAjax ? Roo.Ajax : this.conn;
5752     },
5753
5754     /**
5755      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5756      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5757      * process that block using the passed callback.
5758      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5759      * for the request to the remote server.
5760      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5761      * object into a block of Roo.data.Records.
5762      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5763      * The function must be passed <ul>
5764      * <li>The Record block object</li>
5765      * <li>The "arg" argument from the load function</li>
5766      * <li>A boolean success indicator</li>
5767      * </ul>
5768      * @param {Object} scope The scope in which to call the callback
5769      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5770      */
5771     load : function(params, reader, callback, scope, arg){
5772         if(this.fireEvent("beforeload", this, params) !== false){
5773             var  o = {
5774                 params : params || {},
5775                 request: {
5776                     callback : callback,
5777                     scope : scope,
5778                     arg : arg
5779                 },
5780                 reader: reader,
5781                 callback : this.loadResponse,
5782                 scope: this
5783             };
5784             if(this.useAjax){
5785                 Roo.applyIf(o, this.conn);
5786                 if(this.activeRequest){
5787                     Roo.Ajax.abort(this.activeRequest);
5788                 }
5789                 this.activeRequest = Roo.Ajax.request(o);
5790             }else{
5791                 this.conn.request(o);
5792             }
5793         }else{
5794             callback.call(scope||this, null, arg, false);
5795         }
5796     },
5797
5798     // private
5799     loadResponse : function(o, success, response){
5800         delete this.activeRequest;
5801         if(!success){
5802             this.fireEvent("loadexception", this, o, response);
5803             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5804             return;
5805         }
5806         var result;
5807         try {
5808             result = o.reader.read(response);
5809         }catch(e){
5810             this.fireEvent("loadexception", this, o, response, e);
5811             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5812             return;
5813         }
5814         
5815         this.fireEvent("load", this, o, o.request.arg);
5816         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5817     },
5818
5819     // private
5820     update : function(dataSet){
5821
5822     },
5823
5824     // private
5825     updateResponse : function(dataSet){
5826
5827     }
5828 });/*
5829  * Based on:
5830  * Ext JS Library 1.1.1
5831  * Copyright(c) 2006-2007, Ext JS, LLC.
5832  *
5833  * Originally Released Under LGPL - original licence link has changed is not relivant.
5834  *
5835  * Fork - LGPL
5836  * <script type="text/javascript">
5837  */
5838
5839 /**
5840  * @class Roo.data.ScriptTagProxy
5841  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5842  * other than the originating domain of the running page.<br><br>
5843  * <p>
5844  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
5845  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5846  * <p>
5847  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5848  * source code that is used as the source inside a &lt;script> tag.<br><br>
5849  * <p>
5850  * In order for the browser to process the returned data, the server must wrap the data object
5851  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5852  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5853  * depending on whether the callback name was passed:
5854  * <p>
5855  * <pre><code>
5856 boolean scriptTag = false;
5857 String cb = request.getParameter("callback");
5858 if (cb != null) {
5859     scriptTag = true;
5860     response.setContentType("text/javascript");
5861 } else {
5862     response.setContentType("application/x-json");
5863 }
5864 Writer out = response.getWriter();
5865 if (scriptTag) {
5866     out.write(cb + "(");
5867 }
5868 out.print(dataBlock.toJsonString());
5869 if (scriptTag) {
5870     out.write(");");
5871 }
5872 </pre></code>
5873  *
5874  * @constructor
5875  * @param {Object} config A configuration object.
5876  */
5877 Roo.data.ScriptTagProxy = function(config){
5878     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5879     Roo.apply(this, config);
5880     this.head = document.getElementsByTagName("head")[0];
5881 };
5882
5883 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5884
5885 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5886     /**
5887      * @cfg {String} url The URL from which to request the data object.
5888      */
5889     /**
5890      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5891      */
5892     timeout : 30000,
5893     /**
5894      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5895      * the server the name of the callback function set up by the load call to process the returned data object.
5896      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5897      * javascript output which calls this named function passing the data object as its only parameter.
5898      */
5899     callbackParam : "callback",
5900     /**
5901      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5902      * name to the request.
5903      */
5904     nocache : true,
5905
5906     /**
5907      * Load data from the configured URL, read the data object into
5908      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5909      * process that block using the passed callback.
5910      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5911      * for the request to the remote server.
5912      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5913      * object into a block of Roo.data.Records.
5914      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5915      * The function must be passed <ul>
5916      * <li>The Record block object</li>
5917      * <li>The "arg" argument from the load function</li>
5918      * <li>A boolean success indicator</li>
5919      * </ul>
5920      * @param {Object} scope The scope in which to call the callback
5921      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5922      */
5923     load : function(params, reader, callback, scope, arg){
5924         if(this.fireEvent("beforeload", this, params) !== false){
5925
5926             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5927
5928             var url = this.url;
5929             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5930             if(this.nocache){
5931                 url += "&_dc=" + (new Date().getTime());
5932             }
5933             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5934             var trans = {
5935                 id : transId,
5936                 cb : "stcCallback"+transId,
5937                 scriptId : "stcScript"+transId,
5938                 params : params,
5939                 arg : arg,
5940                 url : url,
5941                 callback : callback,
5942                 scope : scope,
5943                 reader : reader
5944             };
5945             var conn = this;
5946
5947             window[trans.cb] = function(o){
5948                 conn.handleResponse(o, trans);
5949             };
5950
5951             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5952
5953             if(this.autoAbort !== false){
5954                 this.abort();
5955             }
5956
5957             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5958
5959             var script = document.createElement("script");
5960             script.setAttribute("src", url);
5961             script.setAttribute("type", "text/javascript");
5962             script.setAttribute("id", trans.scriptId);
5963             this.head.appendChild(script);
5964
5965             this.trans = trans;
5966         }else{
5967             callback.call(scope||this, null, arg, false);
5968         }
5969     },
5970
5971     // private
5972     isLoading : function(){
5973         return this.trans ? true : false;
5974     },
5975
5976     /**
5977      * Abort the current server request.
5978      */
5979     abort : function(){
5980         if(this.isLoading()){
5981             this.destroyTrans(this.trans);
5982         }
5983     },
5984
5985     // private
5986     destroyTrans : function(trans, isLoaded){
5987         this.head.removeChild(document.getElementById(trans.scriptId));
5988         clearTimeout(trans.timeoutId);
5989         if(isLoaded){
5990             window[trans.cb] = undefined;
5991             try{
5992                 delete window[trans.cb];
5993             }catch(e){}
5994         }else{
5995             // if hasn't been loaded, wait for load to remove it to prevent script error
5996             window[trans.cb] = function(){
5997                 window[trans.cb] = undefined;
5998                 try{
5999                     delete window[trans.cb];
6000                 }catch(e){}
6001             };
6002         }
6003     },
6004
6005     // private
6006     handleResponse : function(o, trans){
6007         this.trans = false;
6008         this.destroyTrans(trans, true);
6009         var result;
6010         try {
6011             result = trans.reader.readRecords(o);
6012         }catch(e){
6013             this.fireEvent("loadexception", this, o, trans.arg, e);
6014             trans.callback.call(trans.scope||window, null, trans.arg, false);
6015             return;
6016         }
6017         this.fireEvent("load", this, o, trans.arg);
6018         trans.callback.call(trans.scope||window, result, trans.arg, true);
6019     },
6020
6021     // private
6022     handleFailure : function(trans){
6023         this.trans = false;
6024         this.destroyTrans(trans, false);
6025         this.fireEvent("loadexception", this, null, trans.arg);
6026         trans.callback.call(trans.scope||window, null, trans.arg, false);
6027     }
6028 });/*
6029  * Based on:
6030  * Ext JS Library 1.1.1
6031  * Copyright(c) 2006-2007, Ext JS, LLC.
6032  *
6033  * Originally Released Under LGPL - original licence link has changed is not relivant.
6034  *
6035  * Fork - LGPL
6036  * <script type="text/javascript">
6037  */
6038
6039 /**
6040  * @class Roo.data.JsonReader
6041  * @extends Roo.data.DataReader
6042  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6043  * based on mappings in a provided Roo.data.Record constructor.
6044  * 
6045  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6046  * in the reply previously. 
6047  * 
6048  * <p>
6049  * Example code:
6050  * <pre><code>
6051 var RecordDef = Roo.data.Record.create([
6052     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6053     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6054 ]);
6055 var myReader = new Roo.data.JsonReader({
6056     totalProperty: "results",    // The property which contains the total dataset size (optional)
6057     root: "rows",                // The property which contains an Array of row objects
6058     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6059 }, RecordDef);
6060 </code></pre>
6061  * <p>
6062  * This would consume a JSON file like this:
6063  * <pre><code>
6064 { 'results': 2, 'rows': [
6065     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6066     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6067 }
6068 </code></pre>
6069  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6070  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6071  * paged from the remote server.
6072  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6073  * @cfg {String} root name of the property which contains the Array of row objects.
6074  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6075  * @constructor
6076  * Create a new JsonReader
6077  * @param {Object} meta Metadata configuration options
6078  * @param {Object} recordType Either an Array of field definition objects,
6079  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6080  */
6081 Roo.data.JsonReader = function(meta, recordType){
6082     
6083     meta = meta || {};
6084     // set some defaults:
6085     Roo.applyIf(meta, {
6086         totalProperty: 'total',
6087         successProperty : 'success',
6088         root : 'data',
6089         id : 'id'
6090     });
6091     
6092     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6093 };
6094 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6095     
6096     /**
6097      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6098      * Used by Store query builder to append _requestMeta to params.
6099      * 
6100      */
6101     metaFromRemote : false,
6102     /**
6103      * This method is only used by a DataProxy which has retrieved data from a remote server.
6104      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6105      * @return {Object} data A data block which is used by an Roo.data.Store object as
6106      * a cache of Roo.data.Records.
6107      */
6108     read : function(response){
6109         var json = response.responseText;
6110        
6111         var o = /* eval:var:o */ eval("("+json+")");
6112         if(!o) {
6113             throw {message: "JsonReader.read: Json object not found"};
6114         }
6115         
6116         if(o.metaData){
6117             
6118             delete this.ef;
6119             this.metaFromRemote = true;
6120             this.meta = o.metaData;
6121             this.recordType = Roo.data.Record.create(o.metaData.fields);
6122             this.onMetaChange(this.meta, this.recordType, o);
6123         }
6124         return this.readRecords(o);
6125     },
6126
6127     // private function a store will implement
6128     onMetaChange : function(meta, recordType, o){
6129
6130     },
6131
6132     /**
6133          * @ignore
6134          */
6135     simpleAccess: function(obj, subsc) {
6136         return obj[subsc];
6137     },
6138
6139         /**
6140          * @ignore
6141          */
6142     getJsonAccessor: function(){
6143         var re = /[\[\.]/;
6144         return function(expr) {
6145             try {
6146                 return(re.test(expr))
6147                     ? new Function("obj", "return obj." + expr)
6148                     : function(obj){
6149                         return obj[expr];
6150                     };
6151             } catch(e){}
6152             return Roo.emptyFn;
6153         };
6154     }(),
6155
6156     /**
6157      * Create a data block containing Roo.data.Records from an XML document.
6158      * @param {Object} o An object which contains an Array of row objects in the property specified
6159      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6160      * which contains the total size of the dataset.
6161      * @return {Object} data A data block which is used by an Roo.data.Store object as
6162      * a cache of Roo.data.Records.
6163      */
6164     readRecords : function(o){
6165         /**
6166          * After any data loads, the raw JSON data is available for further custom processing.
6167          * @type Object
6168          */
6169         this.jsonData = o;
6170         var s = this.meta, Record = this.recordType,
6171             f = Record.prototype.fields, fi = f.items, fl = f.length;
6172
6173 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6174         if (!this.ef) {
6175             if(s.totalProperty) {
6176                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6177                 }
6178                 if(s.successProperty) {
6179                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6180                 }
6181                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6182                 if (s.id) {
6183                         var g = this.getJsonAccessor(s.id);
6184                         this.getId = function(rec) {
6185                                 var r = g(rec);
6186                                 return (r === undefined || r === "") ? null : r;
6187                         };
6188                 } else {
6189                         this.getId = function(){return null;};
6190                 }
6191             this.ef = [];
6192             for(var jj = 0; jj < fl; jj++){
6193                 f = fi[jj];
6194                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6195                 this.ef[jj] = this.getJsonAccessor(map);
6196             }
6197         }
6198
6199         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6200         if(s.totalProperty){
6201             var vt = parseInt(this.getTotal(o), 10);
6202             if(!isNaN(vt)){
6203                 totalRecords = vt;
6204             }
6205         }
6206         if(s.successProperty){
6207             var vs = this.getSuccess(o);
6208             if(vs === false || vs === 'false'){
6209                 success = false;
6210             }
6211         }
6212         var records = [];
6213             for(var i = 0; i < c; i++){
6214                     var n = root[i];
6215                 var values = {};
6216                 var id = this.getId(n);
6217                 for(var j = 0; j < fl; j++){
6218                     f = fi[j];
6219                 var v = this.ef[j](n);
6220                 if (!f.convert) {
6221                     Roo.log('missing convert for ' + f.name);
6222                     Roo.log(f);
6223                     continue;
6224                 }
6225                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6226                 }
6227                 var record = new Record(values, id);
6228                 record.json = n;
6229                 records[i] = record;
6230             }
6231             return {
6232                 success : success,
6233                 records : records,
6234                 totalRecords : totalRecords
6235             };
6236     }
6237 });/*
6238  * Based on:
6239  * Ext JS Library 1.1.1
6240  * Copyright(c) 2006-2007, Ext JS, LLC.
6241  *
6242  * Originally Released Under LGPL - original licence link has changed is not relivant.
6243  *
6244  * Fork - LGPL
6245  * <script type="text/javascript">
6246  */
6247
6248 /**
6249  * @class Roo.data.XmlReader
6250  * @extends Roo.data.DataReader
6251  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6252  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6253  * <p>
6254  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6255  * header in the HTTP response must be set to "text/xml".</em>
6256  * <p>
6257  * Example code:
6258  * <pre><code>
6259 var RecordDef = Roo.data.Record.create([
6260    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6261    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6262 ]);
6263 var myReader = new Roo.data.XmlReader({
6264    totalRecords: "results", // The element which contains the total dataset size (optional)
6265    record: "row",           // The repeated element which contains row information
6266    id: "id"                 // The element within the row that provides an ID for the record (optional)
6267 }, RecordDef);
6268 </code></pre>
6269  * <p>
6270  * This would consume an XML file like this:
6271  * <pre><code>
6272 &lt;?xml?>
6273 &lt;dataset>
6274  &lt;results>2&lt;/results>
6275  &lt;row>
6276    &lt;id>1&lt;/id>
6277    &lt;name>Bill&lt;/name>
6278    &lt;occupation>Gardener&lt;/occupation>
6279  &lt;/row>
6280  &lt;row>
6281    &lt;id>2&lt;/id>
6282    &lt;name>Ben&lt;/name>
6283    &lt;occupation>Horticulturalist&lt;/occupation>
6284  &lt;/row>
6285 &lt;/dataset>
6286 </code></pre>
6287  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6288  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6289  * paged from the remote server.
6290  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6291  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6292  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6293  * a record identifier value.
6294  * @constructor
6295  * Create a new XmlReader
6296  * @param {Object} meta Metadata configuration options
6297  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6298  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6299  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6300  */
6301 Roo.data.XmlReader = function(meta, recordType){
6302     meta = meta || {};
6303     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6304 };
6305 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6306     /**
6307      * This method is only used by a DataProxy which has retrieved data from a remote server.
6308          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6309          * to contain a method called 'responseXML' that returns an XML document object.
6310      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6311      * a cache of Roo.data.Records.
6312      */
6313     read : function(response){
6314         var doc = response.responseXML;
6315         if(!doc) {
6316             throw {message: "XmlReader.read: XML Document not available"};
6317         }
6318         return this.readRecords(doc);
6319     },
6320
6321     /**
6322      * Create a data block containing Roo.data.Records from an XML document.
6323          * @param {Object} doc A parsed XML document.
6324      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6325      * a cache of Roo.data.Records.
6326      */
6327     readRecords : function(doc){
6328         /**
6329          * After any data loads/reads, the raw XML Document is available for further custom processing.
6330          * @type XMLDocument
6331          */
6332         this.xmlData = doc;
6333         var root = doc.documentElement || doc;
6334         var q = Roo.DomQuery;
6335         var recordType = this.recordType, fields = recordType.prototype.fields;
6336         var sid = this.meta.id;
6337         var totalRecords = 0, success = true;
6338         if(this.meta.totalRecords){
6339             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6340         }
6341         
6342         if(this.meta.success){
6343             var sv = q.selectValue(this.meta.success, root, true);
6344             success = sv !== false && sv !== 'false';
6345         }
6346         var records = [];
6347         var ns = q.select(this.meta.record, root);
6348         for(var i = 0, len = ns.length; i < len; i++) {
6349                 var n = ns[i];
6350                 var values = {};
6351                 var id = sid ? q.selectValue(sid, n) : undefined;
6352                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6353                     var f = fields.items[j];
6354                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6355                     v = f.convert(v);
6356                     values[f.name] = v;
6357                 }
6358                 var record = new recordType(values, id);
6359                 record.node = n;
6360                 records[records.length] = record;
6361             }
6362
6363             return {
6364                 success : success,
6365                 records : records,
6366                 totalRecords : totalRecords || records.length
6367             };
6368     }
6369 });/*
6370  * Based on:
6371  * Ext JS Library 1.1.1
6372  * Copyright(c) 2006-2007, Ext JS, LLC.
6373  *
6374  * Originally Released Under LGPL - original licence link has changed is not relivant.
6375  *
6376  * Fork - LGPL
6377  * <script type="text/javascript">
6378  */
6379
6380 /**
6381  * @class Roo.data.ArrayReader
6382  * @extends Roo.data.DataReader
6383  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6384  * Each element of that Array represents a row of data fields. The
6385  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6386  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6387  * <p>
6388  * Example code:.
6389  * <pre><code>
6390 var RecordDef = Roo.data.Record.create([
6391     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6392     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6393 ]);
6394 var myReader = new Roo.data.ArrayReader({
6395     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6396 }, RecordDef);
6397 </code></pre>
6398  * <p>
6399  * This would consume an Array like this:
6400  * <pre><code>
6401 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6402   </code></pre>
6403  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6404  * @constructor
6405  * Create a new JsonReader
6406  * @param {Object} meta Metadata configuration options.
6407  * @param {Object} recordType Either an Array of field definition objects
6408  * as specified to {@link Roo.data.Record#create},
6409  * or an {@link Roo.data.Record} object
6410  * created using {@link Roo.data.Record#create}.
6411  */
6412 Roo.data.ArrayReader = function(meta, recordType){
6413     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6414 };
6415
6416 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6417     /**
6418      * Create a data block containing Roo.data.Records from an XML document.
6419      * @param {Object} o An Array of row objects which represents the dataset.
6420      * @return {Object} data A data block which is used by an Roo.data.Store object as
6421      * a cache of Roo.data.Records.
6422      */
6423     readRecords : function(o){
6424         var sid = this.meta ? this.meta.id : null;
6425         var recordType = this.recordType, fields = recordType.prototype.fields;
6426         var records = [];
6427         var root = o;
6428             for(var i = 0; i < root.length; i++){
6429                     var n = root[i];
6430                 var values = {};
6431                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6432                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6433                 var f = fields.items[j];
6434                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6435                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6436                 v = f.convert(v);
6437                 values[f.name] = v;
6438             }
6439                 var record = new recordType(values, id);
6440                 record.json = n;
6441                 records[records.length] = record;
6442             }
6443             return {
6444                 records : records,
6445                 totalRecords : records.length
6446             };
6447     }
6448 });/*
6449  * Based on:
6450  * Ext JS Library 1.1.1
6451  * Copyright(c) 2006-2007, Ext JS, LLC.
6452  *
6453  * Originally Released Under LGPL - original licence link has changed is not relivant.
6454  *
6455  * Fork - LGPL
6456  * <script type="text/javascript">
6457  */
6458
6459
6460 /**
6461  * @class Roo.data.Tree
6462  * @extends Roo.util.Observable
6463  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6464  * in the tree have most standard DOM functionality.
6465  * @constructor
6466  * @param {Node} root (optional) The root node
6467  */
6468 Roo.data.Tree = function(root){
6469    this.nodeHash = {};
6470    /**
6471     * The root node for this tree
6472     * @type Node
6473     */
6474    this.root = null;
6475    if(root){
6476        this.setRootNode(root);
6477    }
6478    this.addEvents({
6479        /**
6480         * @event append
6481         * Fires when a new child node is appended to a node in this tree.
6482         * @param {Tree} tree The owner tree
6483         * @param {Node} parent The parent node
6484         * @param {Node} node The newly appended node
6485         * @param {Number} index The index of the newly appended node
6486         */
6487        "append" : true,
6488        /**
6489         * @event remove
6490         * Fires when a child node is removed from a node in this tree.
6491         * @param {Tree} tree The owner tree
6492         * @param {Node} parent The parent node
6493         * @param {Node} node The child node removed
6494         */
6495        "remove" : true,
6496        /**
6497         * @event move
6498         * Fires when a node is moved to a new location in the tree
6499         * @param {Tree} tree The owner tree
6500         * @param {Node} node The node moved
6501         * @param {Node} oldParent The old parent of this node
6502         * @param {Node} newParent The new parent of this node
6503         * @param {Number} index The index it was moved to
6504         */
6505        "move" : true,
6506        /**
6507         * @event insert
6508         * Fires when a new child node is inserted in a node in this tree.
6509         * @param {Tree} tree The owner tree
6510         * @param {Node} parent The parent node
6511         * @param {Node} node The child node inserted
6512         * @param {Node} refNode The child node the node was inserted before
6513         */
6514        "insert" : true,
6515        /**
6516         * @event beforeappend
6517         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6518         * @param {Tree} tree The owner tree
6519         * @param {Node} parent The parent node
6520         * @param {Node} node The child node to be appended
6521         */
6522        "beforeappend" : true,
6523        /**
6524         * @event beforeremove
6525         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} parent The parent node
6528         * @param {Node} node The child node to be removed
6529         */
6530        "beforeremove" : true,
6531        /**
6532         * @event beforemove
6533         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} node The node being moved
6536         * @param {Node} oldParent The parent of the node
6537         * @param {Node} newParent The new parent the node is moving to
6538         * @param {Number} index The index it is being moved to
6539         */
6540        "beforemove" : true,
6541        /**
6542         * @event beforeinsert
6543         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6544         * @param {Tree} tree The owner tree
6545         * @param {Node} parent The parent node
6546         * @param {Node} node The child node to be inserted
6547         * @param {Node} refNode The child node the node is being inserted before
6548         */
6549        "beforeinsert" : true
6550    });
6551
6552     Roo.data.Tree.superclass.constructor.call(this);
6553 };
6554
6555 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6556     pathSeparator: "/",
6557
6558     proxyNodeEvent : function(){
6559         return this.fireEvent.apply(this, arguments);
6560     },
6561
6562     /**
6563      * Returns the root node for this tree.
6564      * @return {Node}
6565      */
6566     getRootNode : function(){
6567         return this.root;
6568     },
6569
6570     /**
6571      * Sets the root node for this tree.
6572      * @param {Node} node
6573      * @return {Node}
6574      */
6575     setRootNode : function(node){
6576         this.root = node;
6577         node.ownerTree = this;
6578         node.isRoot = true;
6579         this.registerNode(node);
6580         return node;
6581     },
6582
6583     /**
6584      * Gets a node in this tree by its id.
6585      * @param {String} id
6586      * @return {Node}
6587      */
6588     getNodeById : function(id){
6589         return this.nodeHash[id];
6590     },
6591
6592     registerNode : function(node){
6593         this.nodeHash[node.id] = node;
6594     },
6595
6596     unregisterNode : function(node){
6597         delete this.nodeHash[node.id];
6598     },
6599
6600     toString : function(){
6601         return "[Tree"+(this.id?" "+this.id:"")+"]";
6602     }
6603 });
6604
6605 /**
6606  * @class Roo.data.Node
6607  * @extends Roo.util.Observable
6608  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6609  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6610  * @constructor
6611  * @param {Object} attributes The attributes/config for the node
6612  */
6613 Roo.data.Node = function(attributes){
6614     /**
6615      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6616      * @type {Object}
6617      */
6618     this.attributes = attributes || {};
6619     this.leaf = this.attributes.leaf;
6620     /**
6621      * The node id. @type String
6622      */
6623     this.id = this.attributes.id;
6624     if(!this.id){
6625         this.id = Roo.id(null, "ynode-");
6626         this.attributes.id = this.id;
6627     }
6628     /**
6629      * All child nodes of this node. @type Array
6630      */
6631     this.childNodes = [];
6632     if(!this.childNodes.indexOf){ // indexOf is a must
6633         this.childNodes.indexOf = function(o){
6634             for(var i = 0, len = this.length; i < len; i++){
6635                 if(this[i] == o) {
6636                     return i;
6637                 }
6638             }
6639             return -1;
6640         };
6641     }
6642     /**
6643      * The parent node for this node. @type Node
6644      */
6645     this.parentNode = null;
6646     /**
6647      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6648      */
6649     this.firstChild = null;
6650     /**
6651      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6652      */
6653     this.lastChild = null;
6654     /**
6655      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6656      */
6657     this.previousSibling = null;
6658     /**
6659      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6660      */
6661     this.nextSibling = null;
6662
6663     this.addEvents({
6664        /**
6665         * @event append
6666         * Fires when a new child node is appended
6667         * @param {Tree} tree The owner tree
6668         * @param {Node} this This node
6669         * @param {Node} node The newly appended node
6670         * @param {Number} index The index of the newly appended node
6671         */
6672        "append" : true,
6673        /**
6674         * @event remove
6675         * Fires when a child node is removed
6676         * @param {Tree} tree The owner tree
6677         * @param {Node} this This node
6678         * @param {Node} node The removed node
6679         */
6680        "remove" : true,
6681        /**
6682         * @event move
6683         * Fires when this node is moved to a new location in the tree
6684         * @param {Tree} tree The owner tree
6685         * @param {Node} this This node
6686         * @param {Node} oldParent The old parent of this node
6687         * @param {Node} newParent The new parent of this node
6688         * @param {Number} index The index it was moved to
6689         */
6690        "move" : true,
6691        /**
6692         * @event insert
6693         * Fires when a new child node is inserted.
6694         * @param {Tree} tree The owner tree
6695         * @param {Node} this This node
6696         * @param {Node} node The child node inserted
6697         * @param {Node} refNode The child node the node was inserted before
6698         */
6699        "insert" : true,
6700        /**
6701         * @event beforeappend
6702         * Fires before a new child is appended, return false to cancel the append.
6703         * @param {Tree} tree The owner tree
6704         * @param {Node} this This node
6705         * @param {Node} node The child node to be appended
6706         */
6707        "beforeappend" : true,
6708        /**
6709         * @event beforeremove
6710         * Fires before a child is removed, return false to cancel the remove.
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} node The child node to be removed
6714         */
6715        "beforeremove" : true,
6716        /**
6717         * @event beforemove
6718         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6719         * @param {Tree} tree The owner tree
6720         * @param {Node} this This node
6721         * @param {Node} oldParent The parent of this node
6722         * @param {Node} newParent The new parent this node is moving to
6723         * @param {Number} index The index it is being moved to
6724         */
6725        "beforemove" : true,
6726        /**
6727         * @event beforeinsert
6728         * Fires before a new child is inserted, return false to cancel the insert.
6729         * @param {Tree} tree The owner tree
6730         * @param {Node} this This node
6731         * @param {Node} node The child node to be inserted
6732         * @param {Node} refNode The child node the node is being inserted before
6733         */
6734        "beforeinsert" : true
6735    });
6736     this.listeners = this.attributes.listeners;
6737     Roo.data.Node.superclass.constructor.call(this);
6738 };
6739
6740 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6741     fireEvent : function(evtName){
6742         // first do standard event for this node
6743         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6744             return false;
6745         }
6746         // then bubble it up to the tree if the event wasn't cancelled
6747         var ot = this.getOwnerTree();
6748         if(ot){
6749             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6750                 return false;
6751             }
6752         }
6753         return true;
6754     },
6755
6756     /**
6757      * Returns true if this node is a leaf
6758      * @return {Boolean}
6759      */
6760     isLeaf : function(){
6761         return this.leaf === true;
6762     },
6763
6764     // private
6765     setFirstChild : function(node){
6766         this.firstChild = node;
6767     },
6768
6769     //private
6770     setLastChild : function(node){
6771         this.lastChild = node;
6772     },
6773
6774
6775     /**
6776      * Returns true if this node is the last child of its parent
6777      * @return {Boolean}
6778      */
6779     isLast : function(){
6780        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6781     },
6782
6783     /**
6784      * Returns true if this node is the first child of its parent
6785      * @return {Boolean}
6786      */
6787     isFirst : function(){
6788        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6789     },
6790
6791     hasChildNodes : function(){
6792         return !this.isLeaf() && this.childNodes.length > 0;
6793     },
6794
6795     /**
6796      * Insert node(s) as the last child node of this node.
6797      * @param {Node/Array} node The node or Array of nodes to append
6798      * @return {Node} The appended node if single append, or null if an array was passed
6799      */
6800     appendChild : function(node){
6801         var multi = false;
6802         if(node instanceof Array){
6803             multi = node;
6804         }else if(arguments.length > 1){
6805             multi = arguments;
6806         }
6807         // if passed an array or multiple args do them one by one
6808         if(multi){
6809             for(var i = 0, len = multi.length; i < len; i++) {
6810                 this.appendChild(multi[i]);
6811             }
6812         }else{
6813             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6814                 return false;
6815             }
6816             var index = this.childNodes.length;
6817             var oldParent = node.parentNode;
6818             // it's a move, make sure we move it cleanly
6819             if(oldParent){
6820                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6821                     return false;
6822                 }
6823                 oldParent.removeChild(node);
6824             }
6825             index = this.childNodes.length;
6826             if(index == 0){
6827                 this.setFirstChild(node);
6828             }
6829             this.childNodes.push(node);
6830             node.parentNode = this;
6831             var ps = this.childNodes[index-1];
6832             if(ps){
6833                 node.previousSibling = ps;
6834                 ps.nextSibling = node;
6835             }else{
6836                 node.previousSibling = null;
6837             }
6838             node.nextSibling = null;
6839             this.setLastChild(node);
6840             node.setOwnerTree(this.getOwnerTree());
6841             this.fireEvent("append", this.ownerTree, this, node, index);
6842             if(oldParent){
6843                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6844             }
6845             return node;
6846         }
6847     },
6848
6849     /**
6850      * Removes a child node from this node.
6851      * @param {Node} node The node to remove
6852      * @return {Node} The removed node
6853      */
6854     removeChild : function(node){
6855         var index = this.childNodes.indexOf(node);
6856         if(index == -1){
6857             return false;
6858         }
6859         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6860             return false;
6861         }
6862
6863         // remove it from childNodes collection
6864         this.childNodes.splice(index, 1);
6865
6866         // update siblings
6867         if(node.previousSibling){
6868             node.previousSibling.nextSibling = node.nextSibling;
6869         }
6870         if(node.nextSibling){
6871             node.nextSibling.previousSibling = node.previousSibling;
6872         }
6873
6874         // update child refs
6875         if(this.firstChild == node){
6876             this.setFirstChild(node.nextSibling);
6877         }
6878         if(this.lastChild == node){
6879             this.setLastChild(node.previousSibling);
6880         }
6881
6882         node.setOwnerTree(null);
6883         // clear any references from the node
6884         node.parentNode = null;
6885         node.previousSibling = null;
6886         node.nextSibling = null;
6887         this.fireEvent("remove", this.ownerTree, this, node);
6888         return node;
6889     },
6890
6891     /**
6892      * Inserts the first node before the second node in this nodes childNodes collection.
6893      * @param {Node} node The node to insert
6894      * @param {Node} refNode The node to insert before (if null the node is appended)
6895      * @return {Node} The inserted node
6896      */
6897     insertBefore : function(node, refNode){
6898         if(!refNode){ // like standard Dom, refNode can be null for append
6899             return this.appendChild(node);
6900         }
6901         // nothing to do
6902         if(node == refNode){
6903             return false;
6904         }
6905
6906         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6907             return false;
6908         }
6909         var index = this.childNodes.indexOf(refNode);
6910         var oldParent = node.parentNode;
6911         var refIndex = index;
6912
6913         // when moving internally, indexes will change after remove
6914         if(oldParent == this && this.childNodes.indexOf(node) < index){
6915             refIndex--;
6916         }
6917
6918         // it's a move, make sure we move it cleanly
6919         if(oldParent){
6920             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6921                 return false;
6922             }
6923             oldParent.removeChild(node);
6924         }
6925         if(refIndex == 0){
6926             this.setFirstChild(node);
6927         }
6928         this.childNodes.splice(refIndex, 0, node);
6929         node.parentNode = this;
6930         var ps = this.childNodes[refIndex-1];
6931         if(ps){
6932             node.previousSibling = ps;
6933             ps.nextSibling = node;
6934         }else{
6935             node.previousSibling = null;
6936         }
6937         node.nextSibling = refNode;
6938         refNode.previousSibling = node;
6939         node.setOwnerTree(this.getOwnerTree());
6940         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6941         if(oldParent){
6942             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6943         }
6944         return node;
6945     },
6946
6947     /**
6948      * Returns the child node at the specified index.
6949      * @param {Number} index
6950      * @return {Node}
6951      */
6952     item : function(index){
6953         return this.childNodes[index];
6954     },
6955
6956     /**
6957      * Replaces one child node in this node with another.
6958      * @param {Node} newChild The replacement node
6959      * @param {Node} oldChild The node to replace
6960      * @return {Node} The replaced node
6961      */
6962     replaceChild : function(newChild, oldChild){
6963         this.insertBefore(newChild, oldChild);
6964         this.removeChild(oldChild);
6965         return oldChild;
6966     },
6967
6968     /**
6969      * Returns the index of a child node
6970      * @param {Node} node
6971      * @return {Number} The index of the node or -1 if it was not found
6972      */
6973     indexOf : function(child){
6974         return this.childNodes.indexOf(child);
6975     },
6976
6977     /**
6978      * Returns the tree this node is in.
6979      * @return {Tree}
6980      */
6981     getOwnerTree : function(){
6982         // if it doesn't have one, look for one
6983         if(!this.ownerTree){
6984             var p = this;
6985             while(p){
6986                 if(p.ownerTree){
6987                     this.ownerTree = p.ownerTree;
6988                     break;
6989                 }
6990                 p = p.parentNode;
6991             }
6992         }
6993         return this.ownerTree;
6994     },
6995
6996     /**
6997      * Returns depth of this node (the root node has a depth of 0)
6998      * @return {Number}
6999      */
7000     getDepth : function(){
7001         var depth = 0;
7002         var p = this;
7003         while(p.parentNode){
7004             ++depth;
7005             p = p.parentNode;
7006         }
7007         return depth;
7008     },
7009
7010     // private
7011     setOwnerTree : function(tree){
7012         // if it's move, we need to update everyone
7013         if(tree != this.ownerTree){
7014             if(this.ownerTree){
7015                 this.ownerTree.unregisterNode(this);
7016             }
7017             this.ownerTree = tree;
7018             var cs = this.childNodes;
7019             for(var i = 0, len = cs.length; i < len; i++) {
7020                 cs[i].setOwnerTree(tree);
7021             }
7022             if(tree){
7023                 tree.registerNode(this);
7024             }
7025         }
7026     },
7027
7028     /**
7029      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7030      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7031      * @return {String} The path
7032      */
7033     getPath : function(attr){
7034         attr = attr || "id";
7035         var p = this.parentNode;
7036         var b = [this.attributes[attr]];
7037         while(p){
7038             b.unshift(p.attributes[attr]);
7039             p = p.parentNode;
7040         }
7041         var sep = this.getOwnerTree().pathSeparator;
7042         return sep + b.join(sep);
7043     },
7044
7045     /**
7046      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7047      * function call will be the scope provided or the current node. The arguments to the function
7048      * will be the args provided or the current node. If the function returns false at any point,
7049      * the bubble is stopped.
7050      * @param {Function} fn The function to call
7051      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7052      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7053      */
7054     bubble : function(fn, scope, args){
7055         var p = this;
7056         while(p){
7057             if(fn.call(scope || p, args || p) === false){
7058                 break;
7059             }
7060             p = p.parentNode;
7061         }
7062     },
7063
7064     /**
7065      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7066      * function call will be the scope provided or the current node. The arguments to the function
7067      * will be the args provided or the current node. If the function returns false at any point,
7068      * the cascade is stopped on that branch.
7069      * @param {Function} fn The function to call
7070      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7071      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7072      */
7073     cascade : function(fn, scope, args){
7074         if(fn.call(scope || this, args || this) !== false){
7075             var cs = this.childNodes;
7076             for(var i = 0, len = cs.length; i < len; i++) {
7077                 cs[i].cascade(fn, scope, args);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7084      * function call will be the scope provided or the current node. The arguments to the function
7085      * will be the args provided or the current node. If the function returns false at any point,
7086      * the iteration stops.
7087      * @param {Function} fn The function to call
7088      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7089      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7090      */
7091     eachChild : function(fn, scope, args){
7092         var cs = this.childNodes;
7093         for(var i = 0, len = cs.length; i < len; i++) {
7094                 if(fn.call(scope || this, args || cs[i]) === false){
7095                     break;
7096                 }
7097         }
7098     },
7099
7100     /**
7101      * Finds the first child that has the attribute with the specified value.
7102      * @param {String} attribute The attribute name
7103      * @param {Mixed} value The value to search for
7104      * @return {Node} The found child or null if none was found
7105      */
7106     findChild : function(attribute, value){
7107         var cs = this.childNodes;
7108         for(var i = 0, len = cs.length; i < len; i++) {
7109                 if(cs[i].attributes[attribute] == value){
7110                     return cs[i];
7111                 }
7112         }
7113         return null;
7114     },
7115
7116     /**
7117      * Finds the first child by a custom function. The child matches if the function passed
7118      * returns true.
7119      * @param {Function} fn
7120      * @param {Object} scope (optional)
7121      * @return {Node} The found child or null if none was found
7122      */
7123     findChildBy : function(fn, scope){
7124         var cs = this.childNodes;
7125         for(var i = 0, len = cs.length; i < len; i++) {
7126                 if(fn.call(scope||cs[i], cs[i]) === true){
7127                     return cs[i];
7128                 }
7129         }
7130         return null;
7131     },
7132
7133     /**
7134      * Sorts this nodes children using the supplied sort function
7135      * @param {Function} fn
7136      * @param {Object} scope (optional)
7137      */
7138     sort : function(fn, scope){
7139         var cs = this.childNodes;
7140         var len = cs.length;
7141         if(len > 0){
7142             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7143             cs.sort(sortFn);
7144             for(var i = 0; i < len; i++){
7145                 var n = cs[i];
7146                 n.previousSibling = cs[i-1];
7147                 n.nextSibling = cs[i+1];
7148                 if(i == 0){
7149                     this.setFirstChild(n);
7150                 }
7151                 if(i == len-1){
7152                     this.setLastChild(n);
7153                 }
7154             }
7155         }
7156     },
7157
7158     /**
7159      * Returns true if this node is an ancestor (at any point) of the passed node.
7160      * @param {Node} node
7161      * @return {Boolean}
7162      */
7163     contains : function(node){
7164         return node.isAncestor(this);
7165     },
7166
7167     /**
7168      * Returns true if the passed node is an ancestor (at any point) of this node.
7169      * @param {Node} node
7170      * @return {Boolean}
7171      */
7172     isAncestor : function(node){
7173         var p = this.parentNode;
7174         while(p){
7175             if(p == node){
7176                 return true;
7177             }
7178             p = p.parentNode;
7179         }
7180         return false;
7181     },
7182
7183     toString : function(){
7184         return "[Node"+(this.id?" "+this.id:"")+"]";
7185     }
7186 });/*
7187  * Based on:
7188  * Ext JS Library 1.1.1
7189  * Copyright(c) 2006-2007, Ext JS, LLC.
7190  *
7191  * Originally Released Under LGPL - original licence link has changed is not relivant.
7192  *
7193  * Fork - LGPL
7194  * <script type="text/javascript">
7195  */
7196  
7197
7198 /**
7199  * @class Roo.ComponentMgr
7200  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7201  * @singleton
7202  */
7203 Roo.ComponentMgr = function(){
7204     var all = new Roo.util.MixedCollection();
7205
7206     return {
7207         /**
7208          * Registers a component.
7209          * @param {Roo.Component} c The component
7210          */
7211         register : function(c){
7212             all.add(c);
7213         },
7214
7215         /**
7216          * Unregisters a component.
7217          * @param {Roo.Component} c The component
7218          */
7219         unregister : function(c){
7220             all.remove(c);
7221         },
7222
7223         /**
7224          * Returns a component by id
7225          * @param {String} id The component id
7226          */
7227         get : function(id){
7228             return all.get(id);
7229         },
7230
7231         /**
7232          * Registers a function that will be called when a specified component is added to ComponentMgr
7233          * @param {String} id The component id
7234          * @param {Funtction} fn The callback function
7235          * @param {Object} scope The scope of the callback
7236          */
7237         onAvailable : function(id, fn, scope){
7238             all.on("add", function(index, o){
7239                 if(o.id == id){
7240                     fn.call(scope || o, o);
7241                     all.un("add", fn, scope);
7242                 }
7243             });
7244         }
7245     };
7246 }();/*
7247  * Based on:
7248  * Ext JS Library 1.1.1
7249  * Copyright(c) 2006-2007, Ext JS, LLC.
7250  *
7251  * Originally Released Under LGPL - original licence link has changed is not relivant.
7252  *
7253  * Fork - LGPL
7254  * <script type="text/javascript">
7255  */
7256  
7257 /**
7258  * @class Roo.Component
7259  * @extends Roo.util.Observable
7260  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7261  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7262  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7263  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7264  * All visual components (widgets) that require rendering into a layout should subclass Component.
7265  * @constructor
7266  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7267  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
7268  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7269  */
7270 Roo.Component = function(config){
7271     config = config || {};
7272     if(config.tagName || config.dom || typeof config == "string"){ // element object
7273         config = {el: config, id: config.id || config};
7274     }
7275     this.initialConfig = config;
7276
7277     Roo.apply(this, config);
7278     this.addEvents({
7279         /**
7280          * @event disable
7281          * Fires after the component is disabled.
7282              * @param {Roo.Component} this
7283              */
7284         disable : true,
7285         /**
7286          * @event enable
7287          * Fires after the component is enabled.
7288              * @param {Roo.Component} this
7289              */
7290         enable : true,
7291         /**
7292          * @event beforeshow
7293          * Fires before the component is shown.  Return false to stop the show.
7294              * @param {Roo.Component} this
7295              */
7296         beforeshow : true,
7297         /**
7298          * @event show
7299          * Fires after the component is shown.
7300              * @param {Roo.Component} this
7301              */
7302         show : true,
7303         /**
7304          * @event beforehide
7305          * Fires before the component is hidden. Return false to stop the hide.
7306              * @param {Roo.Component} this
7307              */
7308         beforehide : true,
7309         /**
7310          * @event hide
7311          * Fires after the component is hidden.
7312              * @param {Roo.Component} this
7313              */
7314         hide : true,
7315         /**
7316          * @event beforerender
7317          * Fires before the component is rendered. Return false to stop the render.
7318              * @param {Roo.Component} this
7319              */
7320         beforerender : true,
7321         /**
7322          * @event render
7323          * Fires after the component is rendered.
7324              * @param {Roo.Component} this
7325              */
7326         render : true,
7327         /**
7328          * @event beforedestroy
7329          * Fires before the component is destroyed. Return false to stop the destroy.
7330              * @param {Roo.Component} this
7331              */
7332         beforedestroy : true,
7333         /**
7334          * @event destroy
7335          * Fires after the component is destroyed.
7336              * @param {Roo.Component} this
7337              */
7338         destroy : true
7339     });
7340     if(!this.id){
7341         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7342     }
7343     Roo.ComponentMgr.register(this);
7344     Roo.Component.superclass.constructor.call(this);
7345     this.initComponent();
7346     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7347         this.render(this.renderTo);
7348         delete this.renderTo;
7349     }
7350 };
7351
7352 // private
7353 Roo.Component.AUTO_ID = 1000;
7354
7355 Roo.extend(Roo.Component, Roo.util.Observable, {
7356     /**
7357      * @property {Boolean} hidden
7358      * true if this component is hidden. Read-only.
7359      */
7360     hidden : false,
7361     /**
7362      * true if this component is disabled. Read-only.
7363      */
7364     disabled : false,
7365     /**
7366      * true if this component has been rendered. Read-only.
7367      */
7368     rendered : false,
7369     
7370     /** @cfg {String} disableClass
7371      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7372      */
7373     disabledClass : "x-item-disabled",
7374         /** @cfg {Boolean} allowDomMove
7375          * Whether the component can move the Dom node when rendering (defaults to true).
7376          */
7377     allowDomMove : true,
7378     /** @cfg {String} hideMode
7379      * How this component should hidden. Supported values are
7380      * "visibility" (css visibility), "offsets" (negative offset position) and
7381      * "display" (css display) - defaults to "display".
7382      */
7383     hideMode: 'display',
7384
7385     // private
7386     ctype : "Roo.Component",
7387
7388     /** @cfg {String} actionMode 
7389      * which property holds the element that used for  hide() / show() / disable() / enable()
7390      * default is 'el' 
7391      */
7392     actionMode : "el",
7393
7394     // private
7395     getActionEl : function(){
7396         return this[this.actionMode];
7397     },
7398
7399     initComponent : Roo.emptyFn,
7400     /**
7401      * If this is a lazy rendering component, render it to its container element.
7402      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
7403      */
7404     render : function(container, position){
7405         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7406             if(!container && this.el){
7407                 this.el = Roo.get(this.el);
7408                 container = this.el.dom.parentNode;
7409                 this.allowDomMove = false;
7410             }
7411             this.container = Roo.get(container);
7412             this.rendered = true;
7413             if(position !== undefined){
7414                 if(typeof position == 'number'){
7415                     position = this.container.dom.childNodes[position];
7416                 }else{
7417                     position = Roo.getDom(position);
7418                 }
7419             }
7420             this.onRender(this.container, position || null);
7421             if(this.cls){
7422                 this.el.addClass(this.cls);
7423                 delete this.cls;
7424             }
7425             if(this.style){
7426                 this.el.applyStyles(this.style);
7427                 delete this.style;
7428             }
7429             this.fireEvent("render", this);
7430             this.afterRender(this.container);
7431             if(this.hidden){
7432                 this.hide();
7433             }
7434             if(this.disabled){
7435                 this.disable();
7436             }
7437         }
7438         return this;
7439     },
7440
7441     // private
7442     // default function is not really useful
7443     onRender : function(ct, position){
7444         if(this.el){
7445             this.el = Roo.get(this.el);
7446             if(this.allowDomMove !== false){
7447                 ct.dom.insertBefore(this.el.dom, position);
7448             }
7449         }
7450     },
7451
7452     // private
7453     getAutoCreate : function(){
7454         var cfg = typeof this.autoCreate == "object" ?
7455                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7456         if(this.id && !cfg.id){
7457             cfg.id = this.id;
7458         }
7459         return cfg;
7460     },
7461
7462     // private
7463     afterRender : Roo.emptyFn,
7464
7465     /**
7466      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7467      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7468      */
7469     destroy : function(){
7470         if(this.fireEvent("beforedestroy", this) !== false){
7471             this.purgeListeners();
7472             this.beforeDestroy();
7473             if(this.rendered){
7474                 this.el.removeAllListeners();
7475                 this.el.remove();
7476                 if(this.actionMode == "container"){
7477                     this.container.remove();
7478                 }
7479             }
7480             this.onDestroy();
7481             Roo.ComponentMgr.unregister(this);
7482             this.fireEvent("destroy", this);
7483         }
7484     },
7485
7486         // private
7487     beforeDestroy : function(){
7488
7489     },
7490
7491         // private
7492         onDestroy : function(){
7493
7494     },
7495
7496     /**
7497      * Returns the underlying {@link Roo.Element}.
7498      * @return {Roo.Element} The element
7499      */
7500     getEl : function(){
7501         return this.el;
7502     },
7503
7504     /**
7505      * Returns the id of this component.
7506      * @return {String}
7507      */
7508     getId : function(){
7509         return this.id;
7510     },
7511
7512     /**
7513      * Try to focus this component.
7514      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7515      * @return {Roo.Component} this
7516      */
7517     focus : function(selectText){
7518         if(this.rendered){
7519             this.el.focus();
7520             if(selectText === true){
7521                 this.el.dom.select();
7522             }
7523         }
7524         return this;
7525     },
7526
7527     // private
7528     blur : function(){
7529         if(this.rendered){
7530             this.el.blur();
7531         }
7532         return this;
7533     },
7534
7535     /**
7536      * Disable this component.
7537      * @return {Roo.Component} this
7538      */
7539     disable : function(){
7540         if(this.rendered){
7541             this.onDisable();
7542         }
7543         this.disabled = true;
7544         this.fireEvent("disable", this);
7545         return this;
7546     },
7547
7548         // private
7549     onDisable : function(){
7550         this.getActionEl().addClass(this.disabledClass);
7551         this.el.dom.disabled = true;
7552     },
7553
7554     /**
7555      * Enable this component.
7556      * @return {Roo.Component} this
7557      */
7558     enable : function(){
7559         if(this.rendered){
7560             this.onEnable();
7561         }
7562         this.disabled = false;
7563         this.fireEvent("enable", this);
7564         return this;
7565     },
7566
7567         // private
7568     onEnable : function(){
7569         this.getActionEl().removeClass(this.disabledClass);
7570         this.el.dom.disabled = false;
7571     },
7572
7573     /**
7574      * Convenience function for setting disabled/enabled by boolean.
7575      * @param {Boolean} disabled
7576      */
7577     setDisabled : function(disabled){
7578         this[disabled ? "disable" : "enable"]();
7579     },
7580
7581     /**
7582      * Show this component.
7583      * @return {Roo.Component} this
7584      */
7585     show: function(){
7586         if(this.fireEvent("beforeshow", this) !== false){
7587             this.hidden = false;
7588             if(this.rendered){
7589                 this.onShow();
7590             }
7591             this.fireEvent("show", this);
7592         }
7593         return this;
7594     },
7595
7596     // private
7597     onShow : function(){
7598         var ae = this.getActionEl();
7599         if(this.hideMode == 'visibility'){
7600             ae.dom.style.visibility = "visible";
7601         }else if(this.hideMode == 'offsets'){
7602             ae.removeClass('x-hidden');
7603         }else{
7604             ae.dom.style.display = "";
7605         }
7606     },
7607
7608     /**
7609      * Hide this component.
7610      * @return {Roo.Component} this
7611      */
7612     hide: function(){
7613         if(this.fireEvent("beforehide", this) !== false){
7614             this.hidden = true;
7615             if(this.rendered){
7616                 this.onHide();
7617             }
7618             this.fireEvent("hide", this);
7619         }
7620         return this;
7621     },
7622
7623     // private
7624     onHide : function(){
7625         var ae = this.getActionEl();
7626         if(this.hideMode == 'visibility'){
7627             ae.dom.style.visibility = "hidden";
7628         }else if(this.hideMode == 'offsets'){
7629             ae.addClass('x-hidden');
7630         }else{
7631             ae.dom.style.display = "none";
7632         }
7633     },
7634
7635     /**
7636      * Convenience function to hide or show this component by boolean.
7637      * @param {Boolean} visible True to show, false to hide
7638      * @return {Roo.Component} this
7639      */
7640     setVisible: function(visible){
7641         if(visible) {
7642             this.show();
7643         }else{
7644             this.hide();
7645         }
7646         return this;
7647     },
7648
7649     /**
7650      * Returns true if this component is visible.
7651      */
7652     isVisible : function(){
7653         return this.getActionEl().isVisible();
7654     },
7655
7656     cloneConfig : function(overrides){
7657         overrides = overrides || {};
7658         var id = overrides.id || Roo.id();
7659         var cfg = Roo.applyIf(overrides, this.initialConfig);
7660         cfg.id = id; // prevent dup id
7661         return new this.constructor(cfg);
7662     }
7663 });/*
7664  * Based on:
7665  * Ext JS Library 1.1.1
7666  * Copyright(c) 2006-2007, Ext JS, LLC.
7667  *
7668  * Originally Released Under LGPL - original licence link has changed is not relivant.
7669  *
7670  * Fork - LGPL
7671  * <script type="text/javascript">
7672  */
7673  (function(){ 
7674 /**
7675  * @class Roo.Layer
7676  * @extends Roo.Element
7677  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7678  * automatic maintaining of shadow/shim positions.
7679  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7680  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7681  * you can pass a string with a CSS class name. False turns off the shadow.
7682  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7683  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7684  * @cfg {String} cls CSS class to add to the element
7685  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7686  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7687  * @constructor
7688  * @param {Object} config An object with config options.
7689  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7690  */
7691
7692 Roo.Layer = function(config, existingEl){
7693     config = config || {};
7694     var dh = Roo.DomHelper;
7695     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7696     if(existingEl){
7697         this.dom = Roo.getDom(existingEl);
7698     }
7699     if(!this.dom){
7700         var o = config.dh || {tag: "div", cls: "x-layer"};
7701         this.dom = dh.append(pel, o);
7702     }
7703     if(config.cls){
7704         this.addClass(config.cls);
7705     }
7706     this.constrain = config.constrain !== false;
7707     this.visibilityMode = Roo.Element.VISIBILITY;
7708     if(config.id){
7709         this.id = this.dom.id = config.id;
7710     }else{
7711         this.id = Roo.id(this.dom);
7712     }
7713     this.zindex = config.zindex || this.getZIndex();
7714     this.position("absolute", this.zindex);
7715     if(config.shadow){
7716         this.shadowOffset = config.shadowOffset || 4;
7717         this.shadow = new Roo.Shadow({
7718             offset : this.shadowOffset,
7719             mode : config.shadow
7720         });
7721     }else{
7722         this.shadowOffset = 0;
7723     }
7724     this.useShim = config.shim !== false && Roo.useShims;
7725     this.useDisplay = config.useDisplay;
7726     this.hide();
7727 };
7728
7729 var supr = Roo.Element.prototype;
7730
7731 // shims are shared among layer to keep from having 100 iframes
7732 var shims = [];
7733
7734 Roo.extend(Roo.Layer, Roo.Element, {
7735
7736     getZIndex : function(){
7737         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7738     },
7739
7740     getShim : function(){
7741         if(!this.useShim){
7742             return null;
7743         }
7744         if(this.shim){
7745             return this.shim;
7746         }
7747         var shim = shims.shift();
7748         if(!shim){
7749             shim = this.createShim();
7750             shim.enableDisplayMode('block');
7751             shim.dom.style.display = 'none';
7752             shim.dom.style.visibility = 'visible';
7753         }
7754         var pn = this.dom.parentNode;
7755         if(shim.dom.parentNode != pn){
7756             pn.insertBefore(shim.dom, this.dom);
7757         }
7758         shim.setStyle('z-index', this.getZIndex()-2);
7759         this.shim = shim;
7760         return shim;
7761     },
7762
7763     hideShim : function(){
7764         if(this.shim){
7765             this.shim.setDisplayed(false);
7766             shims.push(this.shim);
7767             delete this.shim;
7768         }
7769     },
7770
7771     disableShadow : function(){
7772         if(this.shadow){
7773             this.shadowDisabled = true;
7774             this.shadow.hide();
7775             this.lastShadowOffset = this.shadowOffset;
7776             this.shadowOffset = 0;
7777         }
7778     },
7779
7780     enableShadow : function(show){
7781         if(this.shadow){
7782             this.shadowDisabled = false;
7783             this.shadowOffset = this.lastShadowOffset;
7784             delete this.lastShadowOffset;
7785             if(show){
7786                 this.sync(true);
7787             }
7788         }
7789     },
7790
7791     // private
7792     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7793     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7794     sync : function(doShow){
7795         var sw = this.shadow;
7796         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7797             var sh = this.getShim();
7798
7799             var w = this.getWidth(),
7800                 h = this.getHeight();
7801
7802             var l = this.getLeft(true),
7803                 t = this.getTop(true);
7804
7805             if(sw && !this.shadowDisabled){
7806                 if(doShow && !sw.isVisible()){
7807                     sw.show(this);
7808                 }else{
7809                     sw.realign(l, t, w, h);
7810                 }
7811                 if(sh){
7812                     if(doShow){
7813                        sh.show();
7814                     }
7815                     // fit the shim behind the shadow, so it is shimmed too
7816                     var a = sw.adjusts, s = sh.dom.style;
7817                     s.left = (Math.min(l, l+a.l))+"px";
7818                     s.top = (Math.min(t, t+a.t))+"px";
7819                     s.width = (w+a.w)+"px";
7820                     s.height = (h+a.h)+"px";
7821                 }
7822             }else if(sh){
7823                 if(doShow){
7824                    sh.show();
7825                 }
7826                 sh.setSize(w, h);
7827                 sh.setLeftTop(l, t);
7828             }
7829             
7830         }
7831     },
7832
7833     // private
7834     destroy : function(){
7835         this.hideShim();
7836         if(this.shadow){
7837             this.shadow.hide();
7838         }
7839         this.removeAllListeners();
7840         var pn = this.dom.parentNode;
7841         if(pn){
7842             pn.removeChild(this.dom);
7843         }
7844         Roo.Element.uncache(this.id);
7845     },
7846
7847     remove : function(){
7848         this.destroy();
7849     },
7850
7851     // private
7852     beginUpdate : function(){
7853         this.updating = true;
7854     },
7855
7856     // private
7857     endUpdate : function(){
7858         this.updating = false;
7859         this.sync(true);
7860     },
7861
7862     // private
7863     hideUnders : function(negOffset){
7864         if(this.shadow){
7865             this.shadow.hide();
7866         }
7867         this.hideShim();
7868     },
7869
7870     // private
7871     constrainXY : function(){
7872         if(this.constrain){
7873             var vw = Roo.lib.Dom.getViewWidth(),
7874                 vh = Roo.lib.Dom.getViewHeight();
7875             var s = Roo.get(document).getScroll();
7876
7877             var xy = this.getXY();
7878             var x = xy[0], y = xy[1];   
7879             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7880             // only move it if it needs it
7881             var moved = false;
7882             // first validate right/bottom
7883             if((x + w) > vw+s.left){
7884                 x = vw - w - this.shadowOffset;
7885                 moved = true;
7886             }
7887             if((y + h) > vh+s.top){
7888                 y = vh - h - this.shadowOffset;
7889                 moved = true;
7890             }
7891             // then make sure top/left isn't negative
7892             if(x < s.left){
7893                 x = s.left;
7894                 moved = true;
7895             }
7896             if(y < s.top){
7897                 y = s.top;
7898                 moved = true;
7899             }
7900             if(moved){
7901                 if(this.avoidY){
7902                     var ay = this.avoidY;
7903                     if(y <= ay && (y+h) >= ay){
7904                         y = ay-h-5;   
7905                     }
7906                 }
7907                 xy = [x, y];
7908                 this.storeXY(xy);
7909                 supr.setXY.call(this, xy);
7910                 this.sync();
7911             }
7912         }
7913     },
7914
7915     isVisible : function(){
7916         return this.visible;    
7917     },
7918
7919     // private
7920     showAction : function(){
7921         this.visible = true; // track visibility to prevent getStyle calls
7922         if(this.useDisplay === true){
7923             this.setDisplayed("");
7924         }else if(this.lastXY){
7925             supr.setXY.call(this, this.lastXY);
7926         }else if(this.lastLT){
7927             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7928         }
7929     },
7930
7931     // private
7932     hideAction : function(){
7933         this.visible = false;
7934         if(this.useDisplay === true){
7935             this.setDisplayed(false);
7936         }else{
7937             this.setLeftTop(-10000,-10000);
7938         }
7939     },
7940
7941     // overridden Element method
7942     setVisible : function(v, a, d, c, e){
7943         if(v){
7944             this.showAction();
7945         }
7946         if(a && v){
7947             var cb = function(){
7948                 this.sync(true);
7949                 if(c){
7950                     c();
7951                 }
7952             }.createDelegate(this);
7953             supr.setVisible.call(this, true, true, d, cb, e);
7954         }else{
7955             if(!v){
7956                 this.hideUnders(true);
7957             }
7958             var cb = c;
7959             if(a){
7960                 cb = function(){
7961                     this.hideAction();
7962                     if(c){
7963                         c();
7964                     }
7965                 }.createDelegate(this);
7966             }
7967             supr.setVisible.call(this, v, a, d, cb, e);
7968             if(v){
7969                 this.sync(true);
7970             }else if(!a){
7971                 this.hideAction();
7972             }
7973         }
7974     },
7975
7976     storeXY : function(xy){
7977         delete this.lastLT;
7978         this.lastXY = xy;
7979     },
7980
7981     storeLeftTop : function(left, top){
7982         delete this.lastXY;
7983         this.lastLT = [left, top];
7984     },
7985
7986     // private
7987     beforeFx : function(){
7988         this.beforeAction();
7989         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
7990     },
7991
7992     // private
7993     afterFx : function(){
7994         Roo.Layer.superclass.afterFx.apply(this, arguments);
7995         this.sync(this.isVisible());
7996     },
7997
7998     // private
7999     beforeAction : function(){
8000         if(!this.updating && this.shadow){
8001             this.shadow.hide();
8002         }
8003     },
8004
8005     // overridden Element method
8006     setLeft : function(left){
8007         this.storeLeftTop(left, this.getTop(true));
8008         supr.setLeft.apply(this, arguments);
8009         this.sync();
8010     },
8011
8012     setTop : function(top){
8013         this.storeLeftTop(this.getLeft(true), top);
8014         supr.setTop.apply(this, arguments);
8015         this.sync();
8016     },
8017
8018     setLeftTop : function(left, top){
8019         this.storeLeftTop(left, top);
8020         supr.setLeftTop.apply(this, arguments);
8021         this.sync();
8022     },
8023
8024     setXY : function(xy, a, d, c, e){
8025         this.fixDisplay();
8026         this.beforeAction();
8027         this.storeXY(xy);
8028         var cb = this.createCB(c);
8029         supr.setXY.call(this, xy, a, d, cb, e);
8030         if(!a){
8031             cb();
8032         }
8033     },
8034
8035     // private
8036     createCB : function(c){
8037         var el = this;
8038         return function(){
8039             el.constrainXY();
8040             el.sync(true);
8041             if(c){
8042                 c();
8043             }
8044         };
8045     },
8046
8047     // overridden Element method
8048     setX : function(x, a, d, c, e){
8049         this.setXY([x, this.getY()], a, d, c, e);
8050     },
8051
8052     // overridden Element method
8053     setY : function(y, a, d, c, e){
8054         this.setXY([this.getX(), y], a, d, c, e);
8055     },
8056
8057     // overridden Element method
8058     setSize : function(w, h, a, d, c, e){
8059         this.beforeAction();
8060         var cb = this.createCB(c);
8061         supr.setSize.call(this, w, h, a, d, cb, e);
8062         if(!a){
8063             cb();
8064         }
8065     },
8066
8067     // overridden Element method
8068     setWidth : function(w, a, d, c, e){
8069         this.beforeAction();
8070         var cb = this.createCB(c);
8071         supr.setWidth.call(this, w, a, d, cb, e);
8072         if(!a){
8073             cb();
8074         }
8075     },
8076
8077     // overridden Element method
8078     setHeight : function(h, a, d, c, e){
8079         this.beforeAction();
8080         var cb = this.createCB(c);
8081         supr.setHeight.call(this, h, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // overridden Element method
8088     setBounds : function(x, y, w, h, a, d, c, e){
8089         this.beforeAction();
8090         var cb = this.createCB(c);
8091         if(!a){
8092             this.storeXY([x, y]);
8093             supr.setXY.call(this, [x, y]);
8094             supr.setSize.call(this, w, h, a, d, cb, e);
8095             cb();
8096         }else{
8097             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8098         }
8099         return this;
8100     },
8101     
8102     /**
8103      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8104      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8105      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8106      * @param {Number} zindex The new z-index to set
8107      * @return {this} The Layer
8108      */
8109     setZIndex : function(zindex){
8110         this.zindex = zindex;
8111         this.setStyle("z-index", zindex + 2);
8112         if(this.shadow){
8113             this.shadow.setZIndex(zindex + 1);
8114         }
8115         if(this.shim){
8116             this.shim.setStyle("z-index", zindex);
8117         }
8118     }
8119 });
8120 })();/*
8121  * Based on:
8122  * Ext JS Library 1.1.1
8123  * Copyright(c) 2006-2007, Ext JS, LLC.
8124  *
8125  * Originally Released Under LGPL - original licence link has changed is not relivant.
8126  *
8127  * Fork - LGPL
8128  * <script type="text/javascript">
8129  */
8130
8131
8132 /**
8133  * @class Roo.Shadow
8134  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8135  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8136  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8137  * @constructor
8138  * Create a new Shadow
8139  * @param {Object} config The config object
8140  */
8141 Roo.Shadow = function(config){
8142     Roo.apply(this, config);
8143     if(typeof this.mode != "string"){
8144         this.mode = this.defaultMode;
8145     }
8146     var o = this.offset, a = {h: 0};
8147     var rad = Math.floor(this.offset/2);
8148     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8149         case "drop":
8150             a.w = 0;
8151             a.l = a.t = o;
8152             a.t -= 1;
8153             if(Roo.isIE){
8154                 a.l -= this.offset + rad;
8155                 a.t -= this.offset + rad;
8156                 a.w -= rad;
8157                 a.h -= rad;
8158                 a.t += 1;
8159             }
8160         break;
8161         case "sides":
8162             a.w = (o*2);
8163             a.l = -o;
8164             a.t = o-1;
8165             if(Roo.isIE){
8166                 a.l -= (this.offset - rad);
8167                 a.t -= this.offset + rad;
8168                 a.l += 1;
8169                 a.w -= (this.offset - rad)*2;
8170                 a.w -= rad + 1;
8171                 a.h -= 1;
8172             }
8173         break;
8174         case "frame":
8175             a.w = a.h = (o*2);
8176             a.l = a.t = -o;
8177             a.t += 1;
8178             a.h -= 2;
8179             if(Roo.isIE){
8180                 a.l -= (this.offset - rad);
8181                 a.t -= (this.offset - rad);
8182                 a.l += 1;
8183                 a.w -= (this.offset + rad + 1);
8184                 a.h -= (this.offset + rad);
8185                 a.h += 1;
8186             }
8187         break;
8188     };
8189
8190     this.adjusts = a;
8191 };
8192
8193 Roo.Shadow.prototype = {
8194     /**
8195      * @cfg {String} mode
8196      * The shadow display mode.  Supports the following options:<br />
8197      * sides: Shadow displays on both sides and bottom only<br />
8198      * frame: Shadow displays equally on all four sides<br />
8199      * drop: Traditional bottom-right drop shadow (default)
8200      */
8201     /**
8202      * @cfg {String} offset
8203      * The number of pixels to offset the shadow from the element (defaults to 4)
8204      */
8205     offset: 4,
8206
8207     // private
8208     defaultMode: "drop",
8209
8210     /**
8211      * Displays the shadow under the target element
8212      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8213      */
8214     show : function(target){
8215         target = Roo.get(target);
8216         if(!this.el){
8217             this.el = Roo.Shadow.Pool.pull();
8218             if(this.el.dom.nextSibling != target.dom){
8219                 this.el.insertBefore(target);
8220             }
8221         }
8222         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8223         if(Roo.isIE){
8224             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8225         }
8226         this.realign(
8227             target.getLeft(true),
8228             target.getTop(true),
8229             target.getWidth(),
8230             target.getHeight()
8231         );
8232         this.el.dom.style.display = "block";
8233     },
8234
8235     /**
8236      * Returns true if the shadow is visible, else false
8237      */
8238     isVisible : function(){
8239         return this.el ? true : false;  
8240     },
8241
8242     /**
8243      * Direct alignment when values are already available. Show must be called at least once before
8244      * calling this method to ensure it is initialized.
8245      * @param {Number} left The target element left position
8246      * @param {Number} top The target element top position
8247      * @param {Number} width The target element width
8248      * @param {Number} height The target element height
8249      */
8250     realign : function(l, t, w, h){
8251         if(!this.el){
8252             return;
8253         }
8254         var a = this.adjusts, d = this.el.dom, s = d.style;
8255         var iea = 0;
8256         s.left = (l+a.l)+"px";
8257         s.top = (t+a.t)+"px";
8258         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8259  
8260         if(s.width != sws || s.height != shs){
8261             s.width = sws;
8262             s.height = shs;
8263             if(!Roo.isIE){
8264                 var cn = d.childNodes;
8265                 var sww = Math.max(0, (sw-12))+"px";
8266                 cn[0].childNodes[1].style.width = sww;
8267                 cn[1].childNodes[1].style.width = sww;
8268                 cn[2].childNodes[1].style.width = sww;
8269                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8270             }
8271         }
8272     },
8273
8274     /**
8275      * Hides this shadow
8276      */
8277     hide : function(){
8278         if(this.el){
8279             this.el.dom.style.display = "none";
8280             Roo.Shadow.Pool.push(this.el);
8281             delete this.el;
8282         }
8283     },
8284
8285     /**
8286      * Adjust the z-index of this shadow
8287      * @param {Number} zindex The new z-index
8288      */
8289     setZIndex : function(z){
8290         this.zIndex = z;
8291         if(this.el){
8292             this.el.setStyle("z-index", z);
8293         }
8294     }
8295 };
8296
8297 // Private utility class that manages the internal Shadow cache
8298 Roo.Shadow.Pool = function(){
8299     var p = [];
8300     var markup = Roo.isIE ?
8301                  '<div class="x-ie-shadow"></div>' :
8302                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
8303     return {
8304         pull : function(){
8305             var sh = p.shift();
8306             if(!sh){
8307                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8308                 sh.autoBoxAdjust = false;
8309             }
8310             return sh;
8311         },
8312
8313         push : function(sh){
8314             p.push(sh);
8315         }
8316     };
8317 }();/*
8318  * Based on:
8319  * Ext JS Library 1.1.1
8320  * Copyright(c) 2006-2007, Ext JS, LLC.
8321  *
8322  * Originally Released Under LGPL - original licence link has changed is not relivant.
8323  *
8324  * Fork - LGPL
8325  * <script type="text/javascript">
8326  */
8327
8328 /**
8329  * @class Roo.BoxComponent
8330  * @extends Roo.Component
8331  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8332  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8333  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8334  * layout containers.
8335  * @constructor
8336  * @param {Roo.Element/String/Object} config The configuration options.
8337  */
8338 Roo.BoxComponent = function(config){
8339     Roo.Component.call(this, config);
8340     this.addEvents({
8341         /**
8342          * @event resize
8343          * Fires after the component is resized.
8344              * @param {Roo.Component} this
8345              * @param {Number} adjWidth The box-adjusted width that was set
8346              * @param {Number} adjHeight The box-adjusted height that was set
8347              * @param {Number} rawWidth The width that was originally specified
8348              * @param {Number} rawHeight The height that was originally specified
8349              */
8350         resize : true,
8351         /**
8352          * @event move
8353          * Fires after the component is moved.
8354              * @param {Roo.Component} this
8355              * @param {Number} x The new x position
8356              * @param {Number} y The new y position
8357              */
8358         move : true
8359     });
8360 };
8361
8362 Roo.extend(Roo.BoxComponent, Roo.Component, {
8363     // private, set in afterRender to signify that the component has been rendered
8364     boxReady : false,
8365     // private, used to defer height settings to subclasses
8366     deferHeight: false,
8367     /** @cfg {Number} width
8368      * width (optional) size of component
8369      */
8370      /** @cfg {Number} height
8371      * height (optional) size of component
8372      */
8373      
8374     /**
8375      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8376      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8377      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8378      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8379      * @return {Roo.BoxComponent} this
8380      */
8381     setSize : function(w, h){
8382         // support for standard size objects
8383         if(typeof w == 'object'){
8384             h = w.height;
8385             w = w.width;
8386         }
8387         // not rendered
8388         if(!this.boxReady){
8389             this.width = w;
8390             this.height = h;
8391             return this;
8392         }
8393
8394         // prevent recalcs when not needed
8395         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8396             return this;
8397         }
8398         this.lastSize = {width: w, height: h};
8399
8400         var adj = this.adjustSize(w, h);
8401         var aw = adj.width, ah = adj.height;
8402         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8403             var rz = this.getResizeEl();
8404             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8405                 rz.setSize(aw, ah);
8406             }else if(!this.deferHeight && ah !== undefined){
8407                 rz.setHeight(ah);
8408             }else if(aw !== undefined){
8409                 rz.setWidth(aw);
8410             }
8411             this.onResize(aw, ah, w, h);
8412             this.fireEvent('resize', this, aw, ah, w, h);
8413         }
8414         return this;
8415     },
8416
8417     /**
8418      * Gets the current size of the component's underlying element.
8419      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8420      */
8421     getSize : function(){
8422         return this.el.getSize();
8423     },
8424
8425     /**
8426      * Gets the current XY position of the component's underlying element.
8427      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8428      * @return {Array} The XY position of the element (e.g., [100, 200])
8429      */
8430     getPosition : function(local){
8431         if(local === true){
8432             return [this.el.getLeft(true), this.el.getTop(true)];
8433         }
8434         return this.xy || this.el.getXY();
8435     },
8436
8437     /**
8438      * Gets the current box measurements of the component's underlying element.
8439      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8440      * @returns {Object} box An object in the format {x, y, width, height}
8441      */
8442     getBox : function(local){
8443         var s = this.el.getSize();
8444         if(local){
8445             s.x = this.el.getLeft(true);
8446             s.y = this.el.getTop(true);
8447         }else{
8448             var xy = this.xy || this.el.getXY();
8449             s.x = xy[0];
8450             s.y = xy[1];
8451         }
8452         return s;
8453     },
8454
8455     /**
8456      * Sets the current box measurements of the component's underlying element.
8457      * @param {Object} box An object in the format {x, y, width, height}
8458      * @returns {Roo.BoxComponent} this
8459      */
8460     updateBox : function(box){
8461         this.setSize(box.width, box.height);
8462         this.setPagePosition(box.x, box.y);
8463         return this;
8464     },
8465
8466     // protected
8467     getResizeEl : function(){
8468         return this.resizeEl || this.el;
8469     },
8470
8471     // protected
8472     getPositionEl : function(){
8473         return this.positionEl || this.el;
8474     },
8475
8476     /**
8477      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8478      * This method fires the move event.
8479      * @param {Number} left The new left
8480      * @param {Number} top The new top
8481      * @returns {Roo.BoxComponent} this
8482      */
8483     setPosition : function(x, y){
8484         this.x = x;
8485         this.y = y;
8486         if(!this.boxReady){
8487             return this;
8488         }
8489         var adj = this.adjustPosition(x, y);
8490         var ax = adj.x, ay = adj.y;
8491
8492         var el = this.getPositionEl();
8493         if(ax !== undefined || ay !== undefined){
8494             if(ax !== undefined && ay !== undefined){
8495                 el.setLeftTop(ax, ay);
8496             }else if(ax !== undefined){
8497                 el.setLeft(ax);
8498             }else if(ay !== undefined){
8499                 el.setTop(ay);
8500             }
8501             this.onPosition(ax, ay);
8502             this.fireEvent('move', this, ax, ay);
8503         }
8504         return this;
8505     },
8506
8507     /**
8508      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8509      * This method fires the move event.
8510      * @param {Number} x The new x position
8511      * @param {Number} y The new y position
8512      * @returns {Roo.BoxComponent} this
8513      */
8514     setPagePosition : function(x, y){
8515         this.pageX = x;
8516         this.pageY = y;
8517         if(!this.boxReady){
8518             return;
8519         }
8520         if(x === undefined || y === undefined){ // cannot translate undefined points
8521             return;
8522         }
8523         var p = this.el.translatePoints(x, y);
8524         this.setPosition(p.left, p.top);
8525         return this;
8526     },
8527
8528     // private
8529     onRender : function(ct, position){
8530         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8531         if(this.resizeEl){
8532             this.resizeEl = Roo.get(this.resizeEl);
8533         }
8534         if(this.positionEl){
8535             this.positionEl = Roo.get(this.positionEl);
8536         }
8537     },
8538
8539     // private
8540     afterRender : function(){
8541         Roo.BoxComponent.superclass.afterRender.call(this);
8542         this.boxReady = true;
8543         this.setSize(this.width, this.height);
8544         if(this.x || this.y){
8545             this.setPosition(this.x, this.y);
8546         }
8547         if(this.pageX || this.pageY){
8548             this.setPagePosition(this.pageX, this.pageY);
8549         }
8550     },
8551
8552     /**
8553      * Force the component's size to recalculate based on the underlying element's current height and width.
8554      * @returns {Roo.BoxComponent} this
8555      */
8556     syncSize : function(){
8557         delete this.lastSize;
8558         this.setSize(this.el.getWidth(), this.el.getHeight());
8559         return this;
8560     },
8561
8562     /**
8563      * Called after the component is resized, this method is empty by default but can be implemented by any
8564      * subclass that needs to perform custom logic after a resize occurs.
8565      * @param {Number} adjWidth The box-adjusted width that was set
8566      * @param {Number} adjHeight The box-adjusted height that was set
8567      * @param {Number} rawWidth The width that was originally specified
8568      * @param {Number} rawHeight The height that was originally specified
8569      */
8570     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8571
8572     },
8573
8574     /**
8575      * Called after the component is moved, this method is empty by default but can be implemented by any
8576      * subclass that needs to perform custom logic after a move occurs.
8577      * @param {Number} x The new x position
8578      * @param {Number} y The new y position
8579      */
8580     onPosition : function(x, y){
8581
8582     },
8583
8584     // private
8585     adjustSize : function(w, h){
8586         if(this.autoWidth){
8587             w = 'auto';
8588         }
8589         if(this.autoHeight){
8590             h = 'auto';
8591         }
8592         return {width : w, height: h};
8593     },
8594
8595     // private
8596     adjustPosition : function(x, y){
8597         return {x : x, y: y};
8598     }
8599 });/*
8600  * Based on:
8601  * Ext JS Library 1.1.1
8602  * Copyright(c) 2006-2007, Ext JS, LLC.
8603  *
8604  * Originally Released Under LGPL - original licence link has changed is not relivant.
8605  *
8606  * Fork - LGPL
8607  * <script type="text/javascript">
8608  */
8609
8610
8611 /**
8612  * @class Roo.SplitBar
8613  * @extends Roo.util.Observable
8614  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8615  * <br><br>
8616  * Usage:
8617  * <pre><code>
8618 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8619                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8620 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8621 split.minSize = 100;
8622 split.maxSize = 600;
8623 split.animate = true;
8624 split.on('moved', splitterMoved);
8625 </code></pre>
8626  * @constructor
8627  * Create a new SplitBar
8628  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8629  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8630  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8631  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8632                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8633                         position of the SplitBar).
8634  */
8635 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8636     
8637     /** @private */
8638     this.el = Roo.get(dragElement, true);
8639     this.el.dom.unselectable = "on";
8640     /** @private */
8641     this.resizingEl = Roo.get(resizingElement, true);
8642
8643     /**
8644      * @private
8645      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8646      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8647      * @type Number
8648      */
8649     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8650     
8651     /**
8652      * The minimum size of the resizing element. (Defaults to 0)
8653      * @type Number
8654      */
8655     this.minSize = 0;
8656     
8657     /**
8658      * The maximum size of the resizing element. (Defaults to 2000)
8659      * @type Number
8660      */
8661     this.maxSize = 2000;
8662     
8663     /**
8664      * Whether to animate the transition to the new size
8665      * @type Boolean
8666      */
8667     this.animate = false;
8668     
8669     /**
8670      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8671      * @type Boolean
8672      */
8673     this.useShim = false;
8674     
8675     /** @private */
8676     this.shim = null;
8677     
8678     if(!existingProxy){
8679         /** @private */
8680         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8681     }else{
8682         this.proxy = Roo.get(existingProxy).dom;
8683     }
8684     /** @private */
8685     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8686     
8687     /** @private */
8688     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8689     
8690     /** @private */
8691     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8692     
8693     /** @private */
8694     this.dragSpecs = {};
8695     
8696     /**
8697      * @private The adapter to use to positon and resize elements
8698      */
8699     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8700     this.adapter.init(this);
8701     
8702     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8703         /** @private */
8704         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8705         this.el.addClass("x-splitbar-h");
8706     }else{
8707         /** @private */
8708         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8709         this.el.addClass("x-splitbar-v");
8710     }
8711     
8712     this.addEvents({
8713         /**
8714          * @event resize
8715          * Fires when the splitter is moved (alias for {@link #event-moved})
8716          * @param {Roo.SplitBar} this
8717          * @param {Number} newSize the new width or height
8718          */
8719         "resize" : true,
8720         /**
8721          * @event moved
8722          * Fires when the splitter is moved
8723          * @param {Roo.SplitBar} this
8724          * @param {Number} newSize the new width or height
8725          */
8726         "moved" : true,
8727         /**
8728          * @event beforeresize
8729          * Fires before the splitter is dragged
8730          * @param {Roo.SplitBar} this
8731          */
8732         "beforeresize" : true,
8733
8734         "beforeapply" : true
8735     });
8736
8737     Roo.util.Observable.call(this);
8738 };
8739
8740 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8741     onStartProxyDrag : function(x, y){
8742         this.fireEvent("beforeresize", this);
8743         if(!this.overlay){
8744             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8745             o.unselectable();
8746             o.enableDisplayMode("block");
8747             // all splitbars share the same overlay
8748             Roo.SplitBar.prototype.overlay = o;
8749         }
8750         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8751         this.overlay.show();
8752         Roo.get(this.proxy).setDisplayed("block");
8753         var size = this.adapter.getElementSize(this);
8754         this.activeMinSize = this.getMinimumSize();;
8755         this.activeMaxSize = this.getMaximumSize();;
8756         var c1 = size - this.activeMinSize;
8757         var c2 = Math.max(this.activeMaxSize - size, 0);
8758         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8759             this.dd.resetConstraints();
8760             this.dd.setXConstraint(
8761                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8762                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8763             );
8764             this.dd.setYConstraint(0, 0);
8765         }else{
8766             this.dd.resetConstraints();
8767             this.dd.setXConstraint(0, 0);
8768             this.dd.setYConstraint(
8769                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8770                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8771             );
8772          }
8773         this.dragSpecs.startSize = size;
8774         this.dragSpecs.startPoint = [x, y];
8775         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8776     },
8777     
8778     /** 
8779      * @private Called after the drag operation by the DDProxy
8780      */
8781     onEndProxyDrag : function(e){
8782         Roo.get(this.proxy).setDisplayed(false);
8783         var endPoint = Roo.lib.Event.getXY(e);
8784         if(this.overlay){
8785             this.overlay.hide();
8786         }
8787         var newSize;
8788         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8789             newSize = this.dragSpecs.startSize + 
8790                 (this.placement == Roo.SplitBar.LEFT ?
8791                     endPoint[0] - this.dragSpecs.startPoint[0] :
8792                     this.dragSpecs.startPoint[0] - endPoint[0]
8793                 );
8794         }else{
8795             newSize = this.dragSpecs.startSize + 
8796                 (this.placement == Roo.SplitBar.TOP ?
8797                     endPoint[1] - this.dragSpecs.startPoint[1] :
8798                     this.dragSpecs.startPoint[1] - endPoint[1]
8799                 );
8800         }
8801         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8802         if(newSize != this.dragSpecs.startSize){
8803             if(this.fireEvent('beforeapply', this, newSize) !== false){
8804                 this.adapter.setElementSize(this, newSize);
8805                 this.fireEvent("moved", this, newSize);
8806                 this.fireEvent("resize", this, newSize);
8807             }
8808         }
8809     },
8810     
8811     /**
8812      * Get the adapter this SplitBar uses
8813      * @return The adapter object
8814      */
8815     getAdapter : function(){
8816         return this.adapter;
8817     },
8818     
8819     /**
8820      * Set the adapter this SplitBar uses
8821      * @param {Object} adapter A SplitBar adapter object
8822      */
8823     setAdapter : function(adapter){
8824         this.adapter = adapter;
8825         this.adapter.init(this);
8826     },
8827     
8828     /**
8829      * Gets the minimum size for the resizing element
8830      * @return {Number} The minimum size
8831      */
8832     getMinimumSize : function(){
8833         return this.minSize;
8834     },
8835     
8836     /**
8837      * Sets the minimum size for the resizing element
8838      * @param {Number} minSize The minimum size
8839      */
8840     setMinimumSize : function(minSize){
8841         this.minSize = minSize;
8842     },
8843     
8844     /**
8845      * Gets the maximum size for the resizing element
8846      * @return {Number} The maximum size
8847      */
8848     getMaximumSize : function(){
8849         return this.maxSize;
8850     },
8851     
8852     /**
8853      * Sets the maximum size for the resizing element
8854      * @param {Number} maxSize The maximum size
8855      */
8856     setMaximumSize : function(maxSize){
8857         this.maxSize = maxSize;
8858     },
8859     
8860     /**
8861      * Sets the initialize size for the resizing element
8862      * @param {Number} size The initial size
8863      */
8864     setCurrentSize : function(size){
8865         var oldAnimate = this.animate;
8866         this.animate = false;
8867         this.adapter.setElementSize(this, size);
8868         this.animate = oldAnimate;
8869     },
8870     
8871     /**
8872      * Destroy this splitbar. 
8873      * @param {Boolean} removeEl True to remove the element
8874      */
8875     destroy : function(removeEl){
8876         if(this.shim){
8877             this.shim.remove();
8878         }
8879         this.dd.unreg();
8880         this.proxy.parentNode.removeChild(this.proxy);
8881         if(removeEl){
8882             this.el.remove();
8883         }
8884     }
8885 });
8886
8887 /**
8888  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
8889  */
8890 Roo.SplitBar.createProxy = function(dir){
8891     var proxy = new Roo.Element(document.createElement("div"));
8892     proxy.unselectable();
8893     var cls = 'x-splitbar-proxy';
8894     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8895     document.body.appendChild(proxy.dom);
8896     return proxy.dom;
8897 };
8898
8899 /** 
8900  * @class Roo.SplitBar.BasicLayoutAdapter
8901  * Default Adapter. It assumes the splitter and resizing element are not positioned
8902  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8903  */
8904 Roo.SplitBar.BasicLayoutAdapter = function(){
8905 };
8906
8907 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8908     // do nothing for now
8909     init : function(s){
8910     
8911     },
8912     /**
8913      * Called before drag operations to get the current size of the resizing element. 
8914      * @param {Roo.SplitBar} s The SplitBar using this adapter
8915      */
8916      getElementSize : function(s){
8917         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8918             return s.resizingEl.getWidth();
8919         }else{
8920             return s.resizingEl.getHeight();
8921         }
8922     },
8923     
8924     /**
8925      * Called after drag operations to set the size of the resizing element.
8926      * @param {Roo.SplitBar} s The SplitBar using this adapter
8927      * @param {Number} newSize The new size to set
8928      * @param {Function} onComplete A function to be invoked when resizing is complete
8929      */
8930     setElementSize : function(s, newSize, onComplete){
8931         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8932             if(!s.animate){
8933                 s.resizingEl.setWidth(newSize);
8934                 if(onComplete){
8935                     onComplete(s, newSize);
8936                 }
8937             }else{
8938                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8939             }
8940         }else{
8941             
8942             if(!s.animate){
8943                 s.resizingEl.setHeight(newSize);
8944                 if(onComplete){
8945                     onComplete(s, newSize);
8946                 }
8947             }else{
8948                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8949             }
8950         }
8951     }
8952 };
8953
8954 /** 
8955  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8956  * @extends Roo.SplitBar.BasicLayoutAdapter
8957  * Adapter that  moves the splitter element to align with the resized sizing element. 
8958  * Used with an absolute positioned SplitBar.
8959  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8960  * document.body, make sure you assign an id to the body element.
8961  */
8962 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8963     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8964     this.container = Roo.get(container);
8965 };
8966
8967 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8968     init : function(s){
8969         this.basic.init(s);
8970     },
8971     
8972     getElementSize : function(s){
8973         return this.basic.getElementSize(s);
8974     },
8975     
8976     setElementSize : function(s, newSize, onComplete){
8977         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8978     },
8979     
8980     moveSplitter : function(s){
8981         var yes = Roo.SplitBar;
8982         switch(s.placement){
8983             case yes.LEFT:
8984                 s.el.setX(s.resizingEl.getRight());
8985                 break;
8986             case yes.RIGHT:
8987                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
8988                 break;
8989             case yes.TOP:
8990                 s.el.setY(s.resizingEl.getBottom());
8991                 break;
8992             case yes.BOTTOM:
8993                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
8994                 break;
8995         }
8996     }
8997 };
8998
8999 /**
9000  * Orientation constant - Create a vertical SplitBar
9001  * @static
9002  * @type Number
9003  */
9004 Roo.SplitBar.VERTICAL = 1;
9005
9006 /**
9007  * Orientation constant - Create a horizontal SplitBar
9008  * @static
9009  * @type Number
9010  */
9011 Roo.SplitBar.HORIZONTAL = 2;
9012
9013 /**
9014  * Placement constant - The resizing element is to the left of the splitter element
9015  * @static
9016  * @type Number
9017  */
9018 Roo.SplitBar.LEFT = 1;
9019
9020 /**
9021  * Placement constant - The resizing element is to the right of the splitter element
9022  * @static
9023  * @type Number
9024  */
9025 Roo.SplitBar.RIGHT = 2;
9026
9027 /**
9028  * Placement constant - The resizing element is positioned above the splitter element
9029  * @static
9030  * @type Number
9031  */
9032 Roo.SplitBar.TOP = 3;
9033
9034 /**
9035  * Placement constant - The resizing element is positioned under splitter element
9036  * @static
9037  * @type Number
9038  */
9039 Roo.SplitBar.BOTTOM = 4;
9040 /*
9041  * Based on:
9042  * Ext JS Library 1.1.1
9043  * Copyright(c) 2006-2007, Ext JS, LLC.
9044  *
9045  * Originally Released Under LGPL - original licence link has changed is not relivant.
9046  *
9047  * Fork - LGPL
9048  * <script type="text/javascript">
9049  */
9050
9051 /**
9052  * @class Roo.View
9053  * @extends Roo.util.Observable
9054  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9055  * This class also supports single and multi selection modes. <br>
9056  * Create a data model bound view:
9057  <pre><code>
9058  var store = new Roo.data.Store(...);
9059
9060  var view = new Roo.View({
9061     el : "my-element",
9062     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9063  
9064     singleSelect: true,
9065     selectedClass: "ydataview-selected",
9066     store: store
9067  });
9068
9069  // listen for node click?
9070  view.on("click", function(vw, index, node, e){
9071  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9072  });
9073
9074  // load XML data
9075  dataModel.load("foobar.xml");
9076  </code></pre>
9077  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9078  * <br><br>
9079  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9080  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9081  * 
9082  * Note: old style constructor is still suported (container, template, config)
9083  * 
9084  * @constructor
9085  * Create a new View
9086  * @param {Object} config The config object
9087  * 
9088  */
9089 Roo.View = function(config, depreciated_tpl, depreciated_config){
9090     
9091     if (typeof(depreciated_tpl) == 'undefined') {
9092         // new way.. - universal constructor.
9093         Roo.apply(this, config);
9094         this.el  = Roo.get(this.el);
9095     } else {
9096         // old format..
9097         this.el  = Roo.get(config);
9098         this.tpl = depreciated_tpl;
9099         Roo.apply(this, depreciated_config);
9100     }
9101      
9102     
9103     if(typeof(this.tpl) == "string"){
9104         this.tpl = new Roo.Template(this.tpl);
9105     } else {
9106         // support xtype ctors..
9107         this.tpl = new Roo.factory(this.tpl, Roo);
9108     }
9109     
9110     
9111     this.tpl.compile();
9112    
9113
9114      
9115     /** @private */
9116     this.addEvents({
9117     /**
9118      * @event beforeclick
9119      * Fires before a click is processed. Returns false to cancel the default action.
9120      * @param {Roo.View} this
9121      * @param {Number} index The index of the target node
9122      * @param {HTMLElement} node The target node
9123      * @param {Roo.EventObject} e The raw event object
9124      */
9125         "beforeclick" : true,
9126     /**
9127      * @event click
9128      * Fires when a template node is clicked.
9129      * @param {Roo.View} this
9130      * @param {Number} index The index of the target node
9131      * @param {HTMLElement} node The target node
9132      * @param {Roo.EventObject} e The raw event object
9133      */
9134         "click" : true,
9135     /**
9136      * @event dblclick
9137      * Fires when a template node is double clicked.
9138      * @param {Roo.View} this
9139      * @param {Number} index The index of the target node
9140      * @param {HTMLElement} node The target node
9141      * @param {Roo.EventObject} e The raw event object
9142      */
9143         "dblclick" : true,
9144     /**
9145      * @event contextmenu
9146      * Fires when a template node is right clicked.
9147      * @param {Roo.View} this
9148      * @param {Number} index The index of the target node
9149      * @param {HTMLElement} node The target node
9150      * @param {Roo.EventObject} e The raw event object
9151      */
9152         "contextmenu" : true,
9153     /**
9154      * @event selectionchange
9155      * Fires when the selected nodes change.
9156      * @param {Roo.View} this
9157      * @param {Array} selections Array of the selected nodes
9158      */
9159         "selectionchange" : true,
9160
9161     /**
9162      * @event beforeselect
9163      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9164      * @param {Roo.View} this
9165      * @param {HTMLElement} node The node to be selected
9166      * @param {Array} selections Array of currently selected nodes
9167      */
9168         "beforeselect" : true
9169     });
9170
9171     this.el.on({
9172         "click": this.onClick,
9173         "dblclick": this.onDblClick,
9174         "contextmenu": this.onContextMenu,
9175         scope:this
9176     });
9177
9178     this.selections = [];
9179     this.nodes = [];
9180     this.cmp = new Roo.CompositeElementLite([]);
9181     if(this.store){
9182         this.store = Roo.factory(this.store, Roo.data);
9183         this.setStore(this.store, true);
9184     }
9185     Roo.View.superclass.constructor.call(this);
9186 };
9187
9188 Roo.extend(Roo.View, Roo.util.Observable, {
9189     
9190      /**
9191      * @cfg {Roo.data.Store} store Data store to load data from.
9192      */
9193     store : false,
9194     
9195     /**
9196      * @cfg {String|Roo.Element} el The container element.
9197      */
9198     el : '',
9199     
9200     /**
9201      * @cfg {String|Roo.Template} tpl The template used by this View 
9202      */
9203     tpl : false,
9204     
9205     /**
9206      * @cfg {String} selectedClass The css class to add to selected nodes
9207      */
9208     selectedClass : "x-view-selected",
9209      /**
9210      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9211      */
9212     emptyText : "",
9213     /**
9214      * @cfg {Boolean} multiSelect Allow multiple selection
9215      */
9216     
9217     multiSelect : false,
9218     /**
9219      * @cfg {Boolean} singleSelect Allow single selection
9220      */
9221     singleSelect:  false,
9222     
9223     /**
9224      * Returns the element this view is bound to.
9225      * @return {Roo.Element}
9226      */
9227     getEl : function(){
9228         return this.el;
9229     },
9230
9231     /**
9232      * Refreshes the view.
9233      */
9234     refresh : function(){
9235         var t = this.tpl;
9236         this.clearSelections();
9237         this.el.update("");
9238         var html = [];
9239         var records = this.store.getRange();
9240         if(records.length < 1){
9241             this.el.update(this.emptyText);
9242             return;
9243         }
9244         for(var i = 0, len = records.length; i < len; i++){
9245             var data = this.prepareData(records[i].data, i, records[i]);
9246             html[html.length] = t.apply(data);
9247         }
9248         this.el.update(html.join(""));
9249         this.nodes = this.el.dom.childNodes;
9250         this.updateIndexes(0);
9251     },
9252
9253     /**
9254      * Function to override to reformat the data that is sent to
9255      * the template for each node.
9256      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9257      * a JSON object for an UpdateManager bound view).
9258      */
9259     prepareData : function(data){
9260         return data;
9261     },
9262
9263     onUpdate : function(ds, record){
9264         this.clearSelections();
9265         var index = this.store.indexOf(record);
9266         var n = this.nodes[index];
9267         this.tpl.insertBefore(n, this.prepareData(record.data));
9268         n.parentNode.removeChild(n);
9269         this.updateIndexes(index, index);
9270     },
9271
9272     onAdd : function(ds, records, index){
9273         this.clearSelections();
9274         if(this.nodes.length == 0){
9275             this.refresh();
9276             return;
9277         }
9278         var n = this.nodes[index];
9279         for(var i = 0, len = records.length; i < len; i++){
9280             var d = this.prepareData(records[i].data);
9281             if(n){
9282                 this.tpl.insertBefore(n, d);
9283             }else{
9284                 this.tpl.append(this.el, d);
9285             }
9286         }
9287         this.updateIndexes(index);
9288     },
9289
9290     onRemove : function(ds, record, index){
9291         this.clearSelections();
9292         this.el.dom.removeChild(this.nodes[index]);
9293         this.updateIndexes(index);
9294     },
9295
9296     /**
9297      * Refresh an individual node.
9298      * @param {Number} index
9299      */
9300     refreshNode : function(index){
9301         this.onUpdate(this.store, this.store.getAt(index));
9302     },
9303
9304     updateIndexes : function(startIndex, endIndex){
9305         var ns = this.nodes;
9306         startIndex = startIndex || 0;
9307         endIndex = endIndex || ns.length - 1;
9308         for(var i = startIndex; i <= endIndex; i++){
9309             ns[i].nodeIndex = i;
9310         }
9311     },
9312
9313     /**
9314      * Changes the data store this view uses and refresh the view.
9315      * @param {Store} store
9316      */
9317     setStore : function(store, initial){
9318         if(!initial && this.store){
9319             this.store.un("datachanged", this.refresh);
9320             this.store.un("add", this.onAdd);
9321             this.store.un("remove", this.onRemove);
9322             this.store.un("update", this.onUpdate);
9323             this.store.un("clear", this.refresh);
9324         }
9325         if(store){
9326           
9327             store.on("datachanged", this.refresh, this);
9328             store.on("add", this.onAdd, this);
9329             store.on("remove", this.onRemove, this);
9330             store.on("update", this.onUpdate, this);
9331             store.on("clear", this.refresh, this);
9332         }
9333         
9334         if(store){
9335             this.refresh();
9336         }
9337     },
9338
9339     /**
9340      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9341      * @param {HTMLElement} node
9342      * @return {HTMLElement} The template node
9343      */
9344     findItemFromChild : function(node){
9345         var el = this.el.dom;
9346         if(!node || node.parentNode == el){
9347                     return node;
9348             }
9349             var p = node.parentNode;
9350             while(p && p != el){
9351             if(p.parentNode == el){
9352                 return p;
9353             }
9354             p = p.parentNode;
9355         }
9356             return null;
9357     },
9358
9359     /** @ignore */
9360     onClick : function(e){
9361         var item = this.findItemFromChild(e.getTarget());
9362         if(item){
9363             var index = this.indexOf(item);
9364             if(this.onItemClick(item, index, e) !== false){
9365                 this.fireEvent("click", this, index, item, e);
9366             }
9367         }else{
9368             this.clearSelections();
9369         }
9370     },
9371
9372     /** @ignore */
9373     onContextMenu : function(e){
9374         var item = this.findItemFromChild(e.getTarget());
9375         if(item){
9376             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9377         }
9378     },
9379
9380     /** @ignore */
9381     onDblClick : function(e){
9382         var item = this.findItemFromChild(e.getTarget());
9383         if(item){
9384             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9385         }
9386     },
9387
9388     onItemClick : function(item, index, e){
9389         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9390             return false;
9391         }
9392         if(this.multiSelect || this.singleSelect){
9393             if(this.multiSelect && e.shiftKey && this.lastSelection){
9394                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9395             }else{
9396                 this.select(item, this.multiSelect && e.ctrlKey);
9397                 this.lastSelection = item;
9398             }
9399             e.preventDefault();
9400         }
9401         return true;
9402     },
9403
9404     /**
9405      * Get the number of selected nodes.
9406      * @return {Number}
9407      */
9408     getSelectionCount : function(){
9409         return this.selections.length;
9410     },
9411
9412     /**
9413      * Get the currently selected nodes.
9414      * @return {Array} An array of HTMLElements
9415      */
9416     getSelectedNodes : function(){
9417         return this.selections;
9418     },
9419
9420     /**
9421      * Get the indexes of the selected nodes.
9422      * @return {Array}
9423      */
9424     getSelectedIndexes : function(){
9425         var indexes = [], s = this.selections;
9426         for(var i = 0, len = s.length; i < len; i++){
9427             indexes.push(s[i].nodeIndex);
9428         }
9429         return indexes;
9430     },
9431
9432     /**
9433      * Clear all selections
9434      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9435      */
9436     clearSelections : function(suppressEvent){
9437         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9438             this.cmp.elements = this.selections;
9439             this.cmp.removeClass(this.selectedClass);
9440             this.selections = [];
9441             if(!suppressEvent){
9442                 this.fireEvent("selectionchange", this, this.selections);
9443             }
9444         }
9445     },
9446
9447     /**
9448      * Returns true if the passed node is selected
9449      * @param {HTMLElement/Number} node The node or node index
9450      * @return {Boolean}
9451      */
9452     isSelected : function(node){
9453         var s = this.selections;
9454         if(s.length < 1){
9455             return false;
9456         }
9457         node = this.getNode(node);
9458         return s.indexOf(node) !== -1;
9459     },
9460
9461     /**
9462      * Selects nodes.
9463      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9464      * @param {Boolean} keepExisting (optional) true to keep existing selections
9465      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9466      */
9467     select : function(nodeInfo, keepExisting, suppressEvent){
9468         if(nodeInfo instanceof Array){
9469             if(!keepExisting){
9470                 this.clearSelections(true);
9471             }
9472             for(var i = 0, len = nodeInfo.length; i < len; i++){
9473                 this.select(nodeInfo[i], true, true);
9474             }
9475         } else{
9476             var node = this.getNode(nodeInfo);
9477             if(node && !this.isSelected(node)){
9478                 if(!keepExisting){
9479                     this.clearSelections(true);
9480                 }
9481                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9482                     Roo.fly(node).addClass(this.selectedClass);
9483                     this.selections.push(node);
9484                     if(!suppressEvent){
9485                         this.fireEvent("selectionchange", this, this.selections);
9486                     }
9487                 }
9488             }
9489         }
9490     },
9491
9492     /**
9493      * Gets a template node.
9494      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9495      * @return {HTMLElement} The node or null if it wasn't found
9496      */
9497     getNode : function(nodeInfo){
9498         if(typeof nodeInfo == "string"){
9499             return document.getElementById(nodeInfo);
9500         }else if(typeof nodeInfo == "number"){
9501             return this.nodes[nodeInfo];
9502         }
9503         return nodeInfo;
9504     },
9505
9506     /**
9507      * Gets a range template nodes.
9508      * @param {Number} startIndex
9509      * @param {Number} endIndex
9510      * @return {Array} An array of nodes
9511      */
9512     getNodes : function(start, end){
9513         var ns = this.nodes;
9514         start = start || 0;
9515         end = typeof end == "undefined" ? ns.length - 1 : end;
9516         var nodes = [];
9517         if(start <= end){
9518             for(var i = start; i <= end; i++){
9519                 nodes.push(ns[i]);
9520             }
9521         } else{
9522             for(var i = start; i >= end; i--){
9523                 nodes.push(ns[i]);
9524             }
9525         }
9526         return nodes;
9527     },
9528
9529     /**
9530      * Finds the index of the passed node
9531      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9532      * @return {Number} The index of the node or -1
9533      */
9534     indexOf : function(node){
9535         node = this.getNode(node);
9536         if(typeof node.nodeIndex == "number"){
9537             return node.nodeIndex;
9538         }
9539         var ns = this.nodes;
9540         for(var i = 0, len = ns.length; i < len; i++){
9541             if(ns[i] == node){
9542                 return i;
9543             }
9544         }
9545         return -1;
9546     }
9547 });
9548 /*
9549  * Based on:
9550  * Ext JS Library 1.1.1
9551  * Copyright(c) 2006-2007, Ext JS, LLC.
9552  *
9553  * Originally Released Under LGPL - original licence link has changed is not relivant.
9554  *
9555  * Fork - LGPL
9556  * <script type="text/javascript">
9557  */
9558
9559 /**
9560  * @class Roo.JsonView
9561  * @extends Roo.View
9562  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9563 <pre><code>
9564 var view = new Roo.JsonView({
9565     container: "my-element",
9566     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9567     multiSelect: true, 
9568     jsonRoot: "data" 
9569 });
9570
9571 // listen for node click?
9572 view.on("click", function(vw, index, node, e){
9573     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9574 });
9575
9576 // direct load of JSON data
9577 view.load("foobar.php");
9578
9579 // Example from my blog list
9580 var tpl = new Roo.Template(
9581     '&lt;div class="entry"&gt;' +
9582     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9583     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9584     "&lt;/div&gt;&lt;hr /&gt;"
9585 );
9586
9587 var moreView = new Roo.JsonView({
9588     container :  "entry-list", 
9589     template : tpl,
9590     jsonRoot: "posts"
9591 });
9592 moreView.on("beforerender", this.sortEntries, this);
9593 moreView.load({
9594     url: "/blog/get-posts.php",
9595     params: "allposts=true",
9596     text: "Loading Blog Entries..."
9597 });
9598 </code></pre>
9599
9600 * Note: old code is supported with arguments : (container, template, config)
9601
9602
9603  * @constructor
9604  * Create a new JsonView
9605  * 
9606  * @param {Object} config The config object
9607  * 
9608  */
9609 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9610     
9611     
9612     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9613
9614     var um = this.el.getUpdateManager();
9615     um.setRenderer(this);
9616     um.on("update", this.onLoad, this);
9617     um.on("failure", this.onLoadException, this);
9618
9619     /**
9620      * @event beforerender
9621      * Fires before rendering of the downloaded JSON data.
9622      * @param {Roo.JsonView} this
9623      * @param {Object} data The JSON data loaded
9624      */
9625     /**
9626      * @event load
9627      * Fires when data is loaded.
9628      * @param {Roo.JsonView} this
9629      * @param {Object} data The JSON data loaded
9630      * @param {Object} response The raw Connect response object
9631      */
9632     /**
9633      * @event loadexception
9634      * Fires when loading fails.
9635      * @param {Roo.JsonView} this
9636      * @param {Object} response The raw Connect response object
9637      */
9638     this.addEvents({
9639         'beforerender' : true,
9640         'load' : true,
9641         'loadexception' : true
9642     });
9643 };
9644 Roo.extend(Roo.JsonView, Roo.View, {
9645     /**
9646      * @type {String} The root property in the loaded JSON object that contains the data
9647      */
9648     jsonRoot : "",
9649
9650     /**
9651      * Refreshes the view.
9652      */
9653     refresh : function(){
9654         this.clearSelections();
9655         this.el.update("");
9656         var html = [];
9657         var o = this.jsonData;
9658         if(o && o.length > 0){
9659             for(var i = 0, len = o.length; i < len; i++){
9660                 var data = this.prepareData(o[i], i, o);
9661                 html[html.length] = this.tpl.apply(data);
9662             }
9663         }else{
9664             html.push(this.emptyText);
9665         }
9666         this.el.update(html.join(""));
9667         this.nodes = this.el.dom.childNodes;
9668         this.updateIndexes(0);
9669     },
9670
9671     /**
9672      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
9673      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
9674      <pre><code>
9675      view.load({
9676          url: "your-url.php",
9677          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9678          callback: yourFunction,
9679          scope: yourObject, //(optional scope)
9680          discardUrl: false,
9681          nocache: false,
9682          text: "Loading...",
9683          timeout: 30,
9684          scripts: false
9685      });
9686      </code></pre>
9687      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9688      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
9689      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9690      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9691      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
9692      */
9693     load : function(){
9694         var um = this.el.getUpdateManager();
9695         um.update.apply(um, arguments);
9696     },
9697
9698     render : function(el, response){
9699         this.clearSelections();
9700         this.el.update("");
9701         var o;
9702         try{
9703             o = Roo.util.JSON.decode(response.responseText);
9704             if(this.jsonRoot){
9705                 
9706                 o = o[this.jsonRoot];
9707             }
9708         } catch(e){
9709         }
9710         /**
9711          * The current JSON data or null
9712          */
9713         this.jsonData = o;
9714         this.beforeRender();
9715         this.refresh();
9716     },
9717
9718 /**
9719  * Get the number of records in the current JSON dataset
9720  * @return {Number}
9721  */
9722     getCount : function(){
9723         return this.jsonData ? this.jsonData.length : 0;
9724     },
9725
9726 /**
9727  * Returns the JSON object for the specified node(s)
9728  * @param {HTMLElement/Array} node The node or an array of nodes
9729  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9730  * you get the JSON object for the node
9731  */
9732     getNodeData : function(node){
9733         if(node instanceof Array){
9734             var data = [];
9735             for(var i = 0, len = node.length; i < len; i++){
9736                 data.push(this.getNodeData(node[i]));
9737             }
9738             return data;
9739         }
9740         return this.jsonData[this.indexOf(node)] || null;
9741     },
9742
9743     beforeRender : function(){
9744         this.snapshot = this.jsonData;
9745         if(this.sortInfo){
9746             this.sort.apply(this, this.sortInfo);
9747         }
9748         this.fireEvent("beforerender", this, this.jsonData);
9749     },
9750
9751     onLoad : function(el, o){
9752         this.fireEvent("load", this, this.jsonData, o);
9753     },
9754
9755     onLoadException : function(el, o){
9756         this.fireEvent("loadexception", this, o);
9757     },
9758
9759 /**
9760  * Filter the data by a specific property.
9761  * @param {String} property A property on your JSON objects
9762  * @param {String/RegExp} value Either string that the property values
9763  * should start with, or a RegExp to test against the property
9764  */
9765     filter : function(property, value){
9766         if(this.jsonData){
9767             var data = [];
9768             var ss = this.snapshot;
9769             if(typeof value == "string"){
9770                 var vlen = value.length;
9771                 if(vlen == 0){
9772                     this.clearFilter();
9773                     return;
9774                 }
9775                 value = value.toLowerCase();
9776                 for(var i = 0, len = ss.length; i < len; i++){
9777                     var o = ss[i];
9778                     if(o[property].substr(0, vlen).toLowerCase() == value){
9779                         data.push(o);
9780                     }
9781                 }
9782             } else if(value.exec){ // regex?
9783                 for(var i = 0, len = ss.length; i < len; i++){
9784                     var o = ss[i];
9785                     if(value.test(o[property])){
9786                         data.push(o);
9787                     }
9788                 }
9789             } else{
9790                 return;
9791             }
9792             this.jsonData = data;
9793             this.refresh();
9794         }
9795     },
9796
9797 /**
9798  * Filter by a function. The passed function will be called with each
9799  * object in the current dataset. If the function returns true the value is kept,
9800  * otherwise it is filtered.
9801  * @param {Function} fn
9802  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9803  */
9804     filterBy : function(fn, scope){
9805         if(this.jsonData){
9806             var data = [];
9807             var ss = this.snapshot;
9808             for(var i = 0, len = ss.length; i < len; i++){
9809                 var o = ss[i];
9810                 if(fn.call(scope || this, o)){
9811                     data.push(o);
9812                 }
9813             }
9814             this.jsonData = data;
9815             this.refresh();
9816         }
9817     },
9818
9819 /**
9820  * Clears the current filter.
9821  */
9822     clearFilter : function(){
9823         if(this.snapshot && this.jsonData != this.snapshot){
9824             this.jsonData = this.snapshot;
9825             this.refresh();
9826         }
9827     },
9828
9829
9830 /**
9831  * Sorts the data for this view and refreshes it.
9832  * @param {String} property A property on your JSON objects to sort on
9833  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9834  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9835  */
9836     sort : function(property, dir, sortType){
9837         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9838         if(this.jsonData){
9839             var p = property;
9840             var dsc = dir && dir.toLowerCase() == "desc";
9841             var f = function(o1, o2){
9842                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9843                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9844                 ;
9845                 if(v1 < v2){
9846                     return dsc ? +1 : -1;
9847                 } else if(v1 > v2){
9848                     return dsc ? -1 : +1;
9849                 } else{
9850                     return 0;
9851                 }
9852             };
9853             this.jsonData.sort(f);
9854             this.refresh();
9855             if(this.jsonData != this.snapshot){
9856                 this.snapshot.sort(f);
9857             }
9858         }
9859     }
9860 });/*
9861  * Based on:
9862  * Ext JS Library 1.1.1
9863  * Copyright(c) 2006-2007, Ext JS, LLC.
9864  *
9865  * Originally Released Under LGPL - original licence link has changed is not relivant.
9866  *
9867  * Fork - LGPL
9868  * <script type="text/javascript">
9869  */
9870  
9871
9872 /**
9873  * @class Roo.ColorPalette
9874  * @extends Roo.Component
9875  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9876  * Here's an example of typical usage:
9877  * <pre><code>
9878 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9879 cp.render('my-div');
9880
9881 cp.on('select', function(palette, selColor){
9882     // do something with selColor
9883 });
9884 </code></pre>
9885  * @constructor
9886  * Create a new ColorPalette
9887  * @param {Object} config The config object
9888  */
9889 Roo.ColorPalette = function(config){
9890     Roo.ColorPalette.superclass.constructor.call(this, config);
9891     this.addEvents({
9892         /**
9893              * @event select
9894              * Fires when a color is selected
9895              * @param {ColorPalette} this
9896              * @param {String} color The 6-digit color hex code (without the # symbol)
9897              */
9898         select: true
9899     });
9900
9901     if(this.handler){
9902         this.on("select", this.handler, this.scope, true);
9903     }
9904 };
9905 Roo.extend(Roo.ColorPalette, Roo.Component, {
9906     /**
9907      * @cfg {String} itemCls
9908      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9909      */
9910     itemCls : "x-color-palette",
9911     /**
9912      * @cfg {String} value
9913      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9914      * the hex codes are case-sensitive.
9915      */
9916     value : null,
9917     clickEvent:'click',
9918     // private
9919     ctype: "Roo.ColorPalette",
9920
9921     /**
9922      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9923      */
9924     allowReselect : false,
9925
9926     /**
9927      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9928      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9929      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9930      * of colors with the width setting until the box is symmetrical.</p>
9931      * <p>You can override individual colors if needed:</p>
9932      * <pre><code>
9933 var cp = new Roo.ColorPalette();
9934 cp.colors[0] = "FF0000";  // change the first box to red
9935 </code></pre>
9936
9937 Or you can provide a custom array of your own for complete control:
9938 <pre><code>
9939 var cp = new Roo.ColorPalette();
9940 cp.colors = ["000000", "993300", "333300"];
9941 </code></pre>
9942      * @type Array
9943      */
9944     colors : [
9945         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9946         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9947         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9948         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9949         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9950     ],
9951
9952     // private
9953     onRender : function(container, position){
9954         var t = new Roo.MasterTemplate(
9955             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9956         );
9957         var c = this.colors;
9958         for(var i = 0, len = c.length; i < len; i++){
9959             t.add([c[i]]);
9960         }
9961         var el = document.createElement("div");
9962         el.className = this.itemCls;
9963         t.overwrite(el);
9964         container.dom.insertBefore(el, position);
9965         this.el = Roo.get(el);
9966         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9967         if(this.clickEvent != 'click'){
9968             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9969         }
9970     },
9971
9972     // private
9973     afterRender : function(){
9974         Roo.ColorPalette.superclass.afterRender.call(this);
9975         if(this.value){
9976             var s = this.value;
9977             this.value = null;
9978             this.select(s);
9979         }
9980     },
9981
9982     // private
9983     handleClick : function(e, t){
9984         e.preventDefault();
9985         if(!this.disabled){
9986             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
9987             this.select(c.toUpperCase());
9988         }
9989     },
9990
9991     /**
9992      * Selects the specified color in the palette (fires the select event)
9993      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
9994      */
9995     select : function(color){
9996         color = color.replace("#", "");
9997         if(color != this.value || this.allowReselect){
9998             var el = this.el;
9999             if(this.value){
10000                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10001             }
10002             el.child("a.color-"+color).addClass("x-color-palette-sel");
10003             this.value = color;
10004             this.fireEvent("select", this, color);
10005         }
10006     }
10007 });/*
10008  * Based on:
10009  * Ext JS Library 1.1.1
10010  * Copyright(c) 2006-2007, Ext JS, LLC.
10011  *
10012  * Originally Released Under LGPL - original licence link has changed is not relivant.
10013  *
10014  * Fork - LGPL
10015  * <script type="text/javascript">
10016  */
10017  
10018 /**
10019  * @class Roo.DatePicker
10020  * @extends Roo.Component
10021  * Simple date picker class.
10022  * @constructor
10023  * Create a new DatePicker
10024  * @param {Object} config The config object
10025  */
10026 Roo.DatePicker = function(config){
10027     Roo.DatePicker.superclass.constructor.call(this, config);
10028
10029     this.value = config && config.value ?
10030                  config.value.clearTime() : new Date().clearTime();
10031
10032     this.addEvents({
10033         /**
10034              * @event select
10035              * Fires when a date is selected
10036              * @param {DatePicker} this
10037              * @param {Date} date The selected date
10038              */
10039         select: true
10040     });
10041
10042     if(this.handler){
10043         this.on("select", this.handler,  this.scope || this);
10044     }
10045     // build the disabledDatesRE
10046     if(!this.disabledDatesRE && this.disabledDates){
10047         var dd = this.disabledDates;
10048         var re = "(?:";
10049         for(var i = 0; i < dd.length; i++){
10050             re += dd[i];
10051             if(i != dd.length-1) re += "|";
10052         }
10053         this.disabledDatesRE = new RegExp(re + ")");
10054     }
10055 };
10056
10057 Roo.extend(Roo.DatePicker, Roo.Component, {
10058     /**
10059      * @cfg {String} todayText
10060      * The text to display on the button that selects the current date (defaults to "Today")
10061      */
10062     todayText : "Today",
10063     /**
10064      * @cfg {String} okText
10065      * The text to display on the ok button
10066      */
10067     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10068     /**
10069      * @cfg {String} cancelText
10070      * The text to display on the cancel button
10071      */
10072     cancelText : "Cancel",
10073     /**
10074      * @cfg {String} todayTip
10075      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10076      */
10077     todayTip : "{0} (Spacebar)",
10078     /**
10079      * @cfg {Date} minDate
10080      * Minimum allowable date (JavaScript date object, defaults to null)
10081      */
10082     minDate : null,
10083     /**
10084      * @cfg {Date} maxDate
10085      * Maximum allowable date (JavaScript date object, defaults to null)
10086      */
10087     maxDate : null,
10088     /**
10089      * @cfg {String} minText
10090      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10091      */
10092     minText : "This date is before the minimum date",
10093     /**
10094      * @cfg {String} maxText
10095      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10096      */
10097     maxText : "This date is after the maximum date",
10098     /**
10099      * @cfg {String} format
10100      * The default date format string which can be overriden for localization support.  The format must be
10101      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10102      */
10103     format : "m/d/y",
10104     /**
10105      * @cfg {Array} disabledDays
10106      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10107      */
10108     disabledDays : null,
10109     /**
10110      * @cfg {String} disabledDaysText
10111      * The tooltip to display when the date falls on a disabled day (defaults to "")
10112      */
10113     disabledDaysText : "",
10114     /**
10115      * @cfg {RegExp} disabledDatesRE
10116      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10117      */
10118     disabledDatesRE : null,
10119     /**
10120      * @cfg {String} disabledDatesText
10121      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10122      */
10123     disabledDatesText : "",
10124     /**
10125      * @cfg {Boolean} constrainToViewport
10126      * True to constrain the date picker to the viewport (defaults to true)
10127      */
10128     constrainToViewport : true,
10129     /**
10130      * @cfg {Array} monthNames
10131      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10132      */
10133     monthNames : Date.monthNames,
10134     /**
10135      * @cfg {Array} dayNames
10136      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10137      */
10138     dayNames : Date.dayNames,
10139     /**
10140      * @cfg {String} nextText
10141      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10142      */
10143     nextText: 'Next Month (Control+Right)',
10144     /**
10145      * @cfg {String} prevText
10146      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10147      */
10148     prevText: 'Previous Month (Control+Left)',
10149     /**
10150      * @cfg {String} monthYearText
10151      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10152      */
10153     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10154     /**
10155      * @cfg {Number} startDay
10156      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10157      */
10158     startDay : 0,
10159     /**
10160      * @cfg {Bool} showClear
10161      * Show a clear button (usefull for date form elements that can be blank.)
10162      */
10163     
10164     showClear: false,
10165     
10166     /**
10167      * Sets the value of the date field
10168      * @param {Date} value The date to set
10169      */
10170     setValue : function(value){
10171         var old = this.value;
10172         this.value = value.clearTime(true);
10173         if(this.el){
10174             this.update(this.value);
10175         }
10176     },
10177
10178     /**
10179      * Gets the current selected value of the date field
10180      * @return {Date} The selected date
10181      */
10182     getValue : function(){
10183         return this.value;
10184     },
10185
10186     // private
10187     focus : function(){
10188         if(this.el){
10189             this.update(this.activeDate);
10190         }
10191     },
10192
10193     // private
10194     onRender : function(container, position){
10195         var m = [
10196              '<table cellspacing="0">',
10197                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
10198                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10199         var dn = this.dayNames;
10200         for(var i = 0; i < 7; i++){
10201             var d = this.startDay+i;
10202             if(d > 6){
10203                 d = d-7;
10204             }
10205             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10206         }
10207         m[m.length] = "</tr></thead><tbody><tr>";
10208         for(var i = 0; i < 42; i++) {
10209             if(i % 7 == 0 && i != 0){
10210                 m[m.length] = "</tr><tr>";
10211             }
10212             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10213         }
10214         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10215             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10216
10217         var el = document.createElement("div");
10218         el.className = "x-date-picker";
10219         el.innerHTML = m.join("");
10220
10221         container.dom.insertBefore(el, position);
10222
10223         this.el = Roo.get(el);
10224         this.eventEl = Roo.get(el.firstChild);
10225
10226         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10227             handler: this.showPrevMonth,
10228             scope: this,
10229             preventDefault:true,
10230             stopDefault:true
10231         });
10232
10233         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10234             handler: this.showNextMonth,
10235             scope: this,
10236             preventDefault:true,
10237             stopDefault:true
10238         });
10239
10240         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10241
10242         this.monthPicker = this.el.down('div.x-date-mp');
10243         this.monthPicker.enableDisplayMode('block');
10244         
10245         var kn = new Roo.KeyNav(this.eventEl, {
10246             "left" : function(e){
10247                 e.ctrlKey ?
10248                     this.showPrevMonth() :
10249                     this.update(this.activeDate.add("d", -1));
10250             },
10251
10252             "right" : function(e){
10253                 e.ctrlKey ?
10254                     this.showNextMonth() :
10255                     this.update(this.activeDate.add("d", 1));
10256             },
10257
10258             "up" : function(e){
10259                 e.ctrlKey ?
10260                     this.showNextYear() :
10261                     this.update(this.activeDate.add("d", -7));
10262             },
10263
10264             "down" : function(e){
10265                 e.ctrlKey ?
10266                     this.showPrevYear() :
10267                     this.update(this.activeDate.add("d", 7));
10268             },
10269
10270             "pageUp" : function(e){
10271                 this.showNextMonth();
10272             },
10273
10274             "pageDown" : function(e){
10275                 this.showPrevMonth();
10276             },
10277
10278             "enter" : function(e){
10279                 e.stopPropagation();
10280                 return true;
10281             },
10282
10283             scope : this
10284         });
10285
10286         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10287
10288         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10289
10290         this.el.unselectable();
10291         
10292         this.cells = this.el.select("table.x-date-inner tbody td");
10293         this.textNodes = this.el.query("table.x-date-inner tbody span");
10294
10295         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10296             text: "&#160;",
10297             tooltip: this.monthYearText
10298         });
10299
10300         this.mbtn.on('click', this.showMonthPicker, this);
10301         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10302
10303
10304         var today = (new Date()).dateFormat(this.format);
10305         
10306         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10307         if (this.showClear) {
10308             baseTb.add( new Roo.Toolbar.Fill());
10309         }
10310         baseTb.add({
10311             text: String.format(this.todayText, today),
10312             tooltip: String.format(this.todayTip, today),
10313             handler: this.selectToday,
10314             scope: this
10315         });
10316         
10317         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10318             
10319         //});
10320         if (this.showClear) {
10321             
10322             baseTb.add( new Roo.Toolbar.Fill());
10323             baseTb.add({
10324                 text: '&#160;',
10325                 cls: 'x-btn-icon x-btn-clear',
10326                 handler: function() {
10327                     //this.value = '';
10328                     this.fireEvent("select", this, '');
10329                 },
10330                 scope: this
10331             });
10332         }
10333         
10334         
10335         if(Roo.isIE){
10336             this.el.repaint();
10337         }
10338         this.update(this.value);
10339     },
10340
10341     createMonthPicker : function(){
10342         if(!this.monthPicker.dom.firstChild){
10343             var buf = ['<table border="0" cellspacing="0">'];
10344             for(var i = 0; i < 6; i++){
10345                 buf.push(
10346                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10347                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10348                     i == 0 ?
10349                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
10350                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10351                 );
10352             }
10353             buf.push(
10354                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10355                     this.okText,
10356                     '</button><button type="button" class="x-date-mp-cancel">',
10357                     this.cancelText,
10358                     '</button></td></tr>',
10359                 '</table>'
10360             );
10361             this.monthPicker.update(buf.join(''));
10362             this.monthPicker.on('click', this.onMonthClick, this);
10363             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10364
10365             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10366             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10367
10368             this.mpMonths.each(function(m, a, i){
10369                 i += 1;
10370                 if((i%2) == 0){
10371                     m.dom.xmonth = 5 + Math.round(i * .5);
10372                 }else{
10373                     m.dom.xmonth = Math.round((i-1) * .5);
10374                 }
10375             });
10376         }
10377     },
10378
10379     showMonthPicker : function(){
10380         this.createMonthPicker();
10381         var size = this.el.getSize();
10382         this.monthPicker.setSize(size);
10383         this.monthPicker.child('table').setSize(size);
10384
10385         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10386         this.updateMPMonth(this.mpSelMonth);
10387         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10388         this.updateMPYear(this.mpSelYear);
10389
10390         this.monthPicker.slideIn('t', {duration:.2});
10391     },
10392
10393     updateMPYear : function(y){
10394         this.mpyear = y;
10395         var ys = this.mpYears.elements;
10396         for(var i = 1; i <= 10; i++){
10397             var td = ys[i-1], y2;
10398             if((i%2) == 0){
10399                 y2 = y + Math.round(i * .5);
10400                 td.firstChild.innerHTML = y2;
10401                 td.xyear = y2;
10402             }else{
10403                 y2 = y - (5-Math.round(i * .5));
10404                 td.firstChild.innerHTML = y2;
10405                 td.xyear = y2;
10406             }
10407             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10408         }
10409     },
10410
10411     updateMPMonth : function(sm){
10412         this.mpMonths.each(function(m, a, i){
10413             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10414         });
10415     },
10416
10417     selectMPMonth: function(m){
10418         
10419     },
10420
10421     onMonthClick : function(e, t){
10422         e.stopEvent();
10423         var el = new Roo.Element(t), pn;
10424         if(el.is('button.x-date-mp-cancel')){
10425             this.hideMonthPicker();
10426         }
10427         else if(el.is('button.x-date-mp-ok')){
10428             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10429             this.hideMonthPicker();
10430         }
10431         else if(pn = el.up('td.x-date-mp-month', 2)){
10432             this.mpMonths.removeClass('x-date-mp-sel');
10433             pn.addClass('x-date-mp-sel');
10434             this.mpSelMonth = pn.dom.xmonth;
10435         }
10436         else if(pn = el.up('td.x-date-mp-year', 2)){
10437             this.mpYears.removeClass('x-date-mp-sel');
10438             pn.addClass('x-date-mp-sel');
10439             this.mpSelYear = pn.dom.xyear;
10440         }
10441         else if(el.is('a.x-date-mp-prev')){
10442             this.updateMPYear(this.mpyear-10);
10443         }
10444         else if(el.is('a.x-date-mp-next')){
10445             this.updateMPYear(this.mpyear+10);
10446         }
10447     },
10448
10449     onMonthDblClick : function(e, t){
10450         e.stopEvent();
10451         var el = new Roo.Element(t), pn;
10452         if(pn = el.up('td.x-date-mp-month', 2)){
10453             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10454             this.hideMonthPicker();
10455         }
10456         else if(pn = el.up('td.x-date-mp-year', 2)){
10457             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10458             this.hideMonthPicker();
10459         }
10460     },
10461
10462     hideMonthPicker : function(disableAnim){
10463         if(this.monthPicker){
10464             if(disableAnim === true){
10465                 this.monthPicker.hide();
10466             }else{
10467                 this.monthPicker.slideOut('t', {duration:.2});
10468             }
10469         }
10470     },
10471
10472     // private
10473     showPrevMonth : function(e){
10474         this.update(this.activeDate.add("mo", -1));
10475     },
10476
10477     // private
10478     showNextMonth : function(e){
10479         this.update(this.activeDate.add("mo", 1));
10480     },
10481
10482     // private
10483     showPrevYear : function(){
10484         this.update(this.activeDate.add("y", -1));
10485     },
10486
10487     // private
10488     showNextYear : function(){
10489         this.update(this.activeDate.add("y", 1));
10490     },
10491
10492     // private
10493     handleMouseWheel : function(e){
10494         var delta = e.getWheelDelta();
10495         if(delta > 0){
10496             this.showPrevMonth();
10497             e.stopEvent();
10498         } else if(delta < 0){
10499             this.showNextMonth();
10500             e.stopEvent();
10501         }
10502     },
10503
10504     // private
10505     handleDateClick : function(e, t){
10506         e.stopEvent();
10507         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10508             this.setValue(new Date(t.dateValue));
10509             this.fireEvent("select", this, this.value);
10510         }
10511     },
10512
10513     // private
10514     selectToday : function(){
10515         this.setValue(new Date().clearTime());
10516         this.fireEvent("select", this, this.value);
10517     },
10518
10519     // private
10520     update : function(date){
10521         var vd = this.activeDate;
10522         this.activeDate = date;
10523         if(vd && this.el){
10524             var t = date.getTime();
10525             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10526                 this.cells.removeClass("x-date-selected");
10527                 this.cells.each(function(c){
10528                    if(c.dom.firstChild.dateValue == t){
10529                        c.addClass("x-date-selected");
10530                        setTimeout(function(){
10531                             try{c.dom.firstChild.focus();}catch(e){}
10532                        }, 50);
10533                        return false;
10534                    }
10535                 });
10536                 return;
10537             }
10538         }
10539         var days = date.getDaysInMonth();
10540         var firstOfMonth = date.getFirstDateOfMonth();
10541         var startingPos = firstOfMonth.getDay()-this.startDay;
10542
10543         if(startingPos <= this.startDay){
10544             startingPos += 7;
10545         }
10546
10547         var pm = date.add("mo", -1);
10548         var prevStart = pm.getDaysInMonth()-startingPos;
10549
10550         var cells = this.cells.elements;
10551         var textEls = this.textNodes;
10552         days += startingPos;
10553
10554         // convert everything to numbers so it's fast
10555         var day = 86400000;
10556         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10557         var today = new Date().clearTime().getTime();
10558         var sel = date.clearTime().getTime();
10559         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10560         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10561         var ddMatch = this.disabledDatesRE;
10562         var ddText = this.disabledDatesText;
10563         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10564         var ddaysText = this.disabledDaysText;
10565         var format = this.format;
10566
10567         var setCellClass = function(cal, cell){
10568             cell.title = "";
10569             var t = d.getTime();
10570             cell.firstChild.dateValue = t;
10571             if(t == today){
10572                 cell.className += " x-date-today";
10573                 cell.title = cal.todayText;
10574             }
10575             if(t == sel){
10576                 cell.className += " x-date-selected";
10577                 setTimeout(function(){
10578                     try{cell.firstChild.focus();}catch(e){}
10579                 }, 50);
10580             }
10581             // disabling
10582             if(t < min) {
10583                 cell.className = " x-date-disabled";
10584                 cell.title = cal.minText;
10585                 return;
10586             }
10587             if(t > max) {
10588                 cell.className = " x-date-disabled";
10589                 cell.title = cal.maxText;
10590                 return;
10591             }
10592             if(ddays){
10593                 if(ddays.indexOf(d.getDay()) != -1){
10594                     cell.title = ddaysText;
10595                     cell.className = " x-date-disabled";
10596                 }
10597             }
10598             if(ddMatch && format){
10599                 var fvalue = d.dateFormat(format);
10600                 if(ddMatch.test(fvalue)){
10601                     cell.title = ddText.replace("%0", fvalue);
10602                     cell.className = " x-date-disabled";
10603                 }
10604             }
10605         };
10606
10607         var i = 0;
10608         for(; i < startingPos; i++) {
10609             textEls[i].innerHTML = (++prevStart);
10610             d.setDate(d.getDate()+1);
10611             cells[i].className = "x-date-prevday";
10612             setCellClass(this, cells[i]);
10613         }
10614         for(; i < days; i++){
10615             intDay = i - startingPos + 1;
10616             textEls[i].innerHTML = (intDay);
10617             d.setDate(d.getDate()+1);
10618             cells[i].className = "x-date-active";
10619             setCellClass(this, cells[i]);
10620         }
10621         var extraDays = 0;
10622         for(; i < 42; i++) {
10623              textEls[i].innerHTML = (++extraDays);
10624              d.setDate(d.getDate()+1);
10625              cells[i].className = "x-date-nextday";
10626              setCellClass(this, cells[i]);
10627         }
10628
10629         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10630
10631         if(!this.internalRender){
10632             var main = this.el.dom.firstChild;
10633             var w = main.offsetWidth;
10634             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10635             Roo.fly(main).setWidth(w);
10636             this.internalRender = true;
10637             // opera does not respect the auto grow header center column
10638             // then, after it gets a width opera refuses to recalculate
10639             // without a second pass
10640             if(Roo.isOpera && !this.secondPass){
10641                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10642                 this.secondPass = true;
10643                 this.update.defer(10, this, [date]);
10644             }
10645         }
10646     }
10647 });/*
10648  * Based on:
10649  * Ext JS Library 1.1.1
10650  * Copyright(c) 2006-2007, Ext JS, LLC.
10651  *
10652  * Originally Released Under LGPL - original licence link has changed is not relivant.
10653  *
10654  * Fork - LGPL
10655  * <script type="text/javascript">
10656  */
10657 /**
10658  * @class Roo.TabPanel
10659  * @extends Roo.util.Observable
10660  * A lightweight tab container.
10661  * <br><br>
10662  * Usage:
10663  * <pre><code>
10664 // basic tabs 1, built from existing content
10665 var tabs = new Roo.TabPanel("tabs1");
10666 tabs.addTab("script", "View Script");
10667 tabs.addTab("markup", "View Markup");
10668 tabs.activate("script");
10669
10670 // more advanced tabs, built from javascript
10671 var jtabs = new Roo.TabPanel("jtabs");
10672 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10673
10674 // set up the UpdateManager
10675 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10676 var updater = tab2.getUpdateManager();
10677 updater.setDefaultUrl("ajax1.htm");
10678 tab2.on('activate', updater.refresh, updater, true);
10679
10680 // Use setUrl for Ajax loading
10681 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10682 tab3.setUrl("ajax2.htm", null, true);
10683
10684 // Disabled tab
10685 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10686 tab4.disable();
10687
10688 jtabs.activate("jtabs-1");
10689  * </code></pre>
10690  * @constructor
10691  * Create a new TabPanel.
10692  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10693  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10694  */
10695 Roo.TabPanel = function(container, config){
10696     /**
10697     * The container element for this TabPanel.
10698     * @type Roo.Element
10699     */
10700     this.el = Roo.get(container, true);
10701     if(config){
10702         if(typeof config == "boolean"){
10703             this.tabPosition = config ? "bottom" : "top";
10704         }else{
10705             Roo.apply(this, config);
10706         }
10707     }
10708     if(this.tabPosition == "bottom"){
10709         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10710         this.el.addClass("x-tabs-bottom");
10711     }
10712     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10713     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10714     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10715     if(Roo.isIE){
10716         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10717     }
10718     if(this.tabPosition != "bottom"){
10719     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10720      * @type Roo.Element
10721      */
10722       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10723       this.el.addClass("x-tabs-top");
10724     }
10725     this.items = [];
10726
10727     this.bodyEl.setStyle("position", "relative");
10728
10729     this.active = null;
10730     this.activateDelegate = this.activate.createDelegate(this);
10731
10732     this.addEvents({
10733         /**
10734          * @event tabchange
10735          * Fires when the active tab changes
10736          * @param {Roo.TabPanel} this
10737          * @param {Roo.TabPanelItem} activePanel The new active tab
10738          */
10739         "tabchange": true,
10740         /**
10741          * @event beforetabchange
10742          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10743          * @param {Roo.TabPanel} this
10744          * @param {Object} e Set cancel to true on this object to cancel the tab change
10745          * @param {Roo.TabPanelItem} tab The tab being changed to
10746          */
10747         "beforetabchange" : true
10748     });
10749
10750     Roo.EventManager.onWindowResize(this.onResize, this);
10751     this.cpad = this.el.getPadding("lr");
10752     this.hiddenCount = 0;
10753
10754     Roo.TabPanel.superclass.constructor.call(this);
10755 };
10756
10757 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10758         /*
10759          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10760          */
10761     tabPosition : "top",
10762         /*
10763          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10764          */
10765     currentTabWidth : 0,
10766         /*
10767          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10768          */
10769     minTabWidth : 40,
10770         /*
10771          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10772          */
10773     maxTabWidth : 250,
10774         /*
10775          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10776          */
10777     preferredTabWidth : 175,
10778         /*
10779          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10780          */
10781     resizeTabs : false,
10782         /*
10783          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10784          */
10785     monitorResize : true,
10786
10787     /**
10788      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10789      * @param {String} id The id of the div to use <b>or create</b>
10790      * @param {String} text The text for the tab
10791      * @param {String} content (optional) Content to put in the TabPanelItem body
10792      * @param {Boolean} closable (optional) True to create a close icon on the tab
10793      * @return {Roo.TabPanelItem} The created TabPanelItem
10794      */
10795     addTab : function(id, text, content, closable){
10796         var item = new Roo.TabPanelItem(this, id, text, closable);
10797         this.addTabItem(item);
10798         if(content){
10799             item.setContent(content);
10800         }
10801         return item;
10802     },
10803
10804     /**
10805      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10806      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10807      * @return {Roo.TabPanelItem}
10808      */
10809     getTab : function(id){
10810         return this.items[id];
10811     },
10812
10813     /**
10814      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10815      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10816      */
10817     hideTab : function(id){
10818         var t = this.items[id];
10819         if(!t.isHidden()){
10820            t.setHidden(true);
10821            this.hiddenCount++;
10822            this.autoSizeTabs();
10823         }
10824     },
10825
10826     /**
10827      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10828      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10829      */
10830     unhideTab : function(id){
10831         var t = this.items[id];
10832         if(t.isHidden()){
10833            t.setHidden(false);
10834            this.hiddenCount--;
10835            this.autoSizeTabs();
10836         }
10837     },
10838
10839     /**
10840      * Adds an existing {@link Roo.TabPanelItem}.
10841      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10842      */
10843     addTabItem : function(item){
10844         this.items[item.id] = item;
10845         this.items.push(item);
10846         if(this.resizeTabs){
10847            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10848            this.autoSizeTabs();
10849         }else{
10850             item.autoSize();
10851         }
10852     },
10853
10854     /**
10855      * Removes a {@link Roo.TabPanelItem}.
10856      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10857      */
10858     removeTab : function(id){
10859         var items = this.items;
10860         var tab = items[id];
10861         if(!tab) { return; }
10862         var index = items.indexOf(tab);
10863         if(this.active == tab && items.length > 1){
10864             var newTab = this.getNextAvailable(index);
10865             if(newTab) {
10866                 newTab.activate();
10867             }
10868         }
10869         this.stripEl.dom.removeChild(tab.pnode.dom);
10870         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10871             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10872         }
10873         items.splice(index, 1);
10874         delete this.items[tab.id];
10875         tab.fireEvent("close", tab);
10876         tab.purgeListeners();
10877         this.autoSizeTabs();
10878     },
10879
10880     getNextAvailable : function(start){
10881         var items = this.items;
10882         var index = start;
10883         // look for a next tab that will slide over to
10884         // replace the one being removed
10885         while(index < items.length){
10886             var item = items[++index];
10887             if(item && !item.isHidden()){
10888                 return item;
10889             }
10890         }
10891         // if one isn't found select the previous tab (on the left)
10892         index = start;
10893         while(index >= 0){
10894             var item = items[--index];
10895             if(item && !item.isHidden()){
10896                 return item;
10897             }
10898         }
10899         return null;
10900     },
10901
10902     /**
10903      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10904      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10905      */
10906     disableTab : function(id){
10907         var tab = this.items[id];
10908         if(tab && this.active != tab){
10909             tab.disable();
10910         }
10911     },
10912
10913     /**
10914      * Enables a {@link Roo.TabPanelItem} that is disabled.
10915      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10916      */
10917     enableTab : function(id){
10918         var tab = this.items[id];
10919         tab.enable();
10920     },
10921
10922     /**
10923      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10924      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10925      * @return {Roo.TabPanelItem} The TabPanelItem.
10926      */
10927     activate : function(id){
10928         var tab = this.items[id];
10929         if(!tab){
10930             return null;
10931         }
10932         if(tab == this.active || tab.disabled){
10933             return tab;
10934         }
10935         var e = {};
10936         this.fireEvent("beforetabchange", this, e, tab);
10937         if(e.cancel !== true && !tab.disabled){
10938             if(this.active){
10939                 this.active.hide();
10940             }
10941             this.active = this.items[id];
10942             this.active.show();
10943             this.fireEvent("tabchange", this, this.active);
10944         }
10945         return tab;
10946     },
10947
10948     /**
10949      * Gets the active {@link Roo.TabPanelItem}.
10950      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10951      */
10952     getActiveTab : function(){
10953         return this.active;
10954     },
10955
10956     /**
10957      * Updates the tab body element to fit the height of the container element
10958      * for overflow scrolling
10959      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10960      */
10961     syncHeight : function(targetHeight){
10962         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10963         var bm = this.bodyEl.getMargins();
10964         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10965         this.bodyEl.setHeight(newHeight);
10966         return newHeight;
10967     },
10968
10969     onResize : function(){
10970         if(this.monitorResize){
10971             this.autoSizeTabs();
10972         }
10973     },
10974
10975     /**
10976      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10977      */
10978     beginUpdate : function(){
10979         this.updating = true;
10980     },
10981
10982     /**
10983      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
10984      */
10985     endUpdate : function(){
10986         this.updating = false;
10987         this.autoSizeTabs();
10988     },
10989
10990     /**
10991      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
10992      */
10993     autoSizeTabs : function(){
10994         var count = this.items.length;
10995         var vcount = count - this.hiddenCount;
10996         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
10997         var w = Math.max(this.el.getWidth() - this.cpad, 10);
10998         var availWidth = Math.floor(w / vcount);
10999         var b = this.stripBody;
11000         if(b.getWidth() > w){
11001             var tabs = this.items;
11002             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11003             if(availWidth < this.minTabWidth){
11004                 /*if(!this.sleft){    // incomplete scrolling code
11005                     this.createScrollButtons();
11006                 }
11007                 this.showScroll();
11008                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11009             }
11010         }else{
11011             if(this.currentTabWidth < this.preferredTabWidth){
11012                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11013             }
11014         }
11015     },
11016
11017     /**
11018      * Returns the number of tabs in this TabPanel.
11019      * @return {Number}
11020      */
11021      getCount : function(){
11022          return this.items.length;
11023      },
11024
11025     /**
11026      * Resizes all the tabs to the passed width
11027      * @param {Number} The new width
11028      */
11029     setTabWidth : function(width){
11030         this.currentTabWidth = width;
11031         for(var i = 0, len = this.items.length; i < len; i++) {
11032                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11033         }
11034     },
11035
11036     /**
11037      * Destroys this TabPanel
11038      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11039      */
11040     destroy : function(removeEl){
11041         Roo.EventManager.removeResizeListener(this.onResize, this);
11042         for(var i = 0, len = this.items.length; i < len; i++){
11043             this.items[i].purgeListeners();
11044         }
11045         if(removeEl === true){
11046             this.el.update("");
11047             this.el.remove();
11048         }
11049     }
11050 });
11051
11052 /**
11053  * @class Roo.TabPanelItem
11054  * @extends Roo.util.Observable
11055  * Represents an individual item (tab plus body) in a TabPanel.
11056  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11057  * @param {String} id The id of this TabPanelItem
11058  * @param {String} text The text for the tab of this TabPanelItem
11059  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11060  */
11061 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11062     /**
11063      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11064      * @type Roo.TabPanel
11065      */
11066     this.tabPanel = tabPanel;
11067     /**
11068      * The id for this TabPanelItem
11069      * @type String
11070      */
11071     this.id = id;
11072     /** @private */
11073     this.disabled = false;
11074     /** @private */
11075     this.text = text;
11076     /** @private */
11077     this.loaded = false;
11078     this.closable = closable;
11079
11080     /**
11081      * The body element for this TabPanelItem.
11082      * @type Roo.Element
11083      */
11084     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11085     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11086     this.bodyEl.setStyle("display", "block");
11087     this.bodyEl.setStyle("zoom", "1");
11088     this.hideAction();
11089
11090     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11091     /** @private */
11092     this.el = Roo.get(els.el, true);
11093     this.inner = Roo.get(els.inner, true);
11094     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11095     this.pnode = Roo.get(els.el.parentNode, true);
11096     this.el.on("mousedown", this.onTabMouseDown, this);
11097     this.el.on("click", this.onTabClick, this);
11098     /** @private */
11099     if(closable){
11100         var c = Roo.get(els.close, true);
11101         c.dom.title = this.closeText;
11102         c.addClassOnOver("close-over");
11103         c.on("click", this.closeClick, this);
11104      }
11105
11106     this.addEvents({
11107          /**
11108          * @event activate
11109          * Fires when this tab becomes the active tab.
11110          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11111          * @param {Roo.TabPanelItem} this
11112          */
11113         "activate": true,
11114         /**
11115          * @event beforeclose
11116          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11117          * @param {Roo.TabPanelItem} this
11118          * @param {Object} e Set cancel to true on this object to cancel the close.
11119          */
11120         "beforeclose": true,
11121         /**
11122          * @event close
11123          * Fires when this tab is closed.
11124          * @param {Roo.TabPanelItem} this
11125          */
11126          "close": true,
11127         /**
11128          * @event deactivate
11129          * Fires when this tab is no longer the active tab.
11130          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11131          * @param {Roo.TabPanelItem} this
11132          */
11133          "deactivate" : true
11134     });
11135     this.hidden = false;
11136
11137     Roo.TabPanelItem.superclass.constructor.call(this);
11138 };
11139
11140 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11141     purgeListeners : function(){
11142        Roo.util.Observable.prototype.purgeListeners.call(this);
11143        this.el.removeAllListeners();
11144     },
11145     /**
11146      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11147      */
11148     show : function(){
11149         this.pnode.addClass("on");
11150         this.showAction();
11151         if(Roo.isOpera){
11152             this.tabPanel.stripWrap.repaint();
11153         }
11154         this.fireEvent("activate", this.tabPanel, this);
11155     },
11156
11157     /**
11158      * Returns true if this tab is the active tab.
11159      * @return {Boolean}
11160      */
11161     isActive : function(){
11162         return this.tabPanel.getActiveTab() == this;
11163     },
11164
11165     /**
11166      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11167      */
11168     hide : function(){
11169         this.pnode.removeClass("on");
11170         this.hideAction();
11171         this.fireEvent("deactivate", this.tabPanel, this);
11172     },
11173
11174     hideAction : function(){
11175         this.bodyEl.hide();
11176         this.bodyEl.setStyle("position", "absolute");
11177         this.bodyEl.setLeft("-20000px");
11178         this.bodyEl.setTop("-20000px");
11179     },
11180
11181     showAction : function(){
11182         this.bodyEl.setStyle("position", "relative");
11183         this.bodyEl.setTop("");
11184         this.bodyEl.setLeft("");
11185         this.bodyEl.show();
11186     },
11187
11188     /**
11189      * Set the tooltip for the tab.
11190      * @param {String} tooltip The tab's tooltip
11191      */
11192     setTooltip : function(text){
11193         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11194             this.textEl.dom.qtip = text;
11195             this.textEl.dom.removeAttribute('title');
11196         }else{
11197             this.textEl.dom.title = text;
11198         }
11199     },
11200
11201     onTabClick : function(e){
11202         e.preventDefault();
11203         this.tabPanel.activate(this.id);
11204     },
11205
11206     onTabMouseDown : function(e){
11207         e.preventDefault();
11208         this.tabPanel.activate(this.id);
11209     },
11210
11211     getWidth : function(){
11212         return this.inner.getWidth();
11213     },
11214
11215     setWidth : function(width){
11216         var iwidth = width - this.pnode.getPadding("lr");
11217         this.inner.setWidth(iwidth);
11218         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11219         this.pnode.setWidth(width);
11220     },
11221
11222     /**
11223      * Show or hide the tab
11224      * @param {Boolean} hidden True to hide or false to show.
11225      */
11226     setHidden : function(hidden){
11227         this.hidden = hidden;
11228         this.pnode.setStyle("display", hidden ? "none" : "");
11229     },
11230
11231     /**
11232      * Returns true if this tab is "hidden"
11233      * @return {Boolean}
11234      */
11235     isHidden : function(){
11236         return this.hidden;
11237     },
11238
11239     /**
11240      * Returns the text for this tab
11241      * @return {String}
11242      */
11243     getText : function(){
11244         return this.text;
11245     },
11246
11247     autoSize : function(){
11248         //this.el.beginMeasure();
11249         this.textEl.setWidth(1);
11250         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11251         //this.el.endMeasure();
11252     },
11253
11254     /**
11255      * Sets the text for the tab (Note: this also sets the tooltip text)
11256      * @param {String} text The tab's text and tooltip
11257      */
11258     setText : function(text){
11259         this.text = text;
11260         this.textEl.update(text);
11261         this.setTooltip(text);
11262         if(!this.tabPanel.resizeTabs){
11263             this.autoSize();
11264         }
11265     },
11266     /**
11267      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11268      */
11269     activate : function(){
11270         this.tabPanel.activate(this.id);
11271     },
11272
11273     /**
11274      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11275      */
11276     disable : function(){
11277         if(this.tabPanel.active != this){
11278             this.disabled = true;
11279             this.pnode.addClass("disabled");
11280         }
11281     },
11282
11283     /**
11284      * Enables this TabPanelItem if it was previously disabled.
11285      */
11286     enable : function(){
11287         this.disabled = false;
11288         this.pnode.removeClass("disabled");
11289     },
11290
11291     /**
11292      * Sets the content for this TabPanelItem.
11293      * @param {String} content The content
11294      * @param {Boolean} loadScripts true to look for and load scripts
11295      */
11296     setContent : function(content, loadScripts){
11297         this.bodyEl.update(content, loadScripts);
11298     },
11299
11300     /**
11301      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11302      * @return {Roo.UpdateManager} The UpdateManager
11303      */
11304     getUpdateManager : function(){
11305         return this.bodyEl.getUpdateManager();
11306     },
11307
11308     /**
11309      * Set a URL to be used to load the content for this TabPanelItem.
11310      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11311      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
11312      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
11313      * @return {Roo.UpdateManager} The UpdateManager
11314      */
11315     setUrl : function(url, params, loadOnce){
11316         if(this.refreshDelegate){
11317             this.un('activate', this.refreshDelegate);
11318         }
11319         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11320         this.on("activate", this.refreshDelegate);
11321         return this.bodyEl.getUpdateManager();
11322     },
11323
11324     /** @private */
11325     _handleRefresh : function(url, params, loadOnce){
11326         if(!loadOnce || !this.loaded){
11327             var updater = this.bodyEl.getUpdateManager();
11328             updater.update(url, params, this._setLoaded.createDelegate(this));
11329         }
11330     },
11331
11332     /**
11333      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11334      *   Will fail silently if the setUrl method has not been called.
11335      *   This does not activate the panel, just updates its content.
11336      */
11337     refresh : function(){
11338         if(this.refreshDelegate){
11339            this.loaded = false;
11340            this.refreshDelegate();
11341         }
11342     },
11343
11344     /** @private */
11345     _setLoaded : function(){
11346         this.loaded = true;
11347     },
11348
11349     /** @private */
11350     closeClick : function(e){
11351         var o = {};
11352         e.stopEvent();
11353         this.fireEvent("beforeclose", this, o);
11354         if(o.cancel !== true){
11355             this.tabPanel.removeTab(this.id);
11356         }
11357     },
11358     /**
11359      * The text displayed in the tooltip for the close icon.
11360      * @type String
11361      */
11362     closeText : "Close this tab"
11363 });
11364
11365 /** @private */
11366 Roo.TabPanel.prototype.createStrip = function(container){
11367     var strip = document.createElement("div");
11368     strip.className = "x-tabs-wrap";
11369     container.appendChild(strip);
11370     return strip;
11371 };
11372 /** @private */
11373 Roo.TabPanel.prototype.createStripList = function(strip){
11374     // div wrapper for retard IE
11375     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
11376     return strip.firstChild.firstChild.firstChild.firstChild;
11377 };
11378 /** @private */
11379 Roo.TabPanel.prototype.createBody = function(container){
11380     var body = document.createElement("div");
11381     Roo.id(body, "tab-body");
11382     Roo.fly(body).addClass("x-tabs-body");
11383     container.appendChild(body);
11384     return body;
11385 };
11386 /** @private */
11387 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11388     var body = Roo.getDom(id);
11389     if(!body){
11390         body = document.createElement("div");
11391         body.id = id;
11392     }
11393     Roo.fly(body).addClass("x-tabs-item-body");
11394     bodyEl.insertBefore(body, bodyEl.firstChild);
11395     return body;
11396 };
11397 /** @private */
11398 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11399     var td = document.createElement("td");
11400     stripEl.appendChild(td);
11401     if(closable){
11402         td.className = "x-tabs-closable";
11403         if(!this.closeTpl){
11404             this.closeTpl = new Roo.Template(
11405                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11406                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11407                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11408             );
11409         }
11410         var el = this.closeTpl.overwrite(td, {"text": text});
11411         var close = el.getElementsByTagName("div")[0];
11412         var inner = el.getElementsByTagName("em")[0];
11413         return {"el": el, "close": close, "inner": inner};
11414     } else {
11415         if(!this.tabTpl){
11416             this.tabTpl = new Roo.Template(
11417                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11418                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11419             );
11420         }
11421         var el = this.tabTpl.overwrite(td, {"text": text});
11422         var inner = el.getElementsByTagName("em")[0];
11423         return {"el": el, "inner": inner};
11424     }
11425 };/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435
11436 /**
11437  * @class Roo.Button
11438  * @extends Roo.util.Observable
11439  * Simple Button class
11440  * @cfg {String} text The button text
11441  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11442  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11443  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11444  * @cfg {Object} scope The scope of the handler
11445  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11446  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11447  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11448  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11449  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11450  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11451    applies if enableToggle = true)
11452  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11453  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11454   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11455  * @constructor
11456  * Create a new button
11457  * @param {Object} config The config object
11458  */
11459 Roo.Button = function(renderTo, config)
11460 {
11461     if (!config) {
11462         config = renderTo;
11463         renderTo = config.renderTo || false;
11464     }
11465     
11466     Roo.apply(this, config);
11467     this.addEvents({
11468         /**
11469              * @event click
11470              * Fires when this button is clicked
11471              * @param {Button} this
11472              * @param {EventObject} e The click event
11473              */
11474             "click" : true,
11475         /**
11476              * @event toggle
11477              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11478              * @param {Button} this
11479              * @param {Boolean} pressed
11480              */
11481             "toggle" : true,
11482         /**
11483              * @event mouseover
11484              * Fires when the mouse hovers over the button
11485              * @param {Button} this
11486              * @param {Event} e The event object
11487              */
11488         'mouseover' : true,
11489         /**
11490              * @event mouseout
11491              * Fires when the mouse exits the button
11492              * @param {Button} this
11493              * @param {Event} e The event object
11494              */
11495         'mouseout': true,
11496          /**
11497              * @event render
11498              * Fires when the button is rendered
11499              * @param {Button} this
11500              */
11501         'render': true
11502     });
11503     if(this.menu){
11504         this.menu = Roo.menu.MenuMgr.get(this.menu);
11505     }
11506     // register listeners first!!  - so render can be captured..
11507     Roo.util.Observable.call(this);
11508     if(renderTo){
11509         this.render(renderTo);
11510     }
11511     
11512   
11513 };
11514
11515 Roo.extend(Roo.Button, Roo.util.Observable, {
11516     /**
11517      * 
11518      */
11519     
11520     /**
11521      * Read-only. True if this button is hidden
11522      * @type Boolean
11523      */
11524     hidden : false,
11525     /**
11526      * Read-only. True if this button is disabled
11527      * @type Boolean
11528      */
11529     disabled : false,
11530     /**
11531      * Read-only. True if this button is pressed (only if enableToggle = true)
11532      * @type Boolean
11533      */
11534     pressed : false,
11535
11536     /**
11537      * @cfg {Number} tabIndex 
11538      * The DOM tabIndex for this button (defaults to undefined)
11539      */
11540     tabIndex : undefined,
11541
11542     /**
11543      * @cfg {Boolean} enableToggle
11544      * True to enable pressed/not pressed toggling (defaults to false)
11545      */
11546     enableToggle: false,
11547     /**
11548      * @cfg {Mixed} menu
11549      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11550      */
11551     menu : undefined,
11552     /**
11553      * @cfg {String} menuAlign
11554      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11555      */
11556     menuAlign : "tl-bl?",
11557
11558     /**
11559      * @cfg {String} iconCls
11560      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11561      */
11562     iconCls : undefined,
11563     /**
11564      * @cfg {String} type
11565      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11566      */
11567     type : 'button',
11568
11569     // private
11570     menuClassTarget: 'tr',
11571
11572     /**
11573      * @cfg {String} clickEvent
11574      * The type of event to map to the button's event handler (defaults to 'click')
11575      */
11576     clickEvent : 'click',
11577
11578     /**
11579      * @cfg {Boolean} handleMouseEvents
11580      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11581      */
11582     handleMouseEvents : true,
11583
11584     /**
11585      * @cfg {String} tooltipType
11586      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11587      */
11588     tooltipType : 'qtip',
11589
11590     /**
11591      * @cfg {String} cls
11592      * A CSS class to apply to the button's main element.
11593      */
11594     
11595     /**
11596      * @cfg {Roo.Template} template (Optional)
11597      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11598      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11599      * require code modifications if required elements (e.g. a button) aren't present.
11600      */
11601
11602     // private
11603     render : function(renderTo){
11604         var btn;
11605         if(this.hideParent){
11606             this.parentEl = Roo.get(renderTo);
11607         }
11608         if(!this.dhconfig){
11609             if(!this.template){
11610                 if(!Roo.Button.buttonTemplate){
11611                     // hideous table template
11612                     Roo.Button.buttonTemplate = new Roo.Template(
11613                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11614                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
11615                         "</tr></tbody></table>");
11616                 }
11617                 this.template = Roo.Button.buttonTemplate;
11618             }
11619             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11620             var btnEl = btn.child("button:first");
11621             btnEl.on('focus', this.onFocus, this);
11622             btnEl.on('blur', this.onBlur, this);
11623             if(this.cls){
11624                 btn.addClass(this.cls);
11625             }
11626             if(this.icon){
11627                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11628             }
11629             if(this.iconCls){
11630                 btnEl.addClass(this.iconCls);
11631                 if(!this.cls){
11632                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11633                 }
11634             }
11635             if(this.tabIndex !== undefined){
11636                 btnEl.dom.tabIndex = this.tabIndex;
11637             }
11638             if(this.tooltip){
11639                 if(typeof this.tooltip == 'object'){
11640                     Roo.QuickTips.tips(Roo.apply({
11641                           target: btnEl.id
11642                     }, this.tooltip));
11643                 } else {
11644                     btnEl.dom[this.tooltipType] = this.tooltip;
11645                 }
11646             }
11647         }else{
11648             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11649         }
11650         this.el = btn;
11651         if(this.id){
11652             this.el.dom.id = this.el.id = this.id;
11653         }
11654         if(this.menu){
11655             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11656             this.menu.on("show", this.onMenuShow, this);
11657             this.menu.on("hide", this.onMenuHide, this);
11658         }
11659         btn.addClass("x-btn");
11660         if(Roo.isIE && !Roo.isIE7){
11661             this.autoWidth.defer(1, this);
11662         }else{
11663             this.autoWidth();
11664         }
11665         if(this.handleMouseEvents){
11666             btn.on("mouseover", this.onMouseOver, this);
11667             btn.on("mouseout", this.onMouseOut, this);
11668             btn.on("mousedown", this.onMouseDown, this);
11669         }
11670         btn.on(this.clickEvent, this.onClick, this);
11671         //btn.on("mouseup", this.onMouseUp, this);
11672         if(this.hidden){
11673             this.hide();
11674         }
11675         if(this.disabled){
11676             this.disable();
11677         }
11678         Roo.ButtonToggleMgr.register(this);
11679         if(this.pressed){
11680             this.el.addClass("x-btn-pressed");
11681         }
11682         if(this.repeat){
11683             var repeater = new Roo.util.ClickRepeater(btn,
11684                 typeof this.repeat == "object" ? this.repeat : {}
11685             );
11686             repeater.on("click", this.onClick,  this);
11687         }
11688         
11689         this.fireEvent('render', this);
11690         
11691     },
11692     /**
11693      * Returns the button's underlying element
11694      * @return {Roo.Element} The element
11695      */
11696     getEl : function(){
11697         return this.el;  
11698     },
11699     
11700     /**
11701      * Destroys this Button and removes any listeners.
11702      */
11703     destroy : function(){
11704         Roo.ButtonToggleMgr.unregister(this);
11705         this.el.removeAllListeners();
11706         this.purgeListeners();
11707         this.el.remove();
11708     },
11709
11710     // private
11711     autoWidth : function(){
11712         if(this.el){
11713             this.el.setWidth("auto");
11714             if(Roo.isIE7 && Roo.isStrict){
11715                 var ib = this.el.child('button');
11716                 if(ib && ib.getWidth() > 20){
11717                     ib.clip();
11718                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11719                 }
11720             }
11721             if(this.minWidth){
11722                 if(this.hidden){
11723                     this.el.beginMeasure();
11724                 }
11725                 if(this.el.getWidth() < this.minWidth){
11726                     this.el.setWidth(this.minWidth);
11727                 }
11728                 if(this.hidden){
11729                     this.el.endMeasure();
11730                 }
11731             }
11732         }
11733     },
11734
11735     /**
11736      * Assigns this button's click handler
11737      * @param {Function} handler The function to call when the button is clicked
11738      * @param {Object} scope (optional) Scope for the function passed in
11739      */
11740     setHandler : function(handler, scope){
11741         this.handler = handler;
11742         this.scope = scope;  
11743     },
11744     
11745     /**
11746      * Sets this button's text
11747      * @param {String} text The button text
11748      */
11749     setText : function(text){
11750         this.text = text;
11751         if(this.el){
11752             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11753         }
11754         this.autoWidth();
11755     },
11756     
11757     /**
11758      * Gets the text for this button
11759      * @return {String} The button text
11760      */
11761     getText : function(){
11762         return this.text;  
11763     },
11764     
11765     /**
11766      * Show this button
11767      */
11768     show: function(){
11769         this.hidden = false;
11770         if(this.el){
11771             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11772         }
11773     },
11774     
11775     /**
11776      * Hide this button
11777      */
11778     hide: function(){
11779         this.hidden = true;
11780         if(this.el){
11781             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11782         }
11783     },
11784     
11785     /**
11786      * Convenience function for boolean show/hide
11787      * @param {Boolean} visible True to show, false to hide
11788      */
11789     setVisible: function(visible){
11790         if(visible) {
11791             this.show();
11792         }else{
11793             this.hide();
11794         }
11795     },
11796     
11797     /**
11798      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11799      * @param {Boolean} state (optional) Force a particular state
11800      */
11801     toggle : function(state){
11802         state = state === undefined ? !this.pressed : state;
11803         if(state != this.pressed){
11804             if(state){
11805                 this.el.addClass("x-btn-pressed");
11806                 this.pressed = true;
11807                 this.fireEvent("toggle", this, true);
11808             }else{
11809                 this.el.removeClass("x-btn-pressed");
11810                 this.pressed = false;
11811                 this.fireEvent("toggle", this, false);
11812             }
11813             if(this.toggleHandler){
11814                 this.toggleHandler.call(this.scope || this, this, state);
11815             }
11816         }
11817     },
11818     
11819     /**
11820      * Focus the button
11821      */
11822     focus : function(){
11823         this.el.child('button:first').focus();
11824     },
11825     
11826     /**
11827      * Disable this button
11828      */
11829     disable : function(){
11830         if(this.el){
11831             this.el.addClass("x-btn-disabled");
11832         }
11833         this.disabled = true;
11834     },
11835     
11836     /**
11837      * Enable this button
11838      */
11839     enable : function(){
11840         if(this.el){
11841             this.el.removeClass("x-btn-disabled");
11842         }
11843         this.disabled = false;
11844     },
11845
11846     /**
11847      * Convenience function for boolean enable/disable
11848      * @param {Boolean} enabled True to enable, false to disable
11849      */
11850     setDisabled : function(v){
11851         this[v !== true ? "enable" : "disable"]();
11852     },
11853
11854     // private
11855     onClick : function(e){
11856         if(e){
11857             e.preventDefault();
11858         }
11859         if(e.button != 0){
11860             return;
11861         }
11862         if(!this.disabled){
11863             if(this.enableToggle){
11864                 this.toggle();
11865             }
11866             if(this.menu && !this.menu.isVisible()){
11867                 this.menu.show(this.el, this.menuAlign);
11868             }
11869             this.fireEvent("click", this, e);
11870             if(this.handler){
11871                 this.el.removeClass("x-btn-over");
11872                 this.handler.call(this.scope || this, this, e);
11873             }
11874         }
11875     },
11876     // private
11877     onMouseOver : function(e){
11878         if(!this.disabled){
11879             this.el.addClass("x-btn-over");
11880             this.fireEvent('mouseover', this, e);
11881         }
11882     },
11883     // private
11884     onMouseOut : function(e){
11885         if(!e.within(this.el,  true)){
11886             this.el.removeClass("x-btn-over");
11887             this.fireEvent('mouseout', this, e);
11888         }
11889     },
11890     // private
11891     onFocus : function(e){
11892         if(!this.disabled){
11893             this.el.addClass("x-btn-focus");
11894         }
11895     },
11896     // private
11897     onBlur : function(e){
11898         this.el.removeClass("x-btn-focus");
11899     },
11900     // private
11901     onMouseDown : function(e){
11902         if(!this.disabled && e.button == 0){
11903             this.el.addClass("x-btn-click");
11904             Roo.get(document).on('mouseup', this.onMouseUp, this);
11905         }
11906     },
11907     // private
11908     onMouseUp : function(e){
11909         if(e.button == 0){
11910             this.el.removeClass("x-btn-click");
11911             Roo.get(document).un('mouseup', this.onMouseUp, this);
11912         }
11913     },
11914     // private
11915     onMenuShow : function(e){
11916         this.el.addClass("x-btn-menu-active");
11917     },
11918     // private
11919     onMenuHide : function(e){
11920         this.el.removeClass("x-btn-menu-active");
11921     }   
11922 });
11923
11924 // Private utility class used by Button
11925 Roo.ButtonToggleMgr = function(){
11926    var groups = {};
11927    
11928    function toggleGroup(btn, state){
11929        if(state){
11930            var g = groups[btn.toggleGroup];
11931            for(var i = 0, l = g.length; i < l; i++){
11932                if(g[i] != btn){
11933                    g[i].toggle(false);
11934                }
11935            }
11936        }
11937    }
11938    
11939    return {
11940        register : function(btn){
11941            if(!btn.toggleGroup){
11942                return;
11943            }
11944            var g = groups[btn.toggleGroup];
11945            if(!g){
11946                g = groups[btn.toggleGroup] = [];
11947            }
11948            g.push(btn);
11949            btn.on("toggle", toggleGroup);
11950        },
11951        
11952        unregister : function(btn){
11953            if(!btn.toggleGroup){
11954                return;
11955            }
11956            var g = groups[btn.toggleGroup];
11957            if(g){
11958                g.remove(btn);
11959                btn.un("toggle", toggleGroup);
11960            }
11961        }
11962    };
11963 }();/*
11964  * Based on:
11965  * Ext JS Library 1.1.1
11966  * Copyright(c) 2006-2007, Ext JS, LLC.
11967  *
11968  * Originally Released Under LGPL - original licence link has changed is not relivant.
11969  *
11970  * Fork - LGPL
11971  * <script type="text/javascript">
11972  */
11973  
11974 /**
11975  * @class Roo.SplitButton
11976  * @extends Roo.Button
11977  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11978  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11979  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11980  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11981  * @cfg {String} arrowTooltip The title attribute of the arrow
11982  * @constructor
11983  * Create a new menu button
11984  * @param {String/HTMLElement/Element} renderTo The element to append the button to
11985  * @param {Object} config The config object
11986  */
11987 Roo.SplitButton = function(renderTo, config){
11988     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
11989     /**
11990      * @event arrowclick
11991      * Fires when this button's arrow is clicked
11992      * @param {SplitButton} this
11993      * @param {EventObject} e The click event
11994      */
11995     this.addEvents({"arrowclick":true});
11996 };
11997
11998 Roo.extend(Roo.SplitButton, Roo.Button, {
11999     render : function(renderTo){
12000         // this is one sweet looking template!
12001         var tpl = new Roo.Template(
12002             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12003             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12004             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
12005             "</tbody></table></td><td>",
12006             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12007             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
12008             "</tbody></table></td></tr></table>"
12009         );
12010         var btn = tpl.append(renderTo, [this.text, this.type], true);
12011         var btnEl = btn.child("button");
12012         if(this.cls){
12013             btn.addClass(this.cls);
12014         }
12015         if(this.icon){
12016             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12017         }
12018         if(this.iconCls){
12019             btnEl.addClass(this.iconCls);
12020             if(!this.cls){
12021                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12022             }
12023         }
12024         this.el = btn;
12025         if(this.handleMouseEvents){
12026             btn.on("mouseover", this.onMouseOver, this);
12027             btn.on("mouseout", this.onMouseOut, this);
12028             btn.on("mousedown", this.onMouseDown, this);
12029             btn.on("mouseup", this.onMouseUp, this);
12030         }
12031         btn.on(this.clickEvent, this.onClick, this);
12032         if(this.tooltip){
12033             if(typeof this.tooltip == 'object'){
12034                 Roo.QuickTips.tips(Roo.apply({
12035                       target: btnEl.id
12036                 }, this.tooltip));
12037             } else {
12038                 btnEl.dom[this.tooltipType] = this.tooltip;
12039             }
12040         }
12041         if(this.arrowTooltip){
12042             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12043         }
12044         if(this.hidden){
12045             this.hide();
12046         }
12047         if(this.disabled){
12048             this.disable();
12049         }
12050         if(this.pressed){
12051             this.el.addClass("x-btn-pressed");
12052         }
12053         if(Roo.isIE && !Roo.isIE7){
12054             this.autoWidth.defer(1, this);
12055         }else{
12056             this.autoWidth();
12057         }
12058         if(this.menu){
12059             this.menu.on("show", this.onMenuShow, this);
12060             this.menu.on("hide", this.onMenuHide, this);
12061         }
12062         this.fireEvent('render', this);
12063     },
12064
12065     // private
12066     autoWidth : function(){
12067         if(this.el){
12068             var tbl = this.el.child("table:first");
12069             var tbl2 = this.el.child("table:last");
12070             this.el.setWidth("auto");
12071             tbl.setWidth("auto");
12072             if(Roo.isIE7 && Roo.isStrict){
12073                 var ib = this.el.child('button:first');
12074                 if(ib && ib.getWidth() > 20){
12075                     ib.clip();
12076                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12077                 }
12078             }
12079             if(this.minWidth){
12080                 if(this.hidden){
12081                     this.el.beginMeasure();
12082                 }
12083                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12084                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12085                 }
12086                 if(this.hidden){
12087                     this.el.endMeasure();
12088                 }
12089             }
12090             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12091         } 
12092     },
12093     /**
12094      * Sets this button's click handler
12095      * @param {Function} handler The function to call when the button is clicked
12096      * @param {Object} scope (optional) Scope for the function passed above
12097      */
12098     setHandler : function(handler, scope){
12099         this.handler = handler;
12100         this.scope = scope;  
12101     },
12102     
12103     /**
12104      * Sets this button's arrow click handler
12105      * @param {Function} handler The function to call when the arrow is clicked
12106      * @param {Object} scope (optional) Scope for the function passed above
12107      */
12108     setArrowHandler : function(handler, scope){
12109         this.arrowHandler = handler;
12110         this.scope = scope;  
12111     },
12112     
12113     /**
12114      * Focus the button
12115      */
12116     focus : function(){
12117         if(this.el){
12118             this.el.child("button:first").focus();
12119         }
12120     },
12121
12122     // private
12123     onClick : function(e){
12124         e.preventDefault();
12125         if(!this.disabled){
12126             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12127                 if(this.menu && !this.menu.isVisible()){
12128                     this.menu.show(this.el, this.menuAlign);
12129                 }
12130                 this.fireEvent("arrowclick", this, e);
12131                 if(this.arrowHandler){
12132                     this.arrowHandler.call(this.scope || this, this, e);
12133                 }
12134             }else{
12135                 this.fireEvent("click", this, e);
12136                 if(this.handler){
12137                     this.handler.call(this.scope || this, this, e);
12138                 }
12139             }
12140         }
12141     },
12142     // private
12143     onMouseDown : function(e){
12144         if(!this.disabled){
12145             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12146         }
12147     },
12148     // private
12149     onMouseUp : function(e){
12150         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12151     }   
12152 });
12153
12154
12155 // backwards compat
12156 Roo.MenuButton = Roo.SplitButton;/*
12157  * Based on:
12158  * Ext JS Library 1.1.1
12159  * Copyright(c) 2006-2007, Ext JS, LLC.
12160  *
12161  * Originally Released Under LGPL - original licence link has changed is not relivant.
12162  *
12163  * Fork - LGPL
12164  * <script type="text/javascript">
12165  */
12166
12167 /**
12168  * @class Roo.Toolbar
12169  * Basic Toolbar class.
12170  * @constructor
12171  * Creates a new Toolbar
12172  * @param {Object} config The config object
12173  */ 
12174 Roo.Toolbar = function(container, buttons, config)
12175 {
12176     /// old consturctor format still supported..
12177     if(container instanceof Array){ // omit the container for later rendering
12178         buttons = container;
12179         config = buttons;
12180         container = null;
12181     }
12182     if (typeof(container) == 'object' && container.xtype) {
12183         config = container;
12184         container = config.container;
12185         buttons = config.buttons; // not really - use items!!
12186     }
12187     var xitems = [];
12188     if (config && config.items) {
12189         xitems = config.items;
12190         delete config.items;
12191     }
12192     Roo.apply(this, config);
12193     this.buttons = buttons;
12194     
12195     if(container){
12196         this.render(container);
12197     }
12198     Roo.each(xitems, function(b) {
12199         this.add(b);
12200     }, this);
12201     
12202 };
12203
12204 Roo.Toolbar.prototype = {
12205     /**
12206      * @cfg {Roo.data.Store} items
12207      * array of button configs or elements to add
12208      */
12209     
12210     /**
12211      * @cfg {String/HTMLElement/Element} container
12212      * The id or element that will contain the toolbar
12213      */
12214     // private
12215     render : function(ct){
12216         this.el = Roo.get(ct);
12217         if(this.cls){
12218             this.el.addClass(this.cls);
12219         }
12220         // using a table allows for vertical alignment
12221         // 100% width is needed by Safari...
12222         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12223         this.tr = this.el.child("tr", true);
12224         var autoId = 0;
12225         this.items = new Roo.util.MixedCollection(false, function(o){
12226             return o.id || ("item" + (++autoId));
12227         });
12228         if(this.buttons){
12229             this.add.apply(this, this.buttons);
12230             delete this.buttons;
12231         }
12232     },
12233
12234     /**
12235      * Adds element(s) to the toolbar -- this function takes a variable number of 
12236      * arguments of mixed type and adds them to the toolbar.
12237      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12238      * <ul>
12239      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12240      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12241      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12242      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12243      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12244      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12245      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12246      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12247      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12248      * </ul>
12249      * @param {Mixed} arg2
12250      * @param {Mixed} etc.
12251      */
12252     add : function(){
12253         var a = arguments, l = a.length;
12254         for(var i = 0; i < l; i++){
12255             this._add(a[i]);
12256         }
12257     },
12258     // private..
12259     _add : function(el) {
12260         
12261         if (el.xtype) {
12262             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12263         }
12264         
12265         if (el.applyTo){ // some kind of form field
12266             return this.addField(el);
12267         } 
12268         if (el.render){ // some kind of Toolbar.Item
12269             return this.addItem(el);
12270         }
12271         if (typeof el == "string"){ // string
12272             if(el == "separator" || el == "-"){
12273                 return this.addSeparator();
12274             }
12275             if (el == " "){
12276                 return this.addSpacer();
12277             }
12278             if(el == "->"){
12279                 return this.addFill();
12280             }
12281             return this.addText(el);
12282             
12283         }
12284         if(el.tagName){ // element
12285             return this.addElement(el);
12286         }
12287         if(typeof el == "object"){ // must be button config?
12288             return this.addButton(el);
12289         }
12290         // and now what?!?!
12291         return false;
12292         
12293     },
12294     
12295     /**
12296      * Add an Xtype element
12297      * @param {Object} xtype Xtype Object
12298      * @return {Object} created Object
12299      */
12300     addxtype : function(e){
12301         return this.add(e);  
12302     },
12303     
12304     /**
12305      * Returns the Element for this toolbar.
12306      * @return {Roo.Element}
12307      */
12308     getEl : function(){
12309         return this.el;  
12310     },
12311     
12312     /**
12313      * Adds a separator
12314      * @return {Roo.Toolbar.Item} The separator item
12315      */
12316     addSeparator : function(){
12317         return this.addItem(new Roo.Toolbar.Separator());
12318     },
12319
12320     /**
12321      * Adds a spacer element
12322      * @return {Roo.Toolbar.Spacer} The spacer item
12323      */
12324     addSpacer : function(){
12325         return this.addItem(new Roo.Toolbar.Spacer());
12326     },
12327
12328     /**
12329      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12330      * @return {Roo.Toolbar.Fill} The fill item
12331      */
12332     addFill : function(){
12333         return this.addItem(new Roo.Toolbar.Fill());
12334     },
12335
12336     /**
12337      * Adds any standard HTML element to the toolbar
12338      * @param {String/HTMLElement/Element} el The element or id of the element to add
12339      * @return {Roo.Toolbar.Item} The element's item
12340      */
12341     addElement : function(el){
12342         return this.addItem(new Roo.Toolbar.Item(el));
12343     },
12344     /**
12345      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12346      * @type Roo.util.MixedCollection  
12347      */
12348     items : false,
12349      
12350     /**
12351      * Adds any Toolbar.Item or subclass
12352      * @param {Roo.Toolbar.Item} item
12353      * @return {Roo.Toolbar.Item} The item
12354      */
12355     addItem : function(item){
12356         var td = this.nextBlock();
12357         item.render(td);
12358         this.items.add(item);
12359         return item;
12360     },
12361     
12362     /**
12363      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12364      * @param {Object/Array} config A button config or array of configs
12365      * @return {Roo.Toolbar.Button/Array}
12366      */
12367     addButton : function(config){
12368         if(config instanceof Array){
12369             var buttons = [];
12370             for(var i = 0, len = config.length; i < len; i++) {
12371                 buttons.push(this.addButton(config[i]));
12372             }
12373             return buttons;
12374         }
12375         var b = config;
12376         if(!(config instanceof Roo.Toolbar.Button)){
12377             b = config.split ?
12378                 new Roo.Toolbar.SplitButton(config) :
12379                 new Roo.Toolbar.Button(config);
12380         }
12381         var td = this.nextBlock();
12382         b.render(td);
12383         this.items.add(b);
12384         return b;
12385     },
12386     
12387     /**
12388      * Adds text to the toolbar
12389      * @param {String} text The text to add
12390      * @return {Roo.Toolbar.Item} The element's item
12391      */
12392     addText : function(text){
12393         return this.addItem(new Roo.Toolbar.TextItem(text));
12394     },
12395     
12396     /**
12397      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12398      * @param {Number} index The index where the item is to be inserted
12399      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12400      * @return {Roo.Toolbar.Button/Item}
12401      */
12402     insertButton : function(index, item){
12403         if(item instanceof Array){
12404             var buttons = [];
12405             for(var i = 0, len = item.length; i < len; i++) {
12406                buttons.push(this.insertButton(index + i, item[i]));
12407             }
12408             return buttons;
12409         }
12410         if (!(item instanceof Roo.Toolbar.Button)){
12411            item = new Roo.Toolbar.Button(item);
12412         }
12413         var td = document.createElement("td");
12414         this.tr.insertBefore(td, this.tr.childNodes[index]);
12415         item.render(td);
12416         this.items.insert(index, item);
12417         return item;
12418     },
12419     
12420     /**
12421      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12422      * @param {Object} config
12423      * @return {Roo.Toolbar.Item} The element's item
12424      */
12425     addDom : function(config, returnEl){
12426         var td = this.nextBlock();
12427         Roo.DomHelper.overwrite(td, config);
12428         var ti = new Roo.Toolbar.Item(td.firstChild);
12429         ti.render(td);
12430         this.items.add(ti);
12431         return ti;
12432     },
12433
12434     /**
12435      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12436      * @type Roo.util.MixedCollection  
12437      */
12438     fields : false,
12439     
12440     /**
12441      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12442      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12443      * @param {Roo.form.Field} field
12444      * @return {Roo.ToolbarItem}
12445      */
12446      
12447       
12448     addField : function(field) {
12449         if (!this.fields) {
12450             var autoId = 0;
12451             this.fields = new Roo.util.MixedCollection(false, function(o){
12452                 return o.id || ("item" + (++autoId));
12453             });
12454
12455         }
12456         
12457         var td = this.nextBlock();
12458         field.render(td);
12459         var ti = new Roo.Toolbar.Item(td.firstChild);
12460         ti.render(td);
12461         this.items.add(ti);
12462         this.fields.add(field);
12463         return ti;
12464     },
12465     /**
12466      * Hide the toolbar
12467      * @method hide
12468      */
12469      
12470       
12471     hide : function()
12472     {
12473         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12474         this.el.child('div').hide();
12475     },
12476     /**
12477      * Show the toolbar
12478      * @method show
12479      */
12480     show : function()
12481     {
12482         this.el.child('div').show();
12483     },
12484       
12485     // private
12486     nextBlock : function(){
12487         var td = document.createElement("td");
12488         this.tr.appendChild(td);
12489         return td;
12490     },
12491
12492     // private
12493     destroy : function(){
12494         if(this.items){ // rendered?
12495             Roo.destroy.apply(Roo, this.items.items);
12496         }
12497         if(this.fields){ // rendered?
12498             Roo.destroy.apply(Roo, this.fields.items);
12499         }
12500         Roo.Element.uncache(this.el, this.tr);
12501     }
12502 };
12503
12504 /**
12505  * @class Roo.Toolbar.Item
12506  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12507  * @constructor
12508  * Creates a new Item
12509  * @param {HTMLElement} el 
12510  */
12511 Roo.Toolbar.Item = function(el){
12512     this.el = Roo.getDom(el);
12513     this.id = Roo.id(this.el);
12514     this.hidden = false;
12515 };
12516
12517 Roo.Toolbar.Item.prototype = {
12518     
12519     /**
12520      * Get this item's HTML Element
12521      * @return {HTMLElement}
12522      */
12523     getEl : function(){
12524        return this.el;  
12525     },
12526
12527     // private
12528     render : function(td){
12529         this.td = td;
12530         td.appendChild(this.el);
12531     },
12532     
12533     /**
12534      * Removes and destroys this item.
12535      */
12536     destroy : function(){
12537         this.td.parentNode.removeChild(this.td);
12538     },
12539     
12540     /**
12541      * Shows this item.
12542      */
12543     show: function(){
12544         this.hidden = false;
12545         this.td.style.display = "";
12546     },
12547     
12548     /**
12549      * Hides this item.
12550      */
12551     hide: function(){
12552         this.hidden = true;
12553         this.td.style.display = "none";
12554     },
12555     
12556     /**
12557      * Convenience function for boolean show/hide.
12558      * @param {Boolean} visible true to show/false to hide
12559      */
12560     setVisible: function(visible){
12561         if(visible) {
12562             this.show();
12563         }else{
12564             this.hide();
12565         }
12566     },
12567     
12568     /**
12569      * Try to focus this item.
12570      */
12571     focus : function(){
12572         Roo.fly(this.el).focus();
12573     },
12574     
12575     /**
12576      * Disables this item.
12577      */
12578     disable : function(){
12579         Roo.fly(this.td).addClass("x-item-disabled");
12580         this.disabled = true;
12581         this.el.disabled = true;
12582     },
12583     
12584     /**
12585      * Enables this item.
12586      */
12587     enable : function(){
12588         Roo.fly(this.td).removeClass("x-item-disabled");
12589         this.disabled = false;
12590         this.el.disabled = false;
12591     }
12592 };
12593
12594
12595 /**
12596  * @class Roo.Toolbar.Separator
12597  * @extends Roo.Toolbar.Item
12598  * A simple toolbar separator class
12599  * @constructor
12600  * Creates a new Separator
12601  */
12602 Roo.Toolbar.Separator = function(){
12603     var s = document.createElement("span");
12604     s.className = "ytb-sep";
12605     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12606 };
12607 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12608     enable:Roo.emptyFn,
12609     disable:Roo.emptyFn,
12610     focus:Roo.emptyFn
12611 });
12612
12613 /**
12614  * @class Roo.Toolbar.Spacer
12615  * @extends Roo.Toolbar.Item
12616  * A simple element that adds extra horizontal space to a toolbar.
12617  * @constructor
12618  * Creates a new Spacer
12619  */
12620 Roo.Toolbar.Spacer = function(){
12621     var s = document.createElement("div");
12622     s.className = "ytb-spacer";
12623     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12624 };
12625 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12626     enable:Roo.emptyFn,
12627     disable:Roo.emptyFn,
12628     focus:Roo.emptyFn
12629 });
12630
12631 /**
12632  * @class Roo.Toolbar.Fill
12633  * @extends Roo.Toolbar.Spacer
12634  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12635  * @constructor
12636  * Creates a new Spacer
12637  */
12638 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12639     // private
12640     render : function(td){
12641         td.style.width = '100%';
12642         Roo.Toolbar.Fill.superclass.render.call(this, td);
12643     }
12644 });
12645
12646 /**
12647  * @class Roo.Toolbar.TextItem
12648  * @extends Roo.Toolbar.Item
12649  * A simple class that renders text directly into a toolbar.
12650  * @constructor
12651  * Creates a new TextItem
12652  * @param {String} text
12653  */
12654 Roo.Toolbar.TextItem = function(text){
12655     if (typeof(text) == 'object') {
12656         text = text.text;
12657     }
12658     var s = document.createElement("span");
12659     s.className = "ytb-text";
12660     s.innerHTML = text;
12661     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12662 };
12663 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12664     enable:Roo.emptyFn,
12665     disable:Roo.emptyFn,
12666     focus:Roo.emptyFn
12667 });
12668
12669 /**
12670  * @class Roo.Toolbar.Button
12671  * @extends Roo.Button
12672  * A button that renders into a toolbar.
12673  * @constructor
12674  * Creates a new Button
12675  * @param {Object} config A standard {@link Roo.Button} config object
12676  */
12677 Roo.Toolbar.Button = function(config){
12678     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12679 };
12680 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12681     render : function(td){
12682         this.td = td;
12683         Roo.Toolbar.Button.superclass.render.call(this, td);
12684     },
12685     
12686     /**
12687      * Removes and destroys this button
12688      */
12689     destroy : function(){
12690         Roo.Toolbar.Button.superclass.destroy.call(this);
12691         this.td.parentNode.removeChild(this.td);
12692     },
12693     
12694     /**
12695      * Shows this button
12696      */
12697     show: function(){
12698         this.hidden = false;
12699         this.td.style.display = "";
12700     },
12701     
12702     /**
12703      * Hides this button
12704      */
12705     hide: function(){
12706         this.hidden = true;
12707         this.td.style.display = "none";
12708     },
12709
12710     /**
12711      * Disables this item
12712      */
12713     disable : function(){
12714         Roo.fly(this.td).addClass("x-item-disabled");
12715         this.disabled = true;
12716     },
12717
12718     /**
12719      * Enables this item
12720      */
12721     enable : function(){
12722         Roo.fly(this.td).removeClass("x-item-disabled");
12723         this.disabled = false;
12724     }
12725 });
12726 // backwards compat
12727 Roo.ToolbarButton = Roo.Toolbar.Button;
12728
12729 /**
12730  * @class Roo.Toolbar.SplitButton
12731  * @extends Roo.SplitButton
12732  * A menu button that renders into a toolbar.
12733  * @constructor
12734  * Creates a new SplitButton
12735  * @param {Object} config A standard {@link Roo.SplitButton} config object
12736  */
12737 Roo.Toolbar.SplitButton = function(config){
12738     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12739 };
12740 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12741     render : function(td){
12742         this.td = td;
12743         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12744     },
12745     
12746     /**
12747      * Removes and destroys this button
12748      */
12749     destroy : function(){
12750         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12751         this.td.parentNode.removeChild(this.td);
12752     },
12753     
12754     /**
12755      * Shows this button
12756      */
12757     show: function(){
12758         this.hidden = false;
12759         this.td.style.display = "";
12760     },
12761     
12762     /**
12763      * Hides this button
12764      */
12765     hide: function(){
12766         this.hidden = true;
12767         this.td.style.display = "none";
12768     }
12769 });
12770
12771 // backwards compat
12772 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12773  * Based on:
12774  * Ext JS Library 1.1.1
12775  * Copyright(c) 2006-2007, Ext JS, LLC.
12776  *
12777  * Originally Released Under LGPL - original licence link has changed is not relivant.
12778  *
12779  * Fork - LGPL
12780  * <script type="text/javascript">
12781  */
12782  
12783 /**
12784  * @class Roo.PagingToolbar
12785  * @extends Roo.Toolbar
12786  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12787  * @constructor
12788  * Create a new PagingToolbar
12789  * @param {Object} config The config object
12790  */
12791 Roo.PagingToolbar = function(el, ds, config)
12792 {
12793     // old args format still supported... - xtype is prefered..
12794     if (typeof(el) == 'object' && el.xtype) {
12795         // created from xtype...
12796         config = el;
12797         ds = el.dataSource;
12798         el = config.container;
12799     }
12800     var items = [];
12801     if (config.items) {
12802         items = config.items;
12803         config.items = [];
12804     }
12805     
12806     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12807     this.ds = ds;
12808     this.cursor = 0;
12809     this.renderButtons(this.el);
12810     this.bind(ds);
12811     
12812     // supprot items array.
12813    
12814     Roo.each(items, function(e) {
12815         this.add(Roo.factory(e));
12816     },this);
12817     
12818 };
12819
12820 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12821     /**
12822      * @cfg {Roo.data.Store} dataSource
12823      * The underlying data store providing the paged data
12824      */
12825     /**
12826      * @cfg {String/HTMLElement/Element} container
12827      * container The id or element that will contain the toolbar
12828      */
12829     /**
12830      * @cfg {Boolean} displayInfo
12831      * True to display the displayMsg (defaults to false)
12832      */
12833     /**
12834      * @cfg {Number} pageSize
12835      * The number of records to display per page (defaults to 20)
12836      */
12837     pageSize: 20,
12838     /**
12839      * @cfg {String} displayMsg
12840      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12841      */
12842     displayMsg : 'Displaying {0} - {1} of {2}',
12843     /**
12844      * @cfg {String} emptyMsg
12845      * The message to display when no records are found (defaults to "No data to display")
12846      */
12847     emptyMsg : 'No data to display',
12848     /**
12849      * Customizable piece of the default paging text (defaults to "Page")
12850      * @type String
12851      */
12852     beforePageText : "Page",
12853     /**
12854      * Customizable piece of the default paging text (defaults to "of %0")
12855      * @type String
12856      */
12857     afterPageText : "of {0}",
12858     /**
12859      * Customizable piece of the default paging text (defaults to "First Page")
12860      * @type String
12861      */
12862     firstText : "First Page",
12863     /**
12864      * Customizable piece of the default paging text (defaults to "Previous Page")
12865      * @type String
12866      */
12867     prevText : "Previous Page",
12868     /**
12869      * Customizable piece of the default paging text (defaults to "Next Page")
12870      * @type String
12871      */
12872     nextText : "Next Page",
12873     /**
12874      * Customizable piece of the default paging text (defaults to "Last Page")
12875      * @type String
12876      */
12877     lastText : "Last Page",
12878     /**
12879      * Customizable piece of the default paging text (defaults to "Refresh")
12880      * @type String
12881      */
12882     refreshText : "Refresh",
12883
12884     // private
12885     renderButtons : function(el){
12886         Roo.PagingToolbar.superclass.render.call(this, el);
12887         this.first = this.addButton({
12888             tooltip: this.firstText,
12889             cls: "x-btn-icon x-grid-page-first",
12890             disabled: true,
12891             handler: this.onClick.createDelegate(this, ["first"])
12892         });
12893         this.prev = this.addButton({
12894             tooltip: this.prevText,
12895             cls: "x-btn-icon x-grid-page-prev",
12896             disabled: true,
12897             handler: this.onClick.createDelegate(this, ["prev"])
12898         });
12899         //this.addSeparator();
12900         this.add(this.beforePageText);
12901         this.field = Roo.get(this.addDom({
12902            tag: "input",
12903            type: "text",
12904            size: "3",
12905            value: "1",
12906            cls: "x-grid-page-number"
12907         }).el);
12908         this.field.on("keydown", this.onPagingKeydown, this);
12909         this.field.on("focus", function(){this.dom.select();});
12910         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12911         this.field.setHeight(18);
12912         //this.addSeparator();
12913         this.next = this.addButton({
12914             tooltip: this.nextText,
12915             cls: "x-btn-icon x-grid-page-next",
12916             disabled: true,
12917             handler: this.onClick.createDelegate(this, ["next"])
12918         });
12919         this.last = this.addButton({
12920             tooltip: this.lastText,
12921             cls: "x-btn-icon x-grid-page-last",
12922             disabled: true,
12923             handler: this.onClick.createDelegate(this, ["last"])
12924         });
12925         //this.addSeparator();
12926         this.loading = this.addButton({
12927             tooltip: this.refreshText,
12928             cls: "x-btn-icon x-grid-loading",
12929             handler: this.onClick.createDelegate(this, ["refresh"])
12930         });
12931
12932         if(this.displayInfo){
12933             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12934         }
12935     },
12936
12937     // private
12938     updateInfo : function(){
12939         if(this.displayEl){
12940             var count = this.ds.getCount();
12941             var msg = count == 0 ?
12942                 this.emptyMsg :
12943                 String.format(
12944                     this.displayMsg,
12945                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12946                 );
12947             this.displayEl.update(msg);
12948         }
12949     },
12950
12951     // private
12952     onLoad : function(ds, r, o){
12953        this.cursor = o.params ? o.params.start : 0;
12954        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12955
12956        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12957        this.field.dom.value = ap;
12958        this.first.setDisabled(ap == 1);
12959        this.prev.setDisabled(ap == 1);
12960        this.next.setDisabled(ap == ps);
12961        this.last.setDisabled(ap == ps);
12962        this.loading.enable();
12963        this.updateInfo();
12964     },
12965
12966     // private
12967     getPageData : function(){
12968         var total = this.ds.getTotalCount();
12969         return {
12970             total : total,
12971             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12972             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12973         };
12974     },
12975
12976     // private
12977     onLoadError : function(){
12978         this.loading.enable();
12979     },
12980
12981     // private
12982     onPagingKeydown : function(e){
12983         var k = e.getKey();
12984         var d = this.getPageData();
12985         if(k == e.RETURN){
12986             var v = this.field.dom.value, pageNum;
12987             if(!v || isNaN(pageNum = parseInt(v, 10))){
12988                 this.field.dom.value = d.activePage;
12989                 return;
12990             }
12991             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
12992             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
12993             e.stopEvent();
12994         }
12995         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
12996         {
12997           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
12998           this.field.dom.value = pageNum;
12999           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13000           e.stopEvent();
13001         }
13002         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13003         {
13004           var v = this.field.dom.value, pageNum; 
13005           var increment = (e.shiftKey) ? 10 : 1;
13006           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13007             increment *= -1;
13008           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13009             this.field.dom.value = d.activePage;
13010             return;
13011           }
13012           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13013           {
13014             this.field.dom.value = parseInt(v, 10) + increment;
13015             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13016             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13017           }
13018           e.stopEvent();
13019         }
13020     },
13021
13022     // private
13023     beforeLoad : function(){
13024         if(this.loading){
13025             this.loading.disable();
13026         }
13027     },
13028
13029     // private
13030     onClick : function(which){
13031         var ds = this.ds;
13032         switch(which){
13033             case "first":
13034                 ds.load({params:{start: 0, limit: this.pageSize}});
13035             break;
13036             case "prev":
13037                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13038             break;
13039             case "next":
13040                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13041             break;
13042             case "last":
13043                 var total = ds.getTotalCount();
13044                 var extra = total % this.pageSize;
13045                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13046                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13047             break;
13048             case "refresh":
13049                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13050             break;
13051         }
13052     },
13053
13054     /**
13055      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13056      * @param {Roo.data.Store} store The data store to unbind
13057      */
13058     unbind : function(ds){
13059         ds.un("beforeload", this.beforeLoad, this);
13060         ds.un("load", this.onLoad, this);
13061         ds.un("loadexception", this.onLoadError, this);
13062         ds.un("remove", this.updateInfo, this);
13063         ds.un("add", this.updateInfo, this);
13064         this.ds = undefined;
13065     },
13066
13067     /**
13068      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13069      * @param {Roo.data.Store} store The data store to bind
13070      */
13071     bind : function(ds){
13072         ds.on("beforeload", this.beforeLoad, this);
13073         ds.on("load", this.onLoad, this);
13074         ds.on("loadexception", this.onLoadError, this);
13075         ds.on("remove", this.updateInfo, this);
13076         ds.on("add", this.updateInfo, this);
13077         this.ds = ds;
13078     }
13079 });/*
13080  * Based on:
13081  * Ext JS Library 1.1.1
13082  * Copyright(c) 2006-2007, Ext JS, LLC.
13083  *
13084  * Originally Released Under LGPL - original licence link has changed is not relivant.
13085  *
13086  * Fork - LGPL
13087  * <script type="text/javascript">
13088  */
13089
13090 /**
13091  * @class Roo.Resizable
13092  * @extends Roo.util.Observable
13093  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13094  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13095  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
13096  * the element will be wrapped for you automatically.</p>
13097  * <p>Here is the list of valid resize handles:</p>
13098  * <pre>
13099 Value   Description
13100 ------  -------------------
13101  'n'     north
13102  's'     south
13103  'e'     east
13104  'w'     west
13105  'nw'    northwest
13106  'sw'    southwest
13107  'se'    southeast
13108  'ne'    northeast
13109  'hd'    horizontal drag
13110  'all'   all
13111 </pre>
13112  * <p>Here's an example showing the creation of a typical Resizable:</p>
13113  * <pre><code>
13114 var resizer = new Roo.Resizable("element-id", {
13115     handles: 'all',
13116     minWidth: 200,
13117     minHeight: 100,
13118     maxWidth: 500,
13119     maxHeight: 400,
13120     pinned: true
13121 });
13122 resizer.on("resize", myHandler);
13123 </code></pre>
13124  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13125  * resizer.east.setDisplayed(false);</p>
13126  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13127  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13128  * resize operation's new size (defaults to [0, 0])
13129  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13130  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13131  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13132  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13133  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13134  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13135  * @cfg {Number} width The width of the element in pixels (defaults to null)
13136  * @cfg {Number} height The height of the element in pixels (defaults to null)
13137  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13138  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13139  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13140  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13141  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13142  * in favor of the handles config option (defaults to false)
13143  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13144  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13145  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13146  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13147  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13148  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13149  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13150  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13151  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13152  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13153  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13154  * @constructor
13155  * Create a new resizable component
13156  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13157  * @param {Object} config configuration options
13158   */
13159 Roo.Resizable = function(el, config)
13160 {
13161     this.el = Roo.get(el);
13162
13163     if(config && config.wrap){
13164         config.resizeChild = this.el;
13165         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13166         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13167         this.el.setStyle("overflow", "hidden");
13168         this.el.setPositioning(config.resizeChild.getPositioning());
13169         config.resizeChild.clearPositioning();
13170         if(!config.width || !config.height){
13171             var csize = config.resizeChild.getSize();
13172             this.el.setSize(csize.width, csize.height);
13173         }
13174         if(config.pinned && !config.adjustments){
13175             config.adjustments = "auto";
13176         }
13177     }
13178
13179     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13180     this.proxy.unselectable();
13181     this.proxy.enableDisplayMode('block');
13182
13183     Roo.apply(this, config);
13184
13185     if(this.pinned){
13186         this.disableTrackOver = true;
13187         this.el.addClass("x-resizable-pinned");
13188     }
13189     // if the element isn't positioned, make it relative
13190     var position = this.el.getStyle("position");
13191     if(position != "absolute" && position != "fixed"){
13192         this.el.setStyle("position", "relative");
13193     }
13194     if(!this.handles){ // no handles passed, must be legacy style
13195         this.handles = 's,e,se';
13196         if(this.multiDirectional){
13197             this.handles += ',n,w';
13198         }
13199     }
13200     if(this.handles == "all"){
13201         this.handles = "n s e w ne nw se sw";
13202     }
13203     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13204     var ps = Roo.Resizable.positions;
13205     for(var i = 0, len = hs.length; i < len; i++){
13206         if(hs[i] && ps[hs[i]]){
13207             var pos = ps[hs[i]];
13208             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13209         }
13210     }
13211     // legacy
13212     this.corner = this.southeast;
13213     
13214     // updateBox = the box can move..
13215     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13216         this.updateBox = true;
13217     }
13218
13219     this.activeHandle = null;
13220
13221     if(this.resizeChild){
13222         if(typeof this.resizeChild == "boolean"){
13223             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13224         }else{
13225             this.resizeChild = Roo.get(this.resizeChild, true);
13226         }
13227     }
13228     
13229     if(this.adjustments == "auto"){
13230         var rc = this.resizeChild;
13231         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13232         if(rc && (hw || hn)){
13233             rc.position("relative");
13234             rc.setLeft(hw ? hw.el.getWidth() : 0);
13235             rc.setTop(hn ? hn.el.getHeight() : 0);
13236         }
13237         this.adjustments = [
13238             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13239             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13240         ];
13241     }
13242
13243     if(this.draggable){
13244         this.dd = this.dynamic ?
13245             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13246         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13247     }
13248
13249     // public events
13250     this.addEvents({
13251         /**
13252          * @event beforeresize
13253          * Fired before resize is allowed. Set enabled to false to cancel resize.
13254          * @param {Roo.Resizable} this
13255          * @param {Roo.EventObject} e The mousedown event
13256          */
13257         "beforeresize" : true,
13258         /**
13259          * @event resize
13260          * Fired after a resize.
13261          * @param {Roo.Resizable} this
13262          * @param {Number} width The new width
13263          * @param {Number} height The new height
13264          * @param {Roo.EventObject} e The mouseup event
13265          */
13266         "resize" : true
13267     });
13268
13269     if(this.width !== null && this.height !== null){
13270         this.resizeTo(this.width, this.height);
13271     }else{
13272         this.updateChildSize();
13273     }
13274     if(Roo.isIE){
13275         this.el.dom.style.zoom = 1;
13276     }
13277     Roo.Resizable.superclass.constructor.call(this);
13278 };
13279
13280 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13281         resizeChild : false,
13282         adjustments : [0, 0],
13283         minWidth : 5,
13284         minHeight : 5,
13285         maxWidth : 10000,
13286         maxHeight : 10000,
13287         enabled : true,
13288         animate : false,
13289         duration : .35,
13290         dynamic : false,
13291         handles : false,
13292         multiDirectional : false,
13293         disableTrackOver : false,
13294         easing : 'easeOutStrong',
13295         widthIncrement : 0,
13296         heightIncrement : 0,
13297         pinned : false,
13298         width : null,
13299         height : null,
13300         preserveRatio : false,
13301         transparent: false,
13302         minX: 0,
13303         minY: 0,
13304         draggable: false,
13305
13306         /**
13307          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13308          */
13309         constrainTo: undefined,
13310         /**
13311          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13312          */
13313         resizeRegion: undefined,
13314
13315
13316     /**
13317      * Perform a manual resize
13318      * @param {Number} width
13319      * @param {Number} height
13320      */
13321     resizeTo : function(width, height){
13322         this.el.setSize(width, height);
13323         this.updateChildSize();
13324         this.fireEvent("resize", this, width, height, null);
13325     },
13326
13327     // private
13328     startSizing : function(e, handle){
13329         this.fireEvent("beforeresize", this, e);
13330         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13331
13332             if(!this.overlay){
13333                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13334                 this.overlay.unselectable();
13335                 this.overlay.enableDisplayMode("block");
13336                 this.overlay.on("mousemove", this.onMouseMove, this);
13337                 this.overlay.on("mouseup", this.onMouseUp, this);
13338             }
13339             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13340
13341             this.resizing = true;
13342             this.startBox = this.el.getBox();
13343             this.startPoint = e.getXY();
13344             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13345                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13346
13347             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13348             this.overlay.show();
13349
13350             if(this.constrainTo) {
13351                 var ct = Roo.get(this.constrainTo);
13352                 this.resizeRegion = ct.getRegion().adjust(
13353                     ct.getFrameWidth('t'),
13354                     ct.getFrameWidth('l'),
13355                     -ct.getFrameWidth('b'),
13356                     -ct.getFrameWidth('r')
13357                 );
13358             }
13359
13360             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13361             this.proxy.show();
13362             this.proxy.setBox(this.startBox);
13363             if(!this.dynamic){
13364                 this.proxy.setStyle('visibility', 'visible');
13365             }
13366         }
13367     },
13368
13369     // private
13370     onMouseDown : function(handle, e){
13371         if(this.enabled){
13372             e.stopEvent();
13373             this.activeHandle = handle;
13374             this.startSizing(e, handle);
13375         }
13376     },
13377
13378     // private
13379     onMouseUp : function(e){
13380         var size = this.resizeElement();
13381         this.resizing = false;
13382         this.handleOut();
13383         this.overlay.hide();
13384         this.proxy.hide();
13385         this.fireEvent("resize", this, size.width, size.height, e);
13386     },
13387
13388     // private
13389     updateChildSize : function(){
13390         if(this.resizeChild){
13391             var el = this.el;
13392             var child = this.resizeChild;
13393             var adj = this.adjustments;
13394             if(el.dom.offsetWidth){
13395                 var b = el.getSize(true);
13396                 child.setSize(b.width+adj[0], b.height+adj[1]);
13397             }
13398             // Second call here for IE
13399             // The first call enables instant resizing and
13400             // the second call corrects scroll bars if they
13401             // exist
13402             if(Roo.isIE){
13403                 setTimeout(function(){
13404                     if(el.dom.offsetWidth){
13405                         var b = el.getSize(true);
13406                         child.setSize(b.width+adj[0], b.height+adj[1]);
13407                     }
13408                 }, 10);
13409             }
13410         }
13411     },
13412
13413     // private
13414     snap : function(value, inc, min){
13415         if(!inc || !value) return value;
13416         var newValue = value;
13417         var m = value % inc;
13418         if(m > 0){
13419             if(m > (inc/2)){
13420                 newValue = value + (inc-m);
13421             }else{
13422                 newValue = value - m;
13423             }
13424         }
13425         return Math.max(min, newValue);
13426     },
13427
13428     // private
13429     resizeElement : function(){
13430         var box = this.proxy.getBox();
13431         if(this.updateBox){
13432             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13433         }else{
13434             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13435         }
13436         this.updateChildSize();
13437         if(!this.dynamic){
13438             this.proxy.hide();
13439         }
13440         return box;
13441     },
13442
13443     // private
13444     constrain : function(v, diff, m, mx){
13445         if(v - diff < m){
13446             diff = v - m;
13447         }else if(v - diff > mx){
13448             diff = mx - v;
13449         }
13450         return diff;
13451     },
13452
13453     // private
13454     onMouseMove : function(e){
13455         if(this.enabled){
13456             try{// try catch so if something goes wrong the user doesn't get hung
13457
13458             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13459                 return;
13460             }
13461
13462             //var curXY = this.startPoint;
13463             var curSize = this.curSize || this.startBox;
13464             var x = this.startBox.x, y = this.startBox.y;
13465             var ox = x, oy = y;
13466             var w = curSize.width, h = curSize.height;
13467             var ow = w, oh = h;
13468             var mw = this.minWidth, mh = this.minHeight;
13469             var mxw = this.maxWidth, mxh = this.maxHeight;
13470             var wi = this.widthIncrement;
13471             var hi = this.heightIncrement;
13472
13473             var eventXY = e.getXY();
13474             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13475             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13476
13477             var pos = this.activeHandle.position;
13478
13479             switch(pos){
13480                 case "east":
13481                     w += diffX;
13482                     w = Math.min(Math.max(mw, w), mxw);
13483                     break;
13484              
13485                 case "south":
13486                     h += diffY;
13487                     h = Math.min(Math.max(mh, h), mxh);
13488                     break;
13489                 case "southeast":
13490                     w += diffX;
13491                     h += diffY;
13492                     w = Math.min(Math.max(mw, w), mxw);
13493                     h = Math.min(Math.max(mh, h), mxh);
13494                     break;
13495                 case "north":
13496                     diffY = this.constrain(h, diffY, mh, mxh);
13497                     y += diffY;
13498                     h -= diffY;
13499                     break;
13500                 case "hdrag":
13501                     
13502                     if (wi) {
13503                         var adiffX = Math.abs(diffX);
13504                         var sub = (adiffX % wi); // how much 
13505                         if (sub > (wi/2)) { // far enough to snap
13506                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13507                         } else {
13508                             // remove difference.. 
13509                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13510                         }
13511                     }
13512                     x += diffX;
13513                     x = Math.max(this.minX, x);
13514                     break;
13515                 case "west":
13516                     diffX = this.constrain(w, diffX, mw, mxw);
13517                     x += diffX;
13518                     w -= diffX;
13519                     break;
13520                 case "northeast":
13521                     w += diffX;
13522                     w = Math.min(Math.max(mw, w), mxw);
13523                     diffY = this.constrain(h, diffY, mh, mxh);
13524                     y += diffY;
13525                     h -= diffY;
13526                     break;
13527                 case "northwest":
13528                     diffX = this.constrain(w, diffX, mw, mxw);
13529                     diffY = this.constrain(h, diffY, mh, mxh);
13530                     y += diffY;
13531                     h -= diffY;
13532                     x += diffX;
13533                     w -= diffX;
13534                     break;
13535                case "southwest":
13536                     diffX = this.constrain(w, diffX, mw, mxw);
13537                     h += diffY;
13538                     h = Math.min(Math.max(mh, h), mxh);
13539                     x += diffX;
13540                     w -= diffX;
13541                     break;
13542             }
13543
13544             var sw = this.snap(w, wi, mw);
13545             var sh = this.snap(h, hi, mh);
13546             if(sw != w || sh != h){
13547                 switch(pos){
13548                     case "northeast":
13549                         y -= sh - h;
13550                     break;
13551                     case "north":
13552                         y -= sh - h;
13553                         break;
13554                     case "southwest":
13555                         x -= sw - w;
13556                     break;
13557                     case "west":
13558                         x -= sw - w;
13559                         break;
13560                     case "northwest":
13561                         x -= sw - w;
13562                         y -= sh - h;
13563                     break;
13564                 }
13565                 w = sw;
13566                 h = sh;
13567             }
13568
13569             if(this.preserveRatio){
13570                 switch(pos){
13571                     case "southeast":
13572                     case "east":
13573                         h = oh * (w/ow);
13574                         h = Math.min(Math.max(mh, h), mxh);
13575                         w = ow * (h/oh);
13576                        break;
13577                     case "south":
13578                         w = ow * (h/oh);
13579                         w = Math.min(Math.max(mw, w), mxw);
13580                         h = oh * (w/ow);
13581                         break;
13582                     case "northeast":
13583                         w = ow * (h/oh);
13584                         w = Math.min(Math.max(mw, w), mxw);
13585                         h = oh * (w/ow);
13586                     break;
13587                     case "north":
13588                         var tw = w;
13589                         w = ow * (h/oh);
13590                         w = Math.min(Math.max(mw, w), mxw);
13591                         h = oh * (w/ow);
13592                         x += (tw - w) / 2;
13593                         break;
13594                     case "southwest":
13595                         h = oh * (w/ow);
13596                         h = Math.min(Math.max(mh, h), mxh);
13597                         var tw = w;
13598                         w = ow * (h/oh);
13599                         x += tw - w;
13600                         break;
13601                     case "west":
13602                         var th = h;
13603                         h = oh * (w/ow);
13604                         h = Math.min(Math.max(mh, h), mxh);
13605                         y += (th - h) / 2;
13606                         var tw = w;
13607                         w = ow * (h/oh);
13608                         x += tw - w;
13609                        break;
13610                     case "northwest":
13611                         var tw = w;
13612                         var th = h;
13613                         h = oh * (w/ow);
13614                         h = Math.min(Math.max(mh, h), mxh);
13615                         w = ow * (h/oh);
13616                         y += th - h;
13617                         x += tw - w;
13618                        break;
13619
13620                 }
13621             }
13622             if (pos == 'hdrag') {
13623                 w = ow;
13624             }
13625             this.proxy.setBounds(x, y, w, h);
13626             if(this.dynamic){
13627                 this.resizeElement();
13628             }
13629             }catch(e){}
13630         }
13631     },
13632
13633     // private
13634     handleOver : function(){
13635         if(this.enabled){
13636             this.el.addClass("x-resizable-over");
13637         }
13638     },
13639
13640     // private
13641     handleOut : function(){
13642         if(!this.resizing){
13643             this.el.removeClass("x-resizable-over");
13644         }
13645     },
13646
13647     /**
13648      * Returns the element this component is bound to.
13649      * @return {Roo.Element}
13650      */
13651     getEl : function(){
13652         return this.el;
13653     },
13654
13655     /**
13656      * Returns the resizeChild element (or null).
13657      * @return {Roo.Element}
13658      */
13659     getResizeChild : function(){
13660         return this.resizeChild;
13661     },
13662
13663     /**
13664      * Destroys this resizable. If the element was wrapped and
13665      * removeEl is not true then the element remains.
13666      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13667      */
13668     destroy : function(removeEl){
13669         this.proxy.remove();
13670         if(this.overlay){
13671             this.overlay.removeAllListeners();
13672             this.overlay.remove();
13673         }
13674         var ps = Roo.Resizable.positions;
13675         for(var k in ps){
13676             if(typeof ps[k] != "function" && this[ps[k]]){
13677                 var h = this[ps[k]];
13678                 h.el.removeAllListeners();
13679                 h.el.remove();
13680             }
13681         }
13682         if(removeEl){
13683             this.el.update("");
13684             this.el.remove();
13685         }
13686     }
13687 });
13688
13689 // private
13690 // hash to map config positions to true positions
13691 Roo.Resizable.positions = {
13692     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13693     hd: "hdrag"
13694 };
13695
13696 // private
13697 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13698     if(!this.tpl){
13699         // only initialize the template if resizable is used
13700         var tpl = Roo.DomHelper.createTemplate(
13701             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13702         );
13703         tpl.compile();
13704         Roo.Resizable.Handle.prototype.tpl = tpl;
13705     }
13706     this.position = pos;
13707     this.rz = rz;
13708     // show north drag fro topdra
13709     var handlepos = pos == 'hdrag' ? 'north' : pos;
13710     
13711     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13712     if (pos == 'hdrag') {
13713         this.el.setStyle('cursor', 'pointer');
13714     }
13715     this.el.unselectable();
13716     if(transparent){
13717         this.el.setOpacity(0);
13718     }
13719     this.el.on("mousedown", this.onMouseDown, this);
13720     if(!disableTrackOver){
13721         this.el.on("mouseover", this.onMouseOver, this);
13722         this.el.on("mouseout", this.onMouseOut, this);
13723     }
13724 };
13725
13726 // private
13727 Roo.Resizable.Handle.prototype = {
13728     afterResize : function(rz){
13729         // do nothing
13730     },
13731     // private
13732     onMouseDown : function(e){
13733         this.rz.onMouseDown(this, e);
13734     },
13735     // private
13736     onMouseOver : function(e){
13737         this.rz.handleOver(this, e);
13738     },
13739     // private
13740     onMouseOut : function(e){
13741         this.rz.handleOut(this, e);
13742     }
13743 };/*
13744  * Based on:
13745  * Ext JS Library 1.1.1
13746  * Copyright(c) 2006-2007, Ext JS, LLC.
13747  *
13748  * Originally Released Under LGPL - original licence link has changed is not relivant.
13749  *
13750  * Fork - LGPL
13751  * <script type="text/javascript">
13752  */
13753
13754 /**
13755  * @class Roo.Editor
13756  * @extends Roo.Component
13757  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13758  * @constructor
13759  * Create a new Editor
13760  * @param {Roo.form.Field} field The Field object (or descendant)
13761  * @param {Object} config The config object
13762  */
13763 Roo.Editor = function(field, config){
13764     Roo.Editor.superclass.constructor.call(this, config);
13765     this.field = field;
13766     this.addEvents({
13767         /**
13768              * @event beforestartedit
13769              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13770              * false from the handler of this event.
13771              * @param {Editor} this
13772              * @param {Roo.Element} boundEl The underlying element bound to this editor
13773              * @param {Mixed} value The field value being set
13774              */
13775         "beforestartedit" : true,
13776         /**
13777              * @event startedit
13778              * Fires when this editor is displayed
13779              * @param {Roo.Element} boundEl The underlying element bound to this editor
13780              * @param {Mixed} value The starting field value
13781              */
13782         "startedit" : true,
13783         /**
13784              * @event beforecomplete
13785              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13786              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13787              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13788              * event will not fire since no edit actually occurred.
13789              * @param {Editor} this
13790              * @param {Mixed} value The current field value
13791              * @param {Mixed} startValue The original field value
13792              */
13793         "beforecomplete" : true,
13794         /**
13795              * @event complete
13796              * Fires after editing is complete and any changed value has been written to the underlying field.
13797              * @param {Editor} this
13798              * @param {Mixed} value The current field value
13799              * @param {Mixed} startValue The original field value
13800              */
13801         "complete" : true,
13802         /**
13803          * @event specialkey
13804          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13805          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13806          * @param {Roo.form.Field} this
13807          * @param {Roo.EventObject} e The event object
13808          */
13809         "specialkey" : true
13810     });
13811 };
13812
13813 Roo.extend(Roo.Editor, Roo.Component, {
13814     /**
13815      * @cfg {Boolean/String} autosize
13816      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13817      * or "height" to adopt the height only (defaults to false)
13818      */
13819     /**
13820      * @cfg {Boolean} revertInvalid
13821      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13822      * validation fails (defaults to true)
13823      */
13824     /**
13825      * @cfg {Boolean} ignoreNoChange
13826      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13827      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13828      * will never be ignored.
13829      */
13830     /**
13831      * @cfg {Boolean} hideEl
13832      * False to keep the bound element visible while the editor is displayed (defaults to true)
13833      */
13834     /**
13835      * @cfg {Mixed} value
13836      * The data value of the underlying field (defaults to "")
13837      */
13838     value : "",
13839     /**
13840      * @cfg {String} alignment
13841      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13842      */
13843     alignment: "c-c?",
13844     /**
13845      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13846      * for bottom-right shadow (defaults to "frame")
13847      */
13848     shadow : "frame",
13849     /**
13850      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13851      */
13852     constrain : false,
13853     /**
13854      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13855      */
13856     completeOnEnter : false,
13857     /**
13858      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13859      */
13860     cancelOnEsc : false,
13861     /**
13862      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13863      */
13864     updateEl : false,
13865
13866     // private
13867     onRender : function(ct, position){
13868         this.el = new Roo.Layer({
13869             shadow: this.shadow,
13870             cls: "x-editor",
13871             parentEl : ct,
13872             shim : this.shim,
13873             shadowOffset:4,
13874             id: this.id,
13875             constrain: this.constrain
13876         });
13877         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13878         if(this.field.msgTarget != 'title'){
13879             this.field.msgTarget = 'qtip';
13880         }
13881         this.field.render(this.el);
13882         if(Roo.isGecko){
13883             this.field.el.dom.setAttribute('autocomplete', 'off');
13884         }
13885         this.field.on("specialkey", this.onSpecialKey, this);
13886         if(this.swallowKeys){
13887             this.field.el.swallowEvent(['keydown','keypress']);
13888         }
13889         this.field.show();
13890         this.field.on("blur", this.onBlur, this);
13891         if(this.field.grow){
13892             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13893         }
13894     },
13895
13896     onSpecialKey : function(field, e){
13897         //Roo.log('editor onSpecialKey');
13898         if(this.completeOnEnter && e.getKey() == e.ENTER){
13899             e.stopEvent();
13900             this.completeEdit();
13901         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13902             this.cancelEdit();
13903         }else{
13904             this.fireEvent('specialkey', field, e);
13905         }
13906     },
13907
13908     /**
13909      * Starts the editing process and shows the editor.
13910      * @param {String/HTMLElement/Element} el The element to edit
13911      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13912       * to the innerHTML of el.
13913      */
13914     startEdit : function(el, value){
13915         if(this.editing){
13916             this.completeEdit();
13917         }
13918         this.boundEl = Roo.get(el);
13919         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13920         if(!this.rendered){
13921             this.render(this.parentEl || document.body);
13922         }
13923         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13924             return;
13925         }
13926         this.startValue = v;
13927         this.field.setValue(v);
13928         if(this.autoSize){
13929             var sz = this.boundEl.getSize();
13930             switch(this.autoSize){
13931                 case "width":
13932                 this.setSize(sz.width,  "");
13933                 break;
13934                 case "height":
13935                 this.setSize("",  sz.height);
13936                 break;
13937                 default:
13938                 this.setSize(sz.width,  sz.height);
13939             }
13940         }
13941         this.el.alignTo(this.boundEl, this.alignment);
13942         this.editing = true;
13943         if(Roo.QuickTips){
13944             Roo.QuickTips.disable();
13945         }
13946         this.show();
13947     },
13948
13949     /**
13950      * Sets the height and width of this editor.
13951      * @param {Number} width The new width
13952      * @param {Number} height The new height
13953      */
13954     setSize : function(w, h){
13955         this.field.setSize(w, h);
13956         if(this.el){
13957             this.el.sync();
13958         }
13959     },
13960
13961     /**
13962      * Realigns the editor to the bound field based on the current alignment config value.
13963      */
13964     realign : function(){
13965         this.el.alignTo(this.boundEl, this.alignment);
13966     },
13967
13968     /**
13969      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13970      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13971      */
13972     completeEdit : function(remainVisible){
13973         if(!this.editing){
13974             return;
13975         }
13976         var v = this.getValue();
13977         if(this.revertInvalid !== false && !this.field.isValid()){
13978             v = this.startValue;
13979             this.cancelEdit(true);
13980         }
13981         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13982             this.editing = false;
13983             this.hide();
13984             return;
13985         }
13986         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
13987             this.editing = false;
13988             if(this.updateEl && this.boundEl){
13989                 this.boundEl.update(v);
13990             }
13991             if(remainVisible !== true){
13992                 this.hide();
13993             }
13994             this.fireEvent("complete", this, v, this.startValue);
13995         }
13996     },
13997
13998     // private
13999     onShow : function(){
14000         this.el.show();
14001         if(this.hideEl !== false){
14002             this.boundEl.hide();
14003         }
14004         this.field.show();
14005         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14006             this.fixIEFocus = true;
14007             this.deferredFocus.defer(50, this);
14008         }else{
14009             this.field.focus();
14010         }
14011         this.fireEvent("startedit", this.boundEl, this.startValue);
14012     },
14013
14014     deferredFocus : function(){
14015         if(this.editing){
14016             this.field.focus();
14017         }
14018     },
14019
14020     /**
14021      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14022      * reverted to the original starting value.
14023      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14024      * cancel (defaults to false)
14025      */
14026     cancelEdit : function(remainVisible){
14027         if(this.editing){
14028             this.setValue(this.startValue);
14029             if(remainVisible !== true){
14030                 this.hide();
14031             }
14032         }
14033     },
14034
14035     // private
14036     onBlur : function(){
14037         if(this.allowBlur !== true && this.editing){
14038             this.completeEdit();
14039         }
14040     },
14041
14042     // private
14043     onHide : function(){
14044         if(this.editing){
14045             this.completeEdit();
14046             return;
14047         }
14048         this.field.blur();
14049         if(this.field.collapse){
14050             this.field.collapse();
14051         }
14052         this.el.hide();
14053         if(this.hideEl !== false){
14054             this.boundEl.show();
14055         }
14056         if(Roo.QuickTips){
14057             Roo.QuickTips.enable();
14058         }
14059     },
14060
14061     /**
14062      * Sets the data value of the editor
14063      * @param {Mixed} value Any valid value supported by the underlying field
14064      */
14065     setValue : function(v){
14066         this.field.setValue(v);
14067     },
14068
14069     /**
14070      * Gets the data value of the editor
14071      * @return {Mixed} The data value
14072      */
14073     getValue : function(){
14074         return this.field.getValue();
14075     }
14076 });/*
14077  * Based on:
14078  * Ext JS Library 1.1.1
14079  * Copyright(c) 2006-2007, Ext JS, LLC.
14080  *
14081  * Originally Released Under LGPL - original licence link has changed is not relivant.
14082  *
14083  * Fork - LGPL
14084  * <script type="text/javascript">
14085  */
14086  
14087 /**
14088  * @class Roo.BasicDialog
14089  * @extends Roo.util.Observable
14090  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14091  * <pre><code>
14092 var dlg = new Roo.BasicDialog("my-dlg", {
14093     height: 200,
14094     width: 300,
14095     minHeight: 100,
14096     minWidth: 150,
14097     modal: true,
14098     proxyDrag: true,
14099     shadow: true
14100 });
14101 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14102 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14103 dlg.addButton('Cancel', dlg.hide, dlg);
14104 dlg.show();
14105 </code></pre>
14106   <b>A Dialog should always be a direct child of the body element.</b>
14107  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14108  * @cfg {String} title Default text to display in the title bar (defaults to null)
14109  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14110  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14111  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14112  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14113  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14114  * (defaults to null with no animation)
14115  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14116  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14117  * property for valid values (defaults to 'all')
14118  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14119  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14120  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14121  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14122  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14123  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14124  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14125  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14126  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14127  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14128  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14129  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14130  * draggable = true (defaults to false)
14131  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14132  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14133  * shadow (defaults to false)
14134  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14135  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14136  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14137  * @cfg {Array} buttons Array of buttons
14138  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14139  * @constructor
14140  * Create a new BasicDialog.
14141  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14142  * @param {Object} config Configuration options
14143  */
14144 Roo.BasicDialog = function(el, config){
14145     this.el = Roo.get(el);
14146     var dh = Roo.DomHelper;
14147     if(!this.el && config && config.autoCreate){
14148         if(typeof config.autoCreate == "object"){
14149             if(!config.autoCreate.id){
14150                 config.autoCreate.id = el;
14151             }
14152             this.el = dh.append(document.body,
14153                         config.autoCreate, true);
14154         }else{
14155             this.el = dh.append(document.body,
14156                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14157         }
14158     }
14159     el = this.el;
14160     el.setDisplayed(true);
14161     el.hide = this.hideAction;
14162     this.id = el.id;
14163     el.addClass("x-dlg");
14164
14165     Roo.apply(this, config);
14166
14167     this.proxy = el.createProxy("x-dlg-proxy");
14168     this.proxy.hide = this.hideAction;
14169     this.proxy.setOpacity(.5);
14170     this.proxy.hide();
14171
14172     if(config.width){
14173         el.setWidth(config.width);
14174     }
14175     if(config.height){
14176         el.setHeight(config.height);
14177     }
14178     this.size = el.getSize();
14179     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14180         this.xy = [config.x,config.y];
14181     }else{
14182         this.xy = el.getCenterXY(true);
14183     }
14184     /** The header element @type Roo.Element */
14185     this.header = el.child("> .x-dlg-hd");
14186     /** The body element @type Roo.Element */
14187     this.body = el.child("> .x-dlg-bd");
14188     /** The footer element @type Roo.Element */
14189     this.footer = el.child("> .x-dlg-ft");
14190
14191     if(!this.header){
14192         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14193     }
14194     if(!this.body){
14195         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14196     }
14197
14198     this.header.unselectable();
14199     if(this.title){
14200         this.header.update(this.title);
14201     }
14202     // this element allows the dialog to be focused for keyboard event
14203     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14204     this.focusEl.swallowEvent("click", true);
14205
14206     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14207
14208     // wrap the body and footer for special rendering
14209     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14210     if(this.footer){
14211         this.bwrap.dom.appendChild(this.footer.dom);
14212     }
14213
14214     this.bg = this.el.createChild({
14215         tag: "div", cls:"x-dlg-bg",
14216         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14217     });
14218     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14219
14220
14221     if(this.autoScroll !== false && !this.autoTabs){
14222         this.body.setStyle("overflow", "auto");
14223     }
14224
14225     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14226
14227     if(this.closable !== false){
14228         this.el.addClass("x-dlg-closable");
14229         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14230         this.close.on("click", this.closeClick, this);
14231         this.close.addClassOnOver("x-dlg-close-over");
14232     }
14233     if(this.collapsible !== false){
14234         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14235         this.collapseBtn.on("click", this.collapseClick, this);
14236         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14237         this.header.on("dblclick", this.collapseClick, this);
14238     }
14239     if(this.resizable !== false){
14240         this.el.addClass("x-dlg-resizable");
14241         this.resizer = new Roo.Resizable(el, {
14242             minWidth: this.minWidth || 80,
14243             minHeight:this.minHeight || 80,
14244             handles: this.resizeHandles || "all",
14245             pinned: true
14246         });
14247         this.resizer.on("beforeresize", this.beforeResize, this);
14248         this.resizer.on("resize", this.onResize, this);
14249     }
14250     if(this.draggable !== false){
14251         el.addClass("x-dlg-draggable");
14252         if (!this.proxyDrag) {
14253             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14254         }
14255         else {
14256             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14257         }
14258         dd.setHandleElId(this.header.id);
14259         dd.endDrag = this.endMove.createDelegate(this);
14260         dd.startDrag = this.startMove.createDelegate(this);
14261         dd.onDrag = this.onDrag.createDelegate(this);
14262         dd.scroll = false;
14263         this.dd = dd;
14264     }
14265     if(this.modal){
14266         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14267         this.mask.enableDisplayMode("block");
14268         this.mask.hide();
14269         this.el.addClass("x-dlg-modal");
14270     }
14271     if(this.shadow){
14272         this.shadow = new Roo.Shadow({
14273             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14274             offset : this.shadowOffset
14275         });
14276     }else{
14277         this.shadowOffset = 0;
14278     }
14279     if(Roo.useShims && this.shim !== false){
14280         this.shim = this.el.createShim();
14281         this.shim.hide = this.hideAction;
14282         this.shim.hide();
14283     }else{
14284         this.shim = false;
14285     }
14286     if(this.autoTabs){
14287         this.initTabs();
14288     }
14289     if (this.buttons) { 
14290         var bts= this.buttons;
14291         this.buttons = [];
14292         Roo.each(bts, function(b) {
14293             this.addButton(b);
14294         }, this);
14295     }
14296     
14297     
14298     this.addEvents({
14299         /**
14300          * @event keydown
14301          * Fires when a key is pressed
14302          * @param {Roo.BasicDialog} this
14303          * @param {Roo.EventObject} e
14304          */
14305         "keydown" : true,
14306         /**
14307          * @event move
14308          * Fires when this dialog is moved by the user.
14309          * @param {Roo.BasicDialog} this
14310          * @param {Number} x The new page X
14311          * @param {Number} y The new page Y
14312          */
14313         "move" : true,
14314         /**
14315          * @event resize
14316          * Fires when this dialog is resized by the user.
14317          * @param {Roo.BasicDialog} this
14318          * @param {Number} width The new width
14319          * @param {Number} height The new height
14320          */
14321         "resize" : true,
14322         /**
14323          * @event beforehide
14324          * Fires before this dialog is hidden.
14325          * @param {Roo.BasicDialog} this
14326          */
14327         "beforehide" : true,
14328         /**
14329          * @event hide
14330          * Fires when this dialog is hidden.
14331          * @param {Roo.BasicDialog} this
14332          */
14333         "hide" : true,
14334         /**
14335          * @event beforeshow
14336          * Fires before this dialog is shown.
14337          * @param {Roo.BasicDialog} this
14338          */
14339         "beforeshow" : true,
14340         /**
14341          * @event show
14342          * Fires when this dialog is shown.
14343          * @param {Roo.BasicDialog} this
14344          */
14345         "show" : true
14346     });
14347     el.on("keydown", this.onKeyDown, this);
14348     el.on("mousedown", this.toFront, this);
14349     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14350     this.el.hide();
14351     Roo.DialogManager.register(this);
14352     Roo.BasicDialog.superclass.constructor.call(this);
14353 };
14354
14355 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14356     shadowOffset: Roo.isIE ? 6 : 5,
14357     minHeight: 80,
14358     minWidth: 200,
14359     minButtonWidth: 75,
14360     defaultButton: null,
14361     buttonAlign: "right",
14362     tabTag: 'div',
14363     firstShow: true,
14364
14365     /**
14366      * Sets the dialog title text
14367      * @param {String} text The title text to display
14368      * @return {Roo.BasicDialog} this
14369      */
14370     setTitle : function(text){
14371         this.header.update(text);
14372         return this;
14373     },
14374
14375     // private
14376     closeClick : function(){
14377         this.hide();
14378     },
14379
14380     // private
14381     collapseClick : function(){
14382         this[this.collapsed ? "expand" : "collapse"]();
14383     },
14384
14385     /**
14386      * Collapses the dialog to its minimized state (only the title bar is visible).
14387      * Equivalent to the user clicking the collapse dialog button.
14388      */
14389     collapse : function(){
14390         if(!this.collapsed){
14391             this.collapsed = true;
14392             this.el.addClass("x-dlg-collapsed");
14393             this.restoreHeight = this.el.getHeight();
14394             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14395         }
14396     },
14397
14398     /**
14399      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14400      * clicking the expand dialog button.
14401      */
14402     expand : function(){
14403         if(this.collapsed){
14404             this.collapsed = false;
14405             this.el.removeClass("x-dlg-collapsed");
14406             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14407         }
14408     },
14409
14410     /**
14411      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14412      * @return {Roo.TabPanel} The tabs component
14413      */
14414     initTabs : function(){
14415         var tabs = this.getTabs();
14416         while(tabs.getTab(0)){
14417             tabs.removeTab(0);
14418         }
14419         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14420             var dom = el.dom;
14421             tabs.addTab(Roo.id(dom), dom.title);
14422             dom.title = "";
14423         });
14424         tabs.activate(0);
14425         return tabs;
14426     },
14427
14428     // private
14429     beforeResize : function(){
14430         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14431     },
14432
14433     // private
14434     onResize : function(){
14435         this.refreshSize();
14436         this.syncBodyHeight();
14437         this.adjustAssets();
14438         this.focus();
14439         this.fireEvent("resize", this, this.size.width, this.size.height);
14440     },
14441
14442     // private
14443     onKeyDown : function(e){
14444         if(this.isVisible()){
14445             this.fireEvent("keydown", this, e);
14446         }
14447     },
14448
14449     /**
14450      * Resizes the dialog.
14451      * @param {Number} width
14452      * @param {Number} height
14453      * @return {Roo.BasicDialog} this
14454      */
14455     resizeTo : function(width, height){
14456         this.el.setSize(width, height);
14457         this.size = {width: width, height: height};
14458         this.syncBodyHeight();
14459         if(this.fixedcenter){
14460             this.center();
14461         }
14462         if(this.isVisible()){
14463             this.constrainXY();
14464             this.adjustAssets();
14465         }
14466         this.fireEvent("resize", this, width, height);
14467         return this;
14468     },
14469
14470
14471     /**
14472      * Resizes the dialog to fit the specified content size.
14473      * @param {Number} width
14474      * @param {Number} height
14475      * @return {Roo.BasicDialog} this
14476      */
14477     setContentSize : function(w, h){
14478         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14479         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14480         //if(!this.el.isBorderBox()){
14481             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14482             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14483         //}
14484         if(this.tabs){
14485             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14486             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14487         }
14488         this.resizeTo(w, h);
14489         return this;
14490     },
14491
14492     /**
14493      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14494      * executed in response to a particular key being pressed while the dialog is active.
14495      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14496      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14497      * @param {Function} fn The function to call
14498      * @param {Object} scope (optional) The scope of the function
14499      * @return {Roo.BasicDialog} this
14500      */
14501     addKeyListener : function(key, fn, scope){
14502         var keyCode, shift, ctrl, alt;
14503         if(typeof key == "object" && !(key instanceof Array)){
14504             keyCode = key["key"];
14505             shift = key["shift"];
14506             ctrl = key["ctrl"];
14507             alt = key["alt"];
14508         }else{
14509             keyCode = key;
14510         }
14511         var handler = function(dlg, e){
14512             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14513                 var k = e.getKey();
14514                 if(keyCode instanceof Array){
14515                     for(var i = 0, len = keyCode.length; i < len; i++){
14516                         if(keyCode[i] == k){
14517                           fn.call(scope || window, dlg, k, e);
14518                           return;
14519                         }
14520                     }
14521                 }else{
14522                     if(k == keyCode){
14523                         fn.call(scope || window, dlg, k, e);
14524                     }
14525                 }
14526             }
14527         };
14528         this.on("keydown", handler);
14529         return this;
14530     },
14531
14532     /**
14533      * Returns the TabPanel component (creates it if it doesn't exist).
14534      * Note: If you wish to simply check for the existence of tabs without creating them,
14535      * check for a null 'tabs' property.
14536      * @return {Roo.TabPanel} The tabs component
14537      */
14538     getTabs : function(){
14539         if(!this.tabs){
14540             this.el.addClass("x-dlg-auto-tabs");
14541             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14542             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14543         }
14544         return this.tabs;
14545     },
14546
14547     /**
14548      * Adds a button to the footer section of the dialog.
14549      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14550      * object or a valid Roo.DomHelper element config
14551      * @param {Function} handler The function called when the button is clicked
14552      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14553      * @return {Roo.Button} The new button
14554      */
14555     addButton : function(config, handler, scope){
14556         var dh = Roo.DomHelper;
14557         if(!this.footer){
14558             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14559         }
14560         if(!this.btnContainer){
14561             var tb = this.footer.createChild({
14562
14563                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14564                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14565             }, null, true);
14566             this.btnContainer = tb.firstChild.firstChild.firstChild;
14567         }
14568         var bconfig = {
14569             handler: handler,
14570             scope: scope,
14571             minWidth: this.minButtonWidth,
14572             hideParent:true
14573         };
14574         if(typeof config == "string"){
14575             bconfig.text = config;
14576         }else{
14577             if(config.tag){
14578                 bconfig.dhconfig = config;
14579             }else{
14580                 Roo.apply(bconfig, config);
14581             }
14582         }
14583         var fc = false;
14584         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14585             bconfig.position = Math.max(0, bconfig.position);
14586             fc = this.btnContainer.childNodes[bconfig.position];
14587         }
14588          
14589         var btn = new Roo.Button(
14590             fc ? 
14591                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14592                 : this.btnContainer.appendChild(document.createElement("td")),
14593             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14594             bconfig
14595         );
14596         this.syncBodyHeight();
14597         if(!this.buttons){
14598             /**
14599              * Array of all the buttons that have been added to this dialog via addButton
14600              * @type Array
14601              */
14602             this.buttons = [];
14603         }
14604         this.buttons.push(btn);
14605         return btn;
14606     },
14607
14608     /**
14609      * Sets the default button to be focused when the dialog is displayed.
14610      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14611      * @return {Roo.BasicDialog} this
14612      */
14613     setDefaultButton : function(btn){
14614         this.defaultButton = btn;
14615         return this;
14616     },
14617
14618     // private
14619     getHeaderFooterHeight : function(safe){
14620         var height = 0;
14621         if(this.header){
14622            height += this.header.getHeight();
14623         }
14624         if(this.footer){
14625            var fm = this.footer.getMargins();
14626             height += (this.footer.getHeight()+fm.top+fm.bottom);
14627         }
14628         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14629         height += this.centerBg.getPadding("tb");
14630         return height;
14631     },
14632
14633     // private
14634     syncBodyHeight : function(){
14635         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14636         var height = this.size.height - this.getHeaderFooterHeight(false);
14637         bd.setHeight(height-bd.getMargins("tb"));
14638         var hh = this.header.getHeight();
14639         var h = this.size.height-hh;
14640         cb.setHeight(h);
14641         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14642         bw.setHeight(h-cb.getPadding("tb"));
14643         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14644         bd.setWidth(bw.getWidth(true));
14645         if(this.tabs){
14646             this.tabs.syncHeight();
14647             if(Roo.isIE){
14648                 this.tabs.el.repaint();
14649             }
14650         }
14651     },
14652
14653     /**
14654      * Restores the previous state of the dialog if Roo.state is configured.
14655      * @return {Roo.BasicDialog} this
14656      */
14657     restoreState : function(){
14658         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14659         if(box && box.width){
14660             this.xy = [box.x, box.y];
14661             this.resizeTo(box.width, box.height);
14662         }
14663         return this;
14664     },
14665
14666     // private
14667     beforeShow : function(){
14668         this.expand();
14669         if(this.fixedcenter){
14670             this.xy = this.el.getCenterXY(true);
14671         }
14672         if(this.modal){
14673             Roo.get(document.body).addClass("x-body-masked");
14674             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14675             this.mask.show();
14676         }
14677         this.constrainXY();
14678     },
14679
14680     // private
14681     animShow : function(){
14682         var b = Roo.get(this.animateTarget).getBox();
14683         this.proxy.setSize(b.width, b.height);
14684         this.proxy.setLocation(b.x, b.y);
14685         this.proxy.show();
14686         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14687                     true, .35, this.showEl.createDelegate(this));
14688     },
14689
14690     /**
14691      * Shows the dialog.
14692      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14693      * @return {Roo.BasicDialog} this
14694      */
14695     show : function(animateTarget){
14696         if (this.fireEvent("beforeshow", this) === false){
14697             return;
14698         }
14699         if(this.syncHeightBeforeShow){
14700             this.syncBodyHeight();
14701         }else if(this.firstShow){
14702             this.firstShow = false;
14703             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14704         }
14705         this.animateTarget = animateTarget || this.animateTarget;
14706         if(!this.el.isVisible()){
14707             this.beforeShow();
14708             if(this.animateTarget && Roo.get(this.animateTarget)){
14709                 this.animShow();
14710             }else{
14711                 this.showEl();
14712             }
14713         }
14714         return this;
14715     },
14716
14717     // private
14718     showEl : function(){
14719         this.proxy.hide();
14720         this.el.setXY(this.xy);
14721         this.el.show();
14722         this.adjustAssets(true);
14723         this.toFront();
14724         this.focus();
14725         // IE peekaboo bug - fix found by Dave Fenwick
14726         if(Roo.isIE){
14727             this.el.repaint();
14728         }
14729         this.fireEvent("show", this);
14730     },
14731
14732     /**
14733      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14734      * dialog itself will receive focus.
14735      */
14736     focus : function(){
14737         if(this.defaultButton){
14738             this.defaultButton.focus();
14739         }else{
14740             this.focusEl.focus();
14741         }
14742     },
14743
14744     // private
14745     constrainXY : function(){
14746         if(this.constraintoviewport !== false){
14747             if(!this.viewSize){
14748                 if(this.container){
14749                     var s = this.container.getSize();
14750                     this.viewSize = [s.width, s.height];
14751                 }else{
14752                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14753                 }
14754             }
14755             var s = Roo.get(this.container||document).getScroll();
14756
14757             var x = this.xy[0], y = this.xy[1];
14758             var w = this.size.width, h = this.size.height;
14759             var vw = this.viewSize[0], vh = this.viewSize[1];
14760             // only move it if it needs it
14761             var moved = false;
14762             // first validate right/bottom
14763             if(x + w > vw+s.left){
14764                 x = vw - w;
14765                 moved = true;
14766             }
14767             if(y + h > vh+s.top){
14768                 y = vh - h;
14769                 moved = true;
14770             }
14771             // then make sure top/left isn't negative
14772             if(x < s.left){
14773                 x = s.left;
14774                 moved = true;
14775             }
14776             if(y < s.top){
14777                 y = s.top;
14778                 moved = true;
14779             }
14780             if(moved){
14781                 // cache xy
14782                 this.xy = [x, y];
14783                 if(this.isVisible()){
14784                     this.el.setLocation(x, y);
14785                     this.adjustAssets();
14786                 }
14787             }
14788         }
14789     },
14790
14791     // private
14792     onDrag : function(){
14793         if(!this.proxyDrag){
14794             this.xy = this.el.getXY();
14795             this.adjustAssets();
14796         }
14797     },
14798
14799     // private
14800     adjustAssets : function(doShow){
14801         var x = this.xy[0], y = this.xy[1];
14802         var w = this.size.width, h = this.size.height;
14803         if(doShow === true){
14804             if(this.shadow){
14805                 this.shadow.show(this.el);
14806             }
14807             if(this.shim){
14808                 this.shim.show();
14809             }
14810         }
14811         if(this.shadow && this.shadow.isVisible()){
14812             this.shadow.show(this.el);
14813         }
14814         if(this.shim && this.shim.isVisible()){
14815             this.shim.setBounds(x, y, w, h);
14816         }
14817     },
14818
14819     // private
14820     adjustViewport : function(w, h){
14821         if(!w || !h){
14822             w = Roo.lib.Dom.getViewWidth();
14823             h = Roo.lib.Dom.getViewHeight();
14824         }
14825         // cache the size
14826         this.viewSize = [w, h];
14827         if(this.modal && this.mask.isVisible()){
14828             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14829             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14830         }
14831         if(this.isVisible()){
14832             this.constrainXY();
14833         }
14834     },
14835
14836     /**
14837      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14838      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14839      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14840      */
14841     destroy : function(removeEl){
14842         if(this.isVisible()){
14843             this.animateTarget = null;
14844             this.hide();
14845         }
14846         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14847         if(this.tabs){
14848             this.tabs.destroy(removeEl);
14849         }
14850         Roo.destroy(
14851              this.shim,
14852              this.proxy,
14853              this.resizer,
14854              this.close,
14855              this.mask
14856         );
14857         if(this.dd){
14858             this.dd.unreg();
14859         }
14860         if(this.buttons){
14861            for(var i = 0, len = this.buttons.length; i < len; i++){
14862                this.buttons[i].destroy();
14863            }
14864         }
14865         this.el.removeAllListeners();
14866         if(removeEl === true){
14867             this.el.update("");
14868             this.el.remove();
14869         }
14870         Roo.DialogManager.unregister(this);
14871     },
14872
14873     // private
14874     startMove : function(){
14875         if(this.proxyDrag){
14876             this.proxy.show();
14877         }
14878         if(this.constraintoviewport !== false){
14879             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14880         }
14881     },
14882
14883     // private
14884     endMove : function(){
14885         if(!this.proxyDrag){
14886             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14887         }else{
14888             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14889             this.proxy.hide();
14890         }
14891         this.refreshSize();
14892         this.adjustAssets();
14893         this.focus();
14894         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14895     },
14896
14897     /**
14898      * Brings this dialog to the front of any other visible dialogs
14899      * @return {Roo.BasicDialog} this
14900      */
14901     toFront : function(){
14902         Roo.DialogManager.bringToFront(this);
14903         return this;
14904     },
14905
14906     /**
14907      * Sends this dialog to the back (under) of any other visible dialogs
14908      * @return {Roo.BasicDialog} this
14909      */
14910     toBack : function(){
14911         Roo.DialogManager.sendToBack(this);
14912         return this;
14913     },
14914
14915     /**
14916      * Centers this dialog in the viewport
14917      * @return {Roo.BasicDialog} this
14918      */
14919     center : function(){
14920         var xy = this.el.getCenterXY(true);
14921         this.moveTo(xy[0], xy[1]);
14922         return this;
14923     },
14924
14925     /**
14926      * Moves the dialog's top-left corner to the specified point
14927      * @param {Number} x
14928      * @param {Number} y
14929      * @return {Roo.BasicDialog} this
14930      */
14931     moveTo : function(x, y){
14932         this.xy = [x,y];
14933         if(this.isVisible()){
14934             this.el.setXY(this.xy);
14935             this.adjustAssets();
14936         }
14937         return this;
14938     },
14939
14940     /**
14941      * Aligns the dialog to the specified element
14942      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14943      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14944      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14945      * @return {Roo.BasicDialog} this
14946      */
14947     alignTo : function(element, position, offsets){
14948         this.xy = this.el.getAlignToXY(element, position, offsets);
14949         if(this.isVisible()){
14950             this.el.setXY(this.xy);
14951             this.adjustAssets();
14952         }
14953         return this;
14954     },
14955
14956     /**
14957      * Anchors an element to another element and realigns it when the window is resized.
14958      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14959      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14960      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14961      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14962      * is a number, it is used as the buffer delay (defaults to 50ms).
14963      * @return {Roo.BasicDialog} this
14964      */
14965     anchorTo : function(el, alignment, offsets, monitorScroll){
14966         var action = function(){
14967             this.alignTo(el, alignment, offsets);
14968         };
14969         Roo.EventManager.onWindowResize(action, this);
14970         var tm = typeof monitorScroll;
14971         if(tm != 'undefined'){
14972             Roo.EventManager.on(window, 'scroll', action, this,
14973                 {buffer: tm == 'number' ? monitorScroll : 50});
14974         }
14975         action.call(this);
14976         return this;
14977     },
14978
14979     /**
14980      * Returns true if the dialog is visible
14981      * @return {Boolean}
14982      */
14983     isVisible : function(){
14984         return this.el.isVisible();
14985     },
14986
14987     // private
14988     animHide : function(callback){
14989         var b = Roo.get(this.animateTarget).getBox();
14990         this.proxy.show();
14991         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
14992         this.el.hide();
14993         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
14994                     this.hideEl.createDelegate(this, [callback]));
14995     },
14996
14997     /**
14998      * Hides the dialog.
14999      * @param {Function} callback (optional) Function to call when the dialog is hidden
15000      * @return {Roo.BasicDialog} this
15001      */
15002     hide : function(callback){
15003         if (this.fireEvent("beforehide", this) === false){
15004             return;
15005         }
15006         if(this.shadow){
15007             this.shadow.hide();
15008         }
15009         if(this.shim) {
15010           this.shim.hide();
15011         }
15012         // sometimes animateTarget seems to get set.. causing problems...
15013         // this just double checks..
15014         if(this.animateTarget && Roo.get(this.animateTarget)) {
15015            this.animHide(callback);
15016         }else{
15017             this.el.hide();
15018             this.hideEl(callback);
15019         }
15020         return this;
15021     },
15022
15023     // private
15024     hideEl : function(callback){
15025         this.proxy.hide();
15026         if(this.modal){
15027             this.mask.hide();
15028             Roo.get(document.body).removeClass("x-body-masked");
15029         }
15030         this.fireEvent("hide", this);
15031         if(typeof callback == "function"){
15032             callback();
15033         }
15034     },
15035
15036     // private
15037     hideAction : function(){
15038         this.setLeft("-10000px");
15039         this.setTop("-10000px");
15040         this.setStyle("visibility", "hidden");
15041     },
15042
15043     // private
15044     refreshSize : function(){
15045         this.size = this.el.getSize();
15046         this.xy = this.el.getXY();
15047         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15048     },
15049
15050     // private
15051     // z-index is managed by the DialogManager and may be overwritten at any time
15052     setZIndex : function(index){
15053         if(this.modal){
15054             this.mask.setStyle("z-index", index);
15055         }
15056         if(this.shim){
15057             this.shim.setStyle("z-index", ++index);
15058         }
15059         if(this.shadow){
15060             this.shadow.setZIndex(++index);
15061         }
15062         this.el.setStyle("z-index", ++index);
15063         if(this.proxy){
15064             this.proxy.setStyle("z-index", ++index);
15065         }
15066         if(this.resizer){
15067             this.resizer.proxy.setStyle("z-index", ++index);
15068         }
15069
15070         this.lastZIndex = index;
15071     },
15072
15073     /**
15074      * Returns the element for this dialog
15075      * @return {Roo.Element} The underlying dialog Element
15076      */
15077     getEl : function(){
15078         return this.el;
15079     }
15080 });
15081
15082 /**
15083  * @class Roo.DialogManager
15084  * Provides global access to BasicDialogs that have been created and
15085  * support for z-indexing (layering) multiple open dialogs.
15086  */
15087 Roo.DialogManager = function(){
15088     var list = {};
15089     var accessList = [];
15090     var front = null;
15091
15092     // private
15093     var sortDialogs = function(d1, d2){
15094         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15095     };
15096
15097     // private
15098     var orderDialogs = function(){
15099         accessList.sort(sortDialogs);
15100         var seed = Roo.DialogManager.zseed;
15101         for(var i = 0, len = accessList.length; i < len; i++){
15102             var dlg = accessList[i];
15103             if(dlg){
15104                 dlg.setZIndex(seed + (i*10));
15105             }
15106         }
15107     };
15108
15109     return {
15110         /**
15111          * The starting z-index for BasicDialogs (defaults to 9000)
15112          * @type Number The z-index value
15113          */
15114         zseed : 9000,
15115
15116         // private
15117         register : function(dlg){
15118             list[dlg.id] = dlg;
15119             accessList.push(dlg);
15120         },
15121
15122         // private
15123         unregister : function(dlg){
15124             delete list[dlg.id];
15125             var i=0;
15126             var len=0;
15127             if(!accessList.indexOf){
15128                 for(  i = 0, len = accessList.length; i < len; i++){
15129                     if(accessList[i] == dlg){
15130                         accessList.splice(i, 1);
15131                         return;
15132                     }
15133                 }
15134             }else{
15135                  i = accessList.indexOf(dlg);
15136                 if(i != -1){
15137                     accessList.splice(i, 1);
15138                 }
15139             }
15140         },
15141
15142         /**
15143          * Gets a registered dialog by id
15144          * @param {String/Object} id The id of the dialog or a dialog
15145          * @return {Roo.BasicDialog} this
15146          */
15147         get : function(id){
15148             return typeof id == "object" ? id : list[id];
15149         },
15150
15151         /**
15152          * Brings the specified dialog to the front
15153          * @param {String/Object} dlg The id of the dialog or a dialog
15154          * @return {Roo.BasicDialog} this
15155          */
15156         bringToFront : function(dlg){
15157             dlg = this.get(dlg);
15158             if(dlg != front){
15159                 front = dlg;
15160                 dlg._lastAccess = new Date().getTime();
15161                 orderDialogs();
15162             }
15163             return dlg;
15164         },
15165
15166         /**
15167          * Sends the specified dialog to the back
15168          * @param {String/Object} dlg The id of the dialog or a dialog
15169          * @return {Roo.BasicDialog} this
15170          */
15171         sendToBack : function(dlg){
15172             dlg = this.get(dlg);
15173             dlg._lastAccess = -(new Date().getTime());
15174             orderDialogs();
15175             return dlg;
15176         },
15177
15178         /**
15179          * Hides all dialogs
15180          */
15181         hideAll : function(){
15182             for(var id in list){
15183                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15184                     list[id].hide();
15185                 }
15186             }
15187         }
15188     };
15189 }();
15190
15191 /**
15192  * @class Roo.LayoutDialog
15193  * @extends Roo.BasicDialog
15194  * Dialog which provides adjustments for working with a layout in a Dialog.
15195  * Add your necessary layout config options to the dialog's config.<br>
15196  * Example usage (including a nested layout):
15197  * <pre><code>
15198 if(!dialog){
15199     dialog = new Roo.LayoutDialog("download-dlg", {
15200         modal: true,
15201         width:600,
15202         height:450,
15203         shadow:true,
15204         minWidth:500,
15205         minHeight:350,
15206         autoTabs:true,
15207         proxyDrag:true,
15208         // layout config merges with the dialog config
15209         center:{
15210             tabPosition: "top",
15211             alwaysShowTabs: true
15212         }
15213     });
15214     dialog.addKeyListener(27, dialog.hide, dialog);
15215     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15216     dialog.addButton("Build It!", this.getDownload, this);
15217
15218     // we can even add nested layouts
15219     var innerLayout = new Roo.BorderLayout("dl-inner", {
15220         east: {
15221             initialSize: 200,
15222             autoScroll:true,
15223             split:true
15224         },
15225         center: {
15226             autoScroll:true
15227         }
15228     });
15229     innerLayout.beginUpdate();
15230     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15231     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15232     innerLayout.endUpdate(true);
15233
15234     var layout = dialog.getLayout();
15235     layout.beginUpdate();
15236     layout.add("center", new Roo.ContentPanel("standard-panel",
15237                         {title: "Download the Source", fitToFrame:true}));
15238     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15239                {title: "Build your own roo.js"}));
15240     layout.getRegion("center").showPanel(sp);
15241     layout.endUpdate();
15242 }
15243 </code></pre>
15244     * @constructor
15245     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15246     * @param {Object} config configuration options
15247   */
15248 Roo.LayoutDialog = function(el, cfg){
15249     
15250     var config=  cfg;
15251     if (typeof(cfg) == 'undefined') {
15252         config = Roo.apply({}, el);
15253         // not sure why we use documentElement here.. - it should always be body.
15254         // IE7 borks horribly if we use documentElement.
15255         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15256         //config.autoCreate = true;
15257     }
15258     
15259     
15260     config.autoTabs = false;
15261     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15262     this.body.setStyle({overflow:"hidden", position:"relative"});
15263     this.layout = new Roo.BorderLayout(this.body.dom, config);
15264     this.layout.monitorWindowResize = false;
15265     this.el.addClass("x-dlg-auto-layout");
15266     // fix case when center region overwrites center function
15267     this.center = Roo.BasicDialog.prototype.center;
15268     this.on("show", this.layout.layout, this.layout, true);
15269     if (config.items) {
15270         var xitems = config.items;
15271         delete config.items;
15272         Roo.each(xitems, this.addxtype, this);
15273     }
15274     
15275     
15276 };
15277 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15278     /**
15279      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15280      * @deprecated
15281      */
15282     endUpdate : function(){
15283         this.layout.endUpdate();
15284     },
15285
15286     /**
15287      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15288      *  @deprecated
15289      */
15290     beginUpdate : function(){
15291         this.layout.beginUpdate();
15292     },
15293
15294     /**
15295      * Get the BorderLayout for this dialog
15296      * @return {Roo.BorderLayout}
15297      */
15298     getLayout : function(){
15299         return this.layout;
15300     },
15301
15302     showEl : function(){
15303         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15304         if(Roo.isIE7){
15305             this.layout.layout();
15306         }
15307     },
15308
15309     // private
15310     // Use the syncHeightBeforeShow config option to control this automatically
15311     syncBodyHeight : function(){
15312         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15313         if(this.layout){this.layout.layout();}
15314     },
15315     
15316       /**
15317      * Add an xtype element (actually adds to the layout.)
15318      * @return {Object} xdata xtype object data.
15319      */
15320     
15321     addxtype : function(c) {
15322         return this.layout.addxtype(c);
15323     }
15324 });/*
15325  * Based on:
15326  * Ext JS Library 1.1.1
15327  * Copyright(c) 2006-2007, Ext JS, LLC.
15328  *
15329  * Originally Released Under LGPL - original licence link has changed is not relivant.
15330  *
15331  * Fork - LGPL
15332  * <script type="text/javascript">
15333  */
15334  
15335 /**
15336  * @class Roo.MessageBox
15337  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15338  * Example usage:
15339  *<pre><code>
15340 // Basic alert:
15341 Roo.Msg.alert('Status', 'Changes saved successfully.');
15342
15343 // Prompt for user data:
15344 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15345     if (btn == 'ok'){
15346         // process text value...
15347     }
15348 });
15349
15350 // Show a dialog using config options:
15351 Roo.Msg.show({
15352    title:'Save Changes?',
15353    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15354    buttons: Roo.Msg.YESNOCANCEL,
15355    fn: processResult,
15356    animEl: 'elId'
15357 });
15358 </code></pre>
15359  * @singleton
15360  */
15361 Roo.MessageBox = function(){
15362     var dlg, opt, mask, waitTimer;
15363     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15364     var buttons, activeTextEl, bwidth;
15365
15366     // private
15367     var handleButton = function(button){
15368         dlg.hide();
15369         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15370     };
15371
15372     // private
15373     var handleHide = function(){
15374         if(opt && opt.cls){
15375             dlg.el.removeClass(opt.cls);
15376         }
15377         if(waitTimer){
15378             Roo.TaskMgr.stop(waitTimer);
15379             waitTimer = null;
15380         }
15381     };
15382
15383     // private
15384     var updateButtons = function(b){
15385         var width = 0;
15386         if(!b){
15387             buttons["ok"].hide();
15388             buttons["cancel"].hide();
15389             buttons["yes"].hide();
15390             buttons["no"].hide();
15391             dlg.footer.dom.style.display = 'none';
15392             return width;
15393         }
15394         dlg.footer.dom.style.display = '';
15395         for(var k in buttons){
15396             if(typeof buttons[k] != "function"){
15397                 if(b[k]){
15398                     buttons[k].show();
15399                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15400                     width += buttons[k].el.getWidth()+15;
15401                 }else{
15402                     buttons[k].hide();
15403                 }
15404             }
15405         }
15406         return width;
15407     };
15408
15409     // private
15410     var handleEsc = function(d, k, e){
15411         if(opt && opt.closable !== false){
15412             dlg.hide();
15413         }
15414         if(e){
15415             e.stopEvent();
15416         }
15417     };
15418
15419     return {
15420         /**
15421          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15422          * @return {Roo.BasicDialog} The BasicDialog element
15423          */
15424         getDialog : function(){
15425            if(!dlg){
15426                 dlg = new Roo.BasicDialog("x-msg-box", {
15427                     autoCreate : true,
15428                     shadow: true,
15429                     draggable: true,
15430                     resizable:false,
15431                     constraintoviewport:false,
15432                     fixedcenter:true,
15433                     collapsible : false,
15434                     shim:true,
15435                     modal: true,
15436                     width:400, height:100,
15437                     buttonAlign:"center",
15438                     closeClick : function(){
15439                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15440                             handleButton("no");
15441                         }else{
15442                             handleButton("cancel");
15443                         }
15444                     }
15445                 });
15446                 dlg.on("hide", handleHide);
15447                 mask = dlg.mask;
15448                 dlg.addKeyListener(27, handleEsc);
15449                 buttons = {};
15450                 var bt = this.buttonText;
15451                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15452                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15453                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15454                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15455                 bodyEl = dlg.body.createChild({
15456
15457                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
15458                 });
15459                 msgEl = bodyEl.dom.firstChild;
15460                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15461                 textboxEl.enableDisplayMode();
15462                 textboxEl.addKeyListener([10,13], function(){
15463                     if(dlg.isVisible() && opt && opt.buttons){
15464                         if(opt.buttons.ok){
15465                             handleButton("ok");
15466                         }else if(opt.buttons.yes){
15467                             handleButton("yes");
15468                         }
15469                     }
15470                 });
15471                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15472                 textareaEl.enableDisplayMode();
15473                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15474                 progressEl.enableDisplayMode();
15475                 var pf = progressEl.dom.firstChild;
15476                 if (pf) {
15477                     pp = Roo.get(pf.firstChild);
15478                     pp.setHeight(pf.offsetHeight);
15479                 }
15480                 
15481             }
15482             return dlg;
15483         },
15484
15485         /**
15486          * Updates the message box body text
15487          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15488          * the XHTML-compliant non-breaking space character '&amp;#160;')
15489          * @return {Roo.MessageBox} This message box
15490          */
15491         updateText : function(text){
15492             if(!dlg.isVisible() && !opt.width){
15493                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15494             }
15495             msgEl.innerHTML = text || '&#160;';
15496             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15497                         Math.max(opt.minWidth || this.minWidth, bwidth));
15498             if(opt.prompt){
15499                 activeTextEl.setWidth(w);
15500             }
15501             if(dlg.isVisible()){
15502                 dlg.fixedcenter = false;
15503             }
15504             dlg.setContentSize(w, bodyEl.getHeight());
15505             if(dlg.isVisible()){
15506                 dlg.fixedcenter = true;
15507             }
15508             return this;
15509         },
15510
15511         /**
15512          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15513          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15514          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15515          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15516          * @return {Roo.MessageBox} This message box
15517          */
15518         updateProgress : function(value, text){
15519             if(text){
15520                 this.updateText(text);
15521             }
15522             if (pp) { // weird bug on my firefox - for some reason this is not defined
15523                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15524             }
15525             return this;
15526         },        
15527
15528         /**
15529          * Returns true if the message box is currently displayed
15530          * @return {Boolean} True if the message box is visible, else false
15531          */
15532         isVisible : function(){
15533             return dlg && dlg.isVisible();  
15534         },
15535
15536         /**
15537          * Hides the message box if it is displayed
15538          */
15539         hide : function(){
15540             if(this.isVisible()){
15541                 dlg.hide();
15542             }  
15543         },
15544
15545         /**
15546          * Displays a new message box, or reinitializes an existing message box, based on the config options
15547          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15548          * The following config object properties are supported:
15549          * <pre>
15550 Property    Type             Description
15551 ----------  ---------------  ------------------------------------------------------------------------------------
15552 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15553                                    closes (defaults to undefined)
15554 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15555                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15556 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15557                                    progress and wait dialogs will ignore this property and always hide the
15558                                    close button as they can only be closed programmatically.
15559 cls               String           A custom CSS class to apply to the message box element
15560 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15561                                    displayed (defaults to 75)
15562 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15563                                    function will be btn (the name of the button that was clicked, if applicable,
15564                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15565                                    Progress and wait dialogs will ignore this option since they do not respond to
15566                                    user actions and can only be closed programmatically, so any required function
15567                                    should be called by the same code after it closes the dialog.
15568 icon              String           A CSS class that provides a background image to be used as an icon for
15569                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15570 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15571 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15572 modal             Boolean          False to allow user interaction with the page while the message box is
15573                                    displayed (defaults to true)
15574 msg               String           A string that will replace the existing message box body text (defaults
15575                                    to the XHTML-compliant non-breaking space character '&#160;')
15576 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15577 progress          Boolean          True to display a progress bar (defaults to false)
15578 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15579 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15580 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15581 title             String           The title text
15582 value             String           The string value to set into the active textbox element if displayed
15583 wait              Boolean          True to display a progress bar (defaults to false)
15584 width             Number           The width of the dialog in pixels
15585 </pre>
15586          *
15587          * Example usage:
15588          * <pre><code>
15589 Roo.Msg.show({
15590    title: 'Address',
15591    msg: 'Please enter your address:',
15592    width: 300,
15593    buttons: Roo.MessageBox.OKCANCEL,
15594    multiline: true,
15595    fn: saveAddress,
15596    animEl: 'addAddressBtn'
15597 });
15598 </code></pre>
15599          * @param {Object} config Configuration options
15600          * @return {Roo.MessageBox} This message box
15601          */
15602         show : function(options){
15603             if(this.isVisible()){
15604                 this.hide();
15605             }
15606             var d = this.getDialog();
15607             opt = options;
15608             d.setTitle(opt.title || "&#160;");
15609             d.close.setDisplayed(opt.closable !== false);
15610             activeTextEl = textboxEl;
15611             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15612             if(opt.prompt){
15613                 if(opt.multiline){
15614                     textboxEl.hide();
15615                     textareaEl.show();
15616                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15617                         opt.multiline : this.defaultTextHeight);
15618                     activeTextEl = textareaEl;
15619                 }else{
15620                     textboxEl.show();
15621                     textareaEl.hide();
15622                 }
15623             }else{
15624                 textboxEl.hide();
15625                 textareaEl.hide();
15626             }
15627             progressEl.setDisplayed(opt.progress === true);
15628             this.updateProgress(0);
15629             activeTextEl.dom.value = opt.value || "";
15630             if(opt.prompt){
15631                 dlg.setDefaultButton(activeTextEl);
15632             }else{
15633                 var bs = opt.buttons;
15634                 var db = null;
15635                 if(bs && bs.ok){
15636                     db = buttons["ok"];
15637                 }else if(bs && bs.yes){
15638                     db = buttons["yes"];
15639                 }
15640                 dlg.setDefaultButton(db);
15641             }
15642             bwidth = updateButtons(opt.buttons);
15643             this.updateText(opt.msg);
15644             if(opt.cls){
15645                 d.el.addClass(opt.cls);
15646             }
15647             d.proxyDrag = opt.proxyDrag === true;
15648             d.modal = opt.modal !== false;
15649             d.mask = opt.modal !== false ? mask : false;
15650             if(!d.isVisible()){
15651                 // force it to the end of the z-index stack so it gets a cursor in FF
15652                 document.body.appendChild(dlg.el.dom);
15653                 d.animateTarget = null;
15654                 d.show(options.animEl);
15655             }
15656             return this;
15657         },
15658
15659         /**
15660          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15661          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15662          * and closing the message box when the process is complete.
15663          * @param {String} title The title bar text
15664          * @param {String} msg The message box body text
15665          * @return {Roo.MessageBox} This message box
15666          */
15667         progress : function(title, msg){
15668             this.show({
15669                 title : title,
15670                 msg : msg,
15671                 buttons: false,
15672                 progress:true,
15673                 closable:false,
15674                 minWidth: this.minProgressWidth,
15675                 modal : true
15676             });
15677             return this;
15678         },
15679
15680         /**
15681          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15682          * If a callback function is passed it will be called after the user clicks the button, and the
15683          * id of the button that was clicked will be passed as the only parameter to the callback
15684          * (could also be the top-right close button).
15685          * @param {String} title The title bar text
15686          * @param {String} msg The message box body text
15687          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15688          * @param {Object} scope (optional) The scope of the callback function
15689          * @return {Roo.MessageBox} This message box
15690          */
15691         alert : function(title, msg, fn, scope){
15692             this.show({
15693                 title : title,
15694                 msg : msg,
15695                 buttons: this.OK,
15696                 fn: fn,
15697                 scope : scope,
15698                 modal : true
15699             });
15700             return this;
15701         },
15702
15703         /**
15704          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15705          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15706          * You are responsible for closing the message box when the process is complete.
15707          * @param {String} msg The message box body text
15708          * @param {String} title (optional) The title bar text
15709          * @return {Roo.MessageBox} This message box
15710          */
15711         wait : function(msg, title){
15712             this.show({
15713                 title : title,
15714                 msg : msg,
15715                 buttons: false,
15716                 closable:false,
15717                 progress:true,
15718                 modal:true,
15719                 width:300,
15720                 wait:true
15721             });
15722             waitTimer = Roo.TaskMgr.start({
15723                 run: function(i){
15724                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15725                 },
15726                 interval: 1000
15727             });
15728             return this;
15729         },
15730
15731         /**
15732          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15733          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15734          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15735          * @param {String} title The title bar text
15736          * @param {String} msg The message box body text
15737          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15738          * @param {Object} scope (optional) The scope of the callback function
15739          * @return {Roo.MessageBox} This message box
15740          */
15741         confirm : function(title, msg, fn, scope){
15742             this.show({
15743                 title : title,
15744                 msg : msg,
15745                 buttons: this.YESNO,
15746                 fn: fn,
15747                 scope : scope,
15748                 modal : true
15749             });
15750             return this;
15751         },
15752
15753         /**
15754          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15755          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15756          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15757          * (could also be the top-right close button) and the text that was entered will be passed as the two
15758          * parameters to the callback.
15759          * @param {String} title The title bar text
15760          * @param {String} msg The message box body text
15761          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15762          * @param {Object} scope (optional) The scope of the callback function
15763          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15764          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15765          * @return {Roo.MessageBox} This message box
15766          */
15767         prompt : function(title, msg, fn, scope, multiline){
15768             this.show({
15769                 title : title,
15770                 msg : msg,
15771                 buttons: this.OKCANCEL,
15772                 fn: fn,
15773                 minWidth:250,
15774                 scope : scope,
15775                 prompt:true,
15776                 multiline: multiline,
15777                 modal : true
15778             });
15779             return this;
15780         },
15781
15782         /**
15783          * Button config that displays a single OK button
15784          * @type Object
15785          */
15786         OK : {ok:true},
15787         /**
15788          * Button config that displays Yes and No buttons
15789          * @type Object
15790          */
15791         YESNO : {yes:true, no:true},
15792         /**
15793          * Button config that displays OK and Cancel buttons
15794          * @type Object
15795          */
15796         OKCANCEL : {ok:true, cancel:true},
15797         /**
15798          * Button config that displays Yes, No and Cancel buttons
15799          * @type Object
15800          */
15801         YESNOCANCEL : {yes:true, no:true, cancel:true},
15802
15803         /**
15804          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15805          * @type Number
15806          */
15807         defaultTextHeight : 75,
15808         /**
15809          * The maximum width in pixels of the message box (defaults to 600)
15810          * @type Number
15811          */
15812         maxWidth : 600,
15813         /**
15814          * The minimum width in pixels of the message box (defaults to 100)
15815          * @type Number
15816          */
15817         minWidth : 100,
15818         /**
15819          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15820          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15821          * @type Number
15822          */
15823         minProgressWidth : 250,
15824         /**
15825          * An object containing the default button text strings that can be overriden for localized language support.
15826          * Supported properties are: ok, cancel, yes and no.
15827          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15828          * @type Object
15829          */
15830         buttonText : {
15831             ok : "OK",
15832             cancel : "Cancel",
15833             yes : "Yes",
15834             no : "No"
15835         }
15836     };
15837 }();
15838
15839 /**
15840  * Shorthand for {@link Roo.MessageBox}
15841  */
15842 Roo.Msg = Roo.MessageBox;/*
15843  * Based on:
15844  * Ext JS Library 1.1.1
15845  * Copyright(c) 2006-2007, Ext JS, LLC.
15846  *
15847  * Originally Released Under LGPL - original licence link has changed is not relivant.
15848  *
15849  * Fork - LGPL
15850  * <script type="text/javascript">
15851  */
15852 /**
15853  * @class Roo.QuickTips
15854  * Provides attractive and customizable tooltips for any element.
15855  * @singleton
15856  */
15857 Roo.QuickTips = function(){
15858     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15859     var ce, bd, xy, dd;
15860     var visible = false, disabled = true, inited = false;
15861     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15862     
15863     var onOver = function(e){
15864         if(disabled){
15865             return;
15866         }
15867         var t = e.getTarget();
15868         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15869             return;
15870         }
15871         if(ce && t == ce.el){
15872             clearTimeout(hideProc);
15873             return;
15874         }
15875         if(t && tagEls[t.id]){
15876             tagEls[t.id].el = t;
15877             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15878             return;
15879         }
15880         var ttp, et = Roo.fly(t);
15881         var ns = cfg.namespace;
15882         if(tm.interceptTitles && t.title){
15883             ttp = t.title;
15884             t.qtip = ttp;
15885             t.removeAttribute("title");
15886             e.preventDefault();
15887         }else{
15888             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15889         }
15890         if(ttp){
15891             showProc = show.defer(tm.showDelay, tm, [{
15892                 el: t, 
15893                 text: ttp, 
15894                 width: et.getAttributeNS(ns, cfg.width),
15895                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15896                 title: et.getAttributeNS(ns, cfg.title),
15897                     cls: et.getAttributeNS(ns, cfg.cls)
15898             }]);
15899         }
15900     };
15901     
15902     var onOut = function(e){
15903         clearTimeout(showProc);
15904         var t = e.getTarget();
15905         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15906             hideProc = setTimeout(hide, tm.hideDelay);
15907         }
15908     };
15909     
15910     var onMove = function(e){
15911         if(disabled){
15912             return;
15913         }
15914         xy = e.getXY();
15915         xy[1] += 18;
15916         if(tm.trackMouse && ce){
15917             el.setXY(xy);
15918         }
15919     };
15920     
15921     var onDown = function(e){
15922         clearTimeout(showProc);
15923         clearTimeout(hideProc);
15924         if(!e.within(el)){
15925             if(tm.hideOnClick){
15926                 hide();
15927                 tm.disable();
15928                 tm.enable.defer(100, tm);
15929             }
15930         }
15931     };
15932     
15933     var getPad = function(){
15934         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15935     };
15936
15937     var show = function(o){
15938         if(disabled){
15939             return;
15940         }
15941         clearTimeout(dismissProc);
15942         ce = o;
15943         if(removeCls){ // in case manually hidden
15944             el.removeClass(removeCls);
15945             removeCls = null;
15946         }
15947         if(ce.cls){
15948             el.addClass(ce.cls);
15949             removeCls = ce.cls;
15950         }
15951         if(ce.title){
15952             tipTitle.update(ce.title);
15953             tipTitle.show();
15954         }else{
15955             tipTitle.update('');
15956             tipTitle.hide();
15957         }
15958         el.dom.style.width  = tm.maxWidth+'px';
15959         //tipBody.dom.style.width = '';
15960         tipBodyText.update(o.text);
15961         var p = getPad(), w = ce.width;
15962         if(!w){
15963             var td = tipBodyText.dom;
15964             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15965             if(aw > tm.maxWidth){
15966                 w = tm.maxWidth;
15967             }else if(aw < tm.minWidth){
15968                 w = tm.minWidth;
15969             }else{
15970                 w = aw;
15971             }
15972         }
15973         //tipBody.setWidth(w);
15974         el.setWidth(parseInt(w, 10) + p);
15975         if(ce.autoHide === false){
15976             close.setDisplayed(true);
15977             if(dd){
15978                 dd.unlock();
15979             }
15980         }else{
15981             close.setDisplayed(false);
15982             if(dd){
15983                 dd.lock();
15984             }
15985         }
15986         if(xy){
15987             el.avoidY = xy[1]-18;
15988             el.setXY(xy);
15989         }
15990         if(tm.animate){
15991             el.setOpacity(.1);
15992             el.setStyle("visibility", "visible");
15993             el.fadeIn({callback: afterShow});
15994         }else{
15995             afterShow();
15996         }
15997     };
15998     
15999     var afterShow = function(){
16000         if(ce){
16001             el.show();
16002             esc.enable();
16003             if(tm.autoDismiss && ce.autoHide !== false){
16004                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16005             }
16006         }
16007     };
16008     
16009     var hide = function(noanim){
16010         clearTimeout(dismissProc);
16011         clearTimeout(hideProc);
16012         ce = null;
16013         if(el.isVisible()){
16014             esc.disable();
16015             if(noanim !== true && tm.animate){
16016                 el.fadeOut({callback: afterHide});
16017             }else{
16018                 afterHide();
16019             } 
16020         }
16021     };
16022     
16023     var afterHide = function(){
16024         el.hide();
16025         if(removeCls){
16026             el.removeClass(removeCls);
16027             removeCls = null;
16028         }
16029     };
16030     
16031     return {
16032         /**
16033         * @cfg {Number} minWidth
16034         * The minimum width of the quick tip (defaults to 40)
16035         */
16036        minWidth : 40,
16037         /**
16038         * @cfg {Number} maxWidth
16039         * The maximum width of the quick tip (defaults to 300)
16040         */
16041        maxWidth : 300,
16042         /**
16043         * @cfg {Boolean} interceptTitles
16044         * True to automatically use the element's DOM title value if available (defaults to false)
16045         */
16046        interceptTitles : false,
16047         /**
16048         * @cfg {Boolean} trackMouse
16049         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16050         */
16051        trackMouse : false,
16052         /**
16053         * @cfg {Boolean} hideOnClick
16054         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16055         */
16056        hideOnClick : true,
16057         /**
16058         * @cfg {Number} showDelay
16059         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16060         */
16061        showDelay : 500,
16062         /**
16063         * @cfg {Number} hideDelay
16064         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16065         */
16066        hideDelay : 200,
16067         /**
16068         * @cfg {Boolean} autoHide
16069         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16070         * Used in conjunction with hideDelay.
16071         */
16072        autoHide : true,
16073         /**
16074         * @cfg {Boolean}
16075         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16076         * (defaults to true).  Used in conjunction with autoDismissDelay.
16077         */
16078        autoDismiss : true,
16079         /**
16080         * @cfg {Number}
16081         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16082         */
16083        autoDismissDelay : 5000,
16084        /**
16085         * @cfg {Boolean} animate
16086         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16087         */
16088        animate : false,
16089
16090        /**
16091         * @cfg {String} title
16092         * Title text to display (defaults to '').  This can be any valid HTML markup.
16093         */
16094         title: '',
16095        /**
16096         * @cfg {String} text
16097         * Body text to display (defaults to '').  This can be any valid HTML markup.
16098         */
16099         text : '',
16100        /**
16101         * @cfg {String} cls
16102         * A CSS class to apply to the base quick tip element (defaults to '').
16103         */
16104         cls : '',
16105        /**
16106         * @cfg {Number} width
16107         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16108         * minWidth or maxWidth.
16109         */
16110         width : null,
16111
16112     /**
16113      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16114      * or display QuickTips in a page.
16115      */
16116        init : function(){
16117           tm = Roo.QuickTips;
16118           cfg = tm.tagConfig;
16119           if(!inited){
16120               if(!Roo.isReady){ // allow calling of init() before onReady
16121                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16122                   return;
16123               }
16124               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16125               el.fxDefaults = {stopFx: true};
16126               // maximum custom styling
16127               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
16128               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
16129               tipTitle = el.child('h3');
16130               tipTitle.enableDisplayMode("block");
16131               tipBody = el.child('div.x-tip-bd');
16132               tipBodyText = el.child('div.x-tip-bd-inner');
16133               //bdLeft = el.child('div.x-tip-bd-left');
16134               //bdRight = el.child('div.x-tip-bd-right');
16135               close = el.child('div.x-tip-close');
16136               close.enableDisplayMode("block");
16137               close.on("click", hide);
16138               var d = Roo.get(document);
16139               d.on("mousedown", onDown);
16140               d.on("mouseover", onOver);
16141               d.on("mouseout", onOut);
16142               d.on("mousemove", onMove);
16143               esc = d.addKeyListener(27, hide);
16144               esc.disable();
16145               if(Roo.dd.DD){
16146                   dd = el.initDD("default", null, {
16147                       onDrag : function(){
16148                           el.sync();  
16149                       }
16150                   });
16151                   dd.setHandleElId(tipTitle.id);
16152                   dd.lock();
16153               }
16154               inited = true;
16155           }
16156           this.enable(); 
16157        },
16158
16159     /**
16160      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16161      * are supported:
16162      * <pre>
16163 Property    Type                   Description
16164 ----------  ---------------------  ------------------------------------------------------------------------
16165 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16166      * </ul>
16167      * @param {Object} config The config object
16168      */
16169        register : function(config){
16170            var cs = config instanceof Array ? config : arguments;
16171            for(var i = 0, len = cs.length; i < len; i++) {
16172                var c = cs[i];
16173                var target = c.target;
16174                if(target){
16175                    if(target instanceof Array){
16176                        for(var j = 0, jlen = target.length; j < jlen; j++){
16177                            tagEls[target[j]] = c;
16178                        }
16179                    }else{
16180                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16181                    }
16182                }
16183            }
16184        },
16185
16186     /**
16187      * Removes this quick tip from its element and destroys it.
16188      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16189      */
16190        unregister : function(el){
16191            delete tagEls[Roo.id(el)];
16192        },
16193
16194     /**
16195      * Enable this quick tip.
16196      */
16197        enable : function(){
16198            if(inited && disabled){
16199                locks.pop();
16200                if(locks.length < 1){
16201                    disabled = false;
16202                }
16203            }
16204        },
16205
16206     /**
16207      * Disable this quick tip.
16208      */
16209        disable : function(){
16210           disabled = true;
16211           clearTimeout(showProc);
16212           clearTimeout(hideProc);
16213           clearTimeout(dismissProc);
16214           if(ce){
16215               hide(true);
16216           }
16217           locks.push(1);
16218        },
16219
16220     /**
16221      * Returns true if the quick tip is enabled, else false.
16222      */
16223        isEnabled : function(){
16224             return !disabled;
16225        },
16226
16227         // private
16228        tagConfig : {
16229            namespace : "ext",
16230            attribute : "qtip",
16231            width : "width",
16232            target : "target",
16233            title : "qtitle",
16234            hide : "hide",
16235            cls : "qclass"
16236        }
16237    };
16238 }();
16239
16240 // backwards compat
16241 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16242  * Based on:
16243  * Ext JS Library 1.1.1
16244  * Copyright(c) 2006-2007, Ext JS, LLC.
16245  *
16246  * Originally Released Under LGPL - original licence link has changed is not relivant.
16247  *
16248  * Fork - LGPL
16249  * <script type="text/javascript">
16250  */
16251  
16252
16253 /**
16254  * @class Roo.tree.TreePanel
16255  * @extends Roo.data.Tree
16256
16257  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16258  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16259  * @cfg {Boolean} enableDD true to enable drag and drop
16260  * @cfg {Boolean} enableDrag true to enable just drag
16261  * @cfg {Boolean} enableDrop true to enable just drop
16262  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16263  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16264  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16265  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16266  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16267  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16268  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16269  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16270  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16271  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16272  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16273  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16274  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16275  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16276  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
16277  * 
16278  * @constructor
16279  * @param {String/HTMLElement/Element} el The container element
16280  * @param {Object} config
16281  */
16282 Roo.tree.TreePanel = function(el, config){
16283     var root = false;
16284     var loader = false;
16285     if (config.root) {
16286         root = config.root;
16287         delete config.root;
16288     }
16289     if (config.loader) {
16290         loader = config.loader;
16291         delete config.loader;
16292     }
16293     
16294     Roo.apply(this, config);
16295     Roo.tree.TreePanel.superclass.constructor.call(this);
16296     this.el = Roo.get(el);
16297     this.el.addClass('x-tree');
16298     //console.log(root);
16299     if (root) {
16300         this.setRootNode( Roo.factory(root, Roo.tree));
16301     }
16302     if (loader) {
16303         this.loader = Roo.factory(loader, Roo.tree);
16304     }
16305    /**
16306     * Read-only. The id of the container element becomes this TreePanel's id.
16307     */
16308    this.id = this.el.id;
16309    this.addEvents({
16310         /**
16311         * @event beforeload
16312         * Fires before a node is loaded, return false to cancel
16313         * @param {Node} node The node being loaded
16314         */
16315         "beforeload" : true,
16316         /**
16317         * @event load
16318         * Fires when a node is loaded
16319         * @param {Node} node The node that was loaded
16320         */
16321         "load" : true,
16322         /**
16323         * @event textchange
16324         * Fires when the text for a node is changed
16325         * @param {Node} node The node
16326         * @param {String} text The new text
16327         * @param {String} oldText The old text
16328         */
16329         "textchange" : true,
16330         /**
16331         * @event beforeexpand
16332         * Fires before a node is expanded, return false to cancel.
16333         * @param {Node} node The node
16334         * @param {Boolean} deep
16335         * @param {Boolean} anim
16336         */
16337         "beforeexpand" : true,
16338         /**
16339         * @event beforecollapse
16340         * Fires before a node is collapsed, return false to cancel.
16341         * @param {Node} node The node
16342         * @param {Boolean} deep
16343         * @param {Boolean} anim
16344         */
16345         "beforecollapse" : true,
16346         /**
16347         * @event expand
16348         * Fires when a node is expanded
16349         * @param {Node} node The node
16350         */
16351         "expand" : true,
16352         /**
16353         * @event disabledchange
16354         * Fires when the disabled status of a node changes
16355         * @param {Node} node The node
16356         * @param {Boolean} disabled
16357         */
16358         "disabledchange" : true,
16359         /**
16360         * @event collapse
16361         * Fires when a node is collapsed
16362         * @param {Node} node The node
16363         */
16364         "collapse" : true,
16365         /**
16366         * @event beforeclick
16367         * Fires before click processing on a node. Return false to cancel the default action.
16368         * @param {Node} node The node
16369         * @param {Roo.EventObject} e The event object
16370         */
16371         "beforeclick":true,
16372         /**
16373         * @event checkchange
16374         * Fires when a node with a checkbox's checked property changes
16375         * @param {Node} this This node
16376         * @param {Boolean} checked
16377         */
16378         "checkchange":true,
16379         /**
16380         * @event click
16381         * Fires when a node is clicked
16382         * @param {Node} node The node
16383         * @param {Roo.EventObject} e The event object
16384         */
16385         "click":true,
16386         /**
16387         * @event dblclick
16388         * Fires when a node is double clicked
16389         * @param {Node} node The node
16390         * @param {Roo.EventObject} e The event object
16391         */
16392         "dblclick":true,
16393         /**
16394         * @event contextmenu
16395         * Fires when a node is right clicked
16396         * @param {Node} node The node
16397         * @param {Roo.EventObject} e The event object
16398         */
16399         "contextmenu":true,
16400         /**
16401         * @event beforechildrenrendered
16402         * Fires right before the child nodes for a node are rendered
16403         * @param {Node} node The node
16404         */
16405         "beforechildrenrendered":true,
16406        /**
16407              * @event startdrag
16408              * Fires when a node starts being dragged
16409              * @param {Roo.tree.TreePanel} this
16410              * @param {Roo.tree.TreeNode} node
16411              * @param {event} e The raw browser event
16412              */ 
16413             "startdrag" : true,
16414             /**
16415              * @event enddrag
16416              * Fires when a drag operation is complete
16417              * @param {Roo.tree.TreePanel} this
16418              * @param {Roo.tree.TreeNode} node
16419              * @param {event} e The raw browser event
16420              */
16421             "enddrag" : true,
16422             /**
16423              * @event dragdrop
16424              * Fires when a dragged node is dropped on a valid DD target
16425              * @param {Roo.tree.TreePanel} this
16426              * @param {Roo.tree.TreeNode} node
16427              * @param {DD} dd The dd it was dropped on
16428              * @param {event} e The raw browser event
16429              */
16430             "dragdrop" : true,
16431             /**
16432              * @event beforenodedrop
16433              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16434              * passed to handlers has the following properties:<br />
16435              * <ul style="padding:5px;padding-left:16px;">
16436              * <li>tree - The TreePanel</li>
16437              * <li>target - The node being targeted for the drop</li>
16438              * <li>data - The drag data from the drag source</li>
16439              * <li>point - The point of the drop - append, above or below</li>
16440              * <li>source - The drag source</li>
16441              * <li>rawEvent - Raw mouse event</li>
16442              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16443              * to be inserted by setting them on this object.</li>
16444              * <li>cancel - Set this to true to cancel the drop.</li>
16445              * </ul>
16446              * @param {Object} dropEvent
16447              */
16448             "beforenodedrop" : true,
16449             /**
16450              * @event nodedrop
16451              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16452              * passed to handlers has the following properties:<br />
16453              * <ul style="padding:5px;padding-left:16px;">
16454              * <li>tree - The TreePanel</li>
16455              * <li>target - The node being targeted for the drop</li>
16456              * <li>data - The drag data from the drag source</li>
16457              * <li>point - The point of the drop - append, above or below</li>
16458              * <li>source - The drag source</li>
16459              * <li>rawEvent - Raw mouse event</li>
16460              * <li>dropNode - Dropped node(s).</li>
16461              * </ul>
16462              * @param {Object} dropEvent
16463              */
16464             "nodedrop" : true,
16465              /**
16466              * @event nodedragover
16467              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16468              * passed to handlers has the following properties:<br />
16469              * <ul style="padding:5px;padding-left:16px;">
16470              * <li>tree - The TreePanel</li>
16471              * <li>target - The node being targeted for the drop</li>
16472              * <li>data - The drag data from the drag source</li>
16473              * <li>point - The point of the drop - append, above or below</li>
16474              * <li>source - The drag source</li>
16475              * <li>rawEvent - Raw mouse event</li>
16476              * <li>dropNode - Drop node(s) provided by the source.</li>
16477              * <li>cancel - Set this to true to signal drop not allowed.</li>
16478              * </ul>
16479              * @param {Object} dragOverEvent
16480              */
16481             "nodedragover" : true
16482         
16483    });
16484    if(this.singleExpand){
16485        this.on("beforeexpand", this.restrictExpand, this);
16486    }
16487 };
16488 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16489     rootVisible : true,
16490     animate: Roo.enableFx,
16491     lines : true,
16492     enableDD : false,
16493     hlDrop : Roo.enableFx,
16494   
16495     renderer: false,
16496     
16497     rendererTip: false,
16498     // private
16499     restrictExpand : function(node){
16500         var p = node.parentNode;
16501         if(p){
16502             if(p.expandedChild && p.expandedChild.parentNode == p){
16503                 p.expandedChild.collapse();
16504             }
16505             p.expandedChild = node;
16506         }
16507     },
16508
16509     // private override
16510     setRootNode : function(node){
16511         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16512         if(!this.rootVisible){
16513             node.ui = new Roo.tree.RootTreeNodeUI(node);
16514         }
16515         return node;
16516     },
16517
16518     /**
16519      * Returns the container element for this TreePanel
16520      */
16521     getEl : function(){
16522         return this.el;
16523     },
16524
16525     /**
16526      * Returns the default TreeLoader for this TreePanel
16527      */
16528     getLoader : function(){
16529         return this.loader;
16530     },
16531
16532     /**
16533      * Expand all nodes
16534      */
16535     expandAll : function(){
16536         this.root.expand(true);
16537     },
16538
16539     /**
16540      * Collapse all nodes
16541      */
16542     collapseAll : function(){
16543         this.root.collapse(true);
16544     },
16545
16546     /**
16547      * Returns the selection model used by this TreePanel
16548      */
16549     getSelectionModel : function(){
16550         if(!this.selModel){
16551             this.selModel = new Roo.tree.DefaultSelectionModel();
16552         }
16553         return this.selModel;
16554     },
16555
16556     /**
16557      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16558      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16559      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16560      * @return {Array}
16561      */
16562     getChecked : function(a, startNode){
16563         startNode = startNode || this.root;
16564         var r = [];
16565         var f = function(){
16566             if(this.attributes.checked){
16567                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16568             }
16569         }
16570         startNode.cascade(f);
16571         return r;
16572     },
16573
16574     /**
16575      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16576      * @param {String} path
16577      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16578      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16579      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16580      */
16581     expandPath : function(path, attr, callback){
16582         attr = attr || "id";
16583         var keys = path.split(this.pathSeparator);
16584         var curNode = this.root;
16585         if(curNode.attributes[attr] != keys[1]){ // invalid root
16586             if(callback){
16587                 callback(false, null);
16588             }
16589             return;
16590         }
16591         var index = 1;
16592         var f = function(){
16593             if(++index == keys.length){
16594                 if(callback){
16595                     callback(true, curNode);
16596                 }
16597                 return;
16598             }
16599             var c = curNode.findChild(attr, keys[index]);
16600             if(!c){
16601                 if(callback){
16602                     callback(false, curNode);
16603                 }
16604                 return;
16605             }
16606             curNode = c;
16607             c.expand(false, false, f);
16608         };
16609         curNode.expand(false, false, f);
16610     },
16611
16612     /**
16613      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16614      * @param {String} path
16615      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16616      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16617      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16618      */
16619     selectPath : function(path, attr, callback){
16620         attr = attr || "id";
16621         var keys = path.split(this.pathSeparator);
16622         var v = keys.pop();
16623         if(keys.length > 0){
16624             var f = function(success, node){
16625                 if(success && node){
16626                     var n = node.findChild(attr, v);
16627                     if(n){
16628                         n.select();
16629                         if(callback){
16630                             callback(true, n);
16631                         }
16632                     }else if(callback){
16633                         callback(false, n);
16634                     }
16635                 }else{
16636                     if(callback){
16637                         callback(false, n);
16638                     }
16639                 }
16640             };
16641             this.expandPath(keys.join(this.pathSeparator), attr, f);
16642         }else{
16643             this.root.select();
16644             if(callback){
16645                 callback(true, this.root);
16646             }
16647         }
16648     },
16649
16650     getTreeEl : function(){
16651         return this.el;
16652     },
16653
16654     /**
16655      * Trigger rendering of this TreePanel
16656      */
16657     render : function(){
16658         if (this.innerCt) {
16659             return this; // stop it rendering more than once!!
16660         }
16661         
16662         this.innerCt = this.el.createChild({tag:"ul",
16663                cls:"x-tree-root-ct " +
16664                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16665
16666         if(this.containerScroll){
16667             Roo.dd.ScrollManager.register(this.el);
16668         }
16669         if((this.enableDD || this.enableDrop) && !this.dropZone){
16670            /**
16671             * The dropZone used by this tree if drop is enabled
16672             * @type Roo.tree.TreeDropZone
16673             */
16674              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16675                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16676            });
16677         }
16678         if((this.enableDD || this.enableDrag) && !this.dragZone){
16679            /**
16680             * The dragZone used by this tree if drag is enabled
16681             * @type Roo.tree.TreeDragZone
16682             */
16683             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16684                ddGroup: this.ddGroup || "TreeDD",
16685                scroll: this.ddScroll
16686            });
16687         }
16688         this.getSelectionModel().init(this);
16689         if (!this.root) {
16690             console.log("ROOT not set in tree");
16691             return;
16692         }
16693         this.root.render();
16694         if(!this.rootVisible){
16695             this.root.renderChildren();
16696         }
16697         return this;
16698     }
16699 });/*
16700  * Based on:
16701  * Ext JS Library 1.1.1
16702  * Copyright(c) 2006-2007, Ext JS, LLC.
16703  *
16704  * Originally Released Under LGPL - original licence link has changed is not relivant.
16705  *
16706  * Fork - LGPL
16707  * <script type="text/javascript">
16708  */
16709  
16710
16711 /**
16712  * @class Roo.tree.DefaultSelectionModel
16713  * @extends Roo.util.Observable
16714  * The default single selection for a TreePanel.
16715  */
16716 Roo.tree.DefaultSelectionModel = function(){
16717    this.selNode = null;
16718    
16719    this.addEvents({
16720        /**
16721         * @event selectionchange
16722         * Fires when the selected node changes
16723         * @param {DefaultSelectionModel} this
16724         * @param {TreeNode} node the new selection
16725         */
16726        "selectionchange" : true,
16727
16728        /**
16729         * @event beforeselect
16730         * Fires before the selected node changes, return false to cancel the change
16731         * @param {DefaultSelectionModel} this
16732         * @param {TreeNode} node the new selection
16733         * @param {TreeNode} node the old selection
16734         */
16735        "beforeselect" : true
16736    });
16737 };
16738
16739 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16740     init : function(tree){
16741         this.tree = tree;
16742         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16743         tree.on("click", this.onNodeClick, this);
16744     },
16745     
16746     onNodeClick : function(node, e){
16747         if (e.ctrlKey && this.selNode == node)  {
16748             this.unselect(node);
16749             return;
16750         }
16751         this.select(node);
16752     },
16753     
16754     /**
16755      * Select a node.
16756      * @param {TreeNode} node The node to select
16757      * @return {TreeNode} The selected node
16758      */
16759     select : function(node){
16760         var last = this.selNode;
16761         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16762             if(last){
16763                 last.ui.onSelectedChange(false);
16764             }
16765             this.selNode = node;
16766             node.ui.onSelectedChange(true);
16767             this.fireEvent("selectionchange", this, node, last);
16768         }
16769         return node;
16770     },
16771     
16772     /**
16773      * Deselect a node.
16774      * @param {TreeNode} node The node to unselect
16775      */
16776     unselect : function(node){
16777         if(this.selNode == node){
16778             this.clearSelections();
16779         }    
16780     },
16781     
16782     /**
16783      * Clear all selections
16784      */
16785     clearSelections : function(){
16786         var n = this.selNode;
16787         if(n){
16788             n.ui.onSelectedChange(false);
16789             this.selNode = null;
16790             this.fireEvent("selectionchange", this, null);
16791         }
16792         return n;
16793     },
16794     
16795     /**
16796      * Get the selected node
16797      * @return {TreeNode} The selected node
16798      */
16799     getSelectedNode : function(){
16800         return this.selNode;    
16801     },
16802     
16803     /**
16804      * Returns true if the node is selected
16805      * @param {TreeNode} node The node to check
16806      * @return {Boolean}
16807      */
16808     isSelected : function(node){
16809         return this.selNode == node;  
16810     },
16811
16812     /**
16813      * Selects the node above the selected node in the tree, intelligently walking the nodes
16814      * @return TreeNode The new selection
16815      */
16816     selectPrevious : function(){
16817         var s = this.selNode || this.lastSelNode;
16818         if(!s){
16819             return null;
16820         }
16821         var ps = s.previousSibling;
16822         if(ps){
16823             if(!ps.isExpanded() || ps.childNodes.length < 1){
16824                 return this.select(ps);
16825             } else{
16826                 var lc = ps.lastChild;
16827                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16828                     lc = lc.lastChild;
16829                 }
16830                 return this.select(lc);
16831             }
16832         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16833             return this.select(s.parentNode);
16834         }
16835         return null;
16836     },
16837
16838     /**
16839      * Selects the node above the selected node in the tree, intelligently walking the nodes
16840      * @return TreeNode The new selection
16841      */
16842     selectNext : function(){
16843         var s = this.selNode || this.lastSelNode;
16844         if(!s){
16845             return null;
16846         }
16847         if(s.firstChild && s.isExpanded()){
16848              return this.select(s.firstChild);
16849          }else if(s.nextSibling){
16850              return this.select(s.nextSibling);
16851          }else if(s.parentNode){
16852             var newS = null;
16853             s.parentNode.bubble(function(){
16854                 if(this.nextSibling){
16855                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16856                     return false;
16857                 }
16858             });
16859             return newS;
16860          }
16861         return null;
16862     },
16863
16864     onKeyDown : function(e){
16865         var s = this.selNode || this.lastSelNode;
16866         // undesirable, but required
16867         var sm = this;
16868         if(!s){
16869             return;
16870         }
16871         var k = e.getKey();
16872         switch(k){
16873              case e.DOWN:
16874                  e.stopEvent();
16875                  this.selectNext();
16876              break;
16877              case e.UP:
16878                  e.stopEvent();
16879                  this.selectPrevious();
16880              break;
16881              case e.RIGHT:
16882                  e.preventDefault();
16883                  if(s.hasChildNodes()){
16884                      if(!s.isExpanded()){
16885                          s.expand();
16886                      }else if(s.firstChild){
16887                          this.select(s.firstChild, e);
16888                      }
16889                  }
16890              break;
16891              case e.LEFT:
16892                  e.preventDefault();
16893                  if(s.hasChildNodes() && s.isExpanded()){
16894                      s.collapse();
16895                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16896                      this.select(s.parentNode, e);
16897                  }
16898              break;
16899         };
16900     }
16901 });
16902
16903 /**
16904  * @class Roo.tree.MultiSelectionModel
16905  * @extends Roo.util.Observable
16906  * Multi selection for a TreePanel.
16907  */
16908 Roo.tree.MultiSelectionModel = function(){
16909    this.selNodes = [];
16910    this.selMap = {};
16911    this.addEvents({
16912        /**
16913         * @event selectionchange
16914         * Fires when the selected nodes change
16915         * @param {MultiSelectionModel} this
16916         * @param {Array} nodes Array of the selected nodes
16917         */
16918        "selectionchange" : true
16919    });
16920 };
16921
16922 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16923     init : function(tree){
16924         this.tree = tree;
16925         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16926         tree.on("click", this.onNodeClick, this);
16927     },
16928     
16929     onNodeClick : function(node, e){
16930         this.select(node, e, e.ctrlKey);
16931     },
16932     
16933     /**
16934      * Select a node.
16935      * @param {TreeNode} node The node to select
16936      * @param {EventObject} e (optional) An event associated with the selection
16937      * @param {Boolean} keepExisting True to retain existing selections
16938      * @return {TreeNode} The selected node
16939      */
16940     select : function(node, e, keepExisting){
16941         if(keepExisting !== true){
16942             this.clearSelections(true);
16943         }
16944         if(this.isSelected(node)){
16945             this.lastSelNode = node;
16946             return node;
16947         }
16948         this.selNodes.push(node);
16949         this.selMap[node.id] = node;
16950         this.lastSelNode = node;
16951         node.ui.onSelectedChange(true);
16952         this.fireEvent("selectionchange", this, this.selNodes);
16953         return node;
16954     },
16955     
16956     /**
16957      * Deselect a node.
16958      * @param {TreeNode} node The node to unselect
16959      */
16960     unselect : function(node){
16961         if(this.selMap[node.id]){
16962             node.ui.onSelectedChange(false);
16963             var sn = this.selNodes;
16964             var index = -1;
16965             if(sn.indexOf){
16966                 index = sn.indexOf(node);
16967             }else{
16968                 for(var i = 0, len = sn.length; i < len; i++){
16969                     if(sn[i] == node){
16970                         index = i;
16971                         break;
16972                     }
16973                 }
16974             }
16975             if(index != -1){
16976                 this.selNodes.splice(index, 1);
16977             }
16978             delete this.selMap[node.id];
16979             this.fireEvent("selectionchange", this, this.selNodes);
16980         }
16981     },
16982     
16983     /**
16984      * Clear all selections
16985      */
16986     clearSelections : function(suppressEvent){
16987         var sn = this.selNodes;
16988         if(sn.length > 0){
16989             for(var i = 0, len = sn.length; i < len; i++){
16990                 sn[i].ui.onSelectedChange(false);
16991             }
16992             this.selNodes = [];
16993             this.selMap = {};
16994             if(suppressEvent !== true){
16995                 this.fireEvent("selectionchange", this, this.selNodes);
16996             }
16997         }
16998     },
16999     
17000     /**
17001      * Returns true if the node is selected
17002      * @param {TreeNode} node The node to check
17003      * @return {Boolean}
17004      */
17005     isSelected : function(node){
17006         return this.selMap[node.id] ? true : false;  
17007     },
17008     
17009     /**
17010      * Returns an array of the selected nodes
17011      * @return {Array}
17012      */
17013     getSelectedNodes : function(){
17014         return this.selNodes;    
17015     },
17016
17017     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17018
17019     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17020
17021     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17022 });/*
17023  * Based on:
17024  * Ext JS Library 1.1.1
17025  * Copyright(c) 2006-2007, Ext JS, LLC.
17026  *
17027  * Originally Released Under LGPL - original licence link has changed is not relivant.
17028  *
17029  * Fork - LGPL
17030  * <script type="text/javascript">
17031  */
17032  
17033 /**
17034  * @class Roo.tree.TreeNode
17035  * @extends Roo.data.Node
17036  * @cfg {String} text The text for this node
17037  * @cfg {Boolean} expanded true to start the node expanded
17038  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17039  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17040  * @cfg {Boolean} disabled true to start the node disabled
17041  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17042  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17043  * @cfg {String} cls A css class to be added to the node
17044  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17045  * @cfg {String} href URL of the link used for the node (defaults to #)
17046  * @cfg {String} hrefTarget target frame for the link
17047  * @cfg {String} qtip An Ext QuickTip for the node
17048  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17049  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17050  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17051  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17052  * (defaults to undefined with no checkbox rendered)
17053  * @constructor
17054  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17055  */
17056 Roo.tree.TreeNode = function(attributes){
17057     attributes = attributes || {};
17058     if(typeof attributes == "string"){
17059         attributes = {text: attributes};
17060     }
17061     this.childrenRendered = false;
17062     this.rendered = false;
17063     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17064     this.expanded = attributes.expanded === true;
17065     this.isTarget = attributes.isTarget !== false;
17066     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17067     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17068
17069     /**
17070      * Read-only. The text for this node. To change it use setText().
17071      * @type String
17072      */
17073     this.text = attributes.text;
17074     /**
17075      * True if this node is disabled.
17076      * @type Boolean
17077      */
17078     this.disabled = attributes.disabled === true;
17079
17080     this.addEvents({
17081         /**
17082         * @event textchange
17083         * Fires when the text for this node is changed
17084         * @param {Node} this This node
17085         * @param {String} text The new text
17086         * @param {String} oldText The old text
17087         */
17088         "textchange" : true,
17089         /**
17090         * @event beforeexpand
17091         * Fires before this node is expanded, return false to cancel.
17092         * @param {Node} this This node
17093         * @param {Boolean} deep
17094         * @param {Boolean} anim
17095         */
17096         "beforeexpand" : true,
17097         /**
17098         * @event beforecollapse
17099         * Fires before this node is collapsed, return false to cancel.
17100         * @param {Node} this This node
17101         * @param {Boolean} deep
17102         * @param {Boolean} anim
17103         */
17104         "beforecollapse" : true,
17105         /**
17106         * @event expand
17107         * Fires when this node is expanded
17108         * @param {Node} this This node
17109         */
17110         "expand" : true,
17111         /**
17112         * @event disabledchange
17113         * Fires when the disabled status of this node changes
17114         * @param {Node} this This node
17115         * @param {Boolean} disabled
17116         */
17117         "disabledchange" : true,
17118         /**
17119         * @event collapse
17120         * Fires when this node is collapsed
17121         * @param {Node} this This node
17122         */
17123         "collapse" : true,
17124         /**
17125         * @event beforeclick
17126         * Fires before click processing. Return false to cancel the default action.
17127         * @param {Node} this This node
17128         * @param {Roo.EventObject} e The event object
17129         */
17130         "beforeclick":true,
17131         /**
17132         * @event checkchange
17133         * Fires when a node with a checkbox's checked property changes
17134         * @param {Node} this This node
17135         * @param {Boolean} checked
17136         */
17137         "checkchange":true,
17138         /**
17139         * @event click
17140         * Fires when this node is clicked
17141         * @param {Node} this This node
17142         * @param {Roo.EventObject} e The event object
17143         */
17144         "click":true,
17145         /**
17146         * @event dblclick
17147         * Fires when this node is double clicked
17148         * @param {Node} this This node
17149         * @param {Roo.EventObject} e The event object
17150         */
17151         "dblclick":true,
17152         /**
17153         * @event contextmenu
17154         * Fires when this node is right clicked
17155         * @param {Node} this This node
17156         * @param {Roo.EventObject} e The event object
17157         */
17158         "contextmenu":true,
17159         /**
17160         * @event beforechildrenrendered
17161         * Fires right before the child nodes for this node are rendered
17162         * @param {Node} this This node
17163         */
17164         "beforechildrenrendered":true
17165     });
17166
17167     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17168
17169     /**
17170      * Read-only. The UI for this node
17171      * @type TreeNodeUI
17172      */
17173     this.ui = new uiClass(this);
17174 };
17175 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17176     preventHScroll: true,
17177     /**
17178      * Returns true if this node is expanded
17179      * @return {Boolean}
17180      */
17181     isExpanded : function(){
17182         return this.expanded;
17183     },
17184
17185     /**
17186      * Returns the UI object for this node
17187      * @return {TreeNodeUI}
17188      */
17189     getUI : function(){
17190         return this.ui;
17191     },
17192
17193     // private override
17194     setFirstChild : function(node){
17195         var of = this.firstChild;
17196         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17197         if(this.childrenRendered && of && node != of){
17198             of.renderIndent(true, true);
17199         }
17200         if(this.rendered){
17201             this.renderIndent(true, true);
17202         }
17203     },
17204
17205     // private override
17206     setLastChild : function(node){
17207         var ol = this.lastChild;
17208         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17209         if(this.childrenRendered && ol && node != ol){
17210             ol.renderIndent(true, true);
17211         }
17212         if(this.rendered){
17213             this.renderIndent(true, true);
17214         }
17215     },
17216
17217     // these methods are overridden to provide lazy rendering support
17218     // private override
17219     appendChild : function(){
17220         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17221         if(node && this.childrenRendered){
17222             node.render();
17223         }
17224         this.ui.updateExpandIcon();
17225         return node;
17226     },
17227
17228     // private override
17229     removeChild : function(node){
17230         this.ownerTree.getSelectionModel().unselect(node);
17231         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17232         // if it's been rendered remove dom node
17233         if(this.childrenRendered){
17234             node.ui.remove();
17235         }
17236         if(this.childNodes.length < 1){
17237             this.collapse(false, false);
17238         }else{
17239             this.ui.updateExpandIcon();
17240         }
17241         if(!this.firstChild) {
17242             this.childrenRendered = false;
17243         }
17244         return node;
17245     },
17246
17247     // private override
17248     insertBefore : function(node, refNode){
17249         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17250         if(newNode && refNode && this.childrenRendered){
17251             node.render();
17252         }
17253         this.ui.updateExpandIcon();
17254         return newNode;
17255     },
17256
17257     /**
17258      * Sets the text for this node
17259      * @param {String} text
17260      */
17261     setText : function(text){
17262         var oldText = this.text;
17263         this.text = text;
17264         this.attributes.text = text;
17265         if(this.rendered){ // event without subscribing
17266             this.ui.onTextChange(this, text, oldText);
17267         }
17268         this.fireEvent("textchange", this, text, oldText);
17269     },
17270
17271     /**
17272      * Triggers selection of this node
17273      */
17274     select : function(){
17275         this.getOwnerTree().getSelectionModel().select(this);
17276     },
17277
17278     /**
17279      * Triggers deselection of this node
17280      */
17281     unselect : function(){
17282         this.getOwnerTree().getSelectionModel().unselect(this);
17283     },
17284
17285     /**
17286      * Returns true if this node is selected
17287      * @return {Boolean}
17288      */
17289     isSelected : function(){
17290         return this.getOwnerTree().getSelectionModel().isSelected(this);
17291     },
17292
17293     /**
17294      * Expand this node.
17295      * @param {Boolean} deep (optional) True to expand all children as well
17296      * @param {Boolean} anim (optional) false to cancel the default animation
17297      * @param {Function} callback (optional) A callback to be called when
17298      * expanding this node completes (does not wait for deep expand to complete).
17299      * Called with 1 parameter, this node.
17300      */
17301     expand : function(deep, anim, callback){
17302         if(!this.expanded){
17303             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17304                 return;
17305             }
17306             if(!this.childrenRendered){
17307                 this.renderChildren();
17308             }
17309             this.expanded = true;
17310             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17311                 this.ui.animExpand(function(){
17312                     this.fireEvent("expand", this);
17313                     if(typeof callback == "function"){
17314                         callback(this);
17315                     }
17316                     if(deep === true){
17317                         this.expandChildNodes(true);
17318                     }
17319                 }.createDelegate(this));
17320                 return;
17321             }else{
17322                 this.ui.expand();
17323                 this.fireEvent("expand", this);
17324                 if(typeof callback == "function"){
17325                     callback(this);
17326                 }
17327             }
17328         }else{
17329            if(typeof callback == "function"){
17330                callback(this);
17331            }
17332         }
17333         if(deep === true){
17334             this.expandChildNodes(true);
17335         }
17336     },
17337
17338     isHiddenRoot : function(){
17339         return this.isRoot && !this.getOwnerTree().rootVisible;
17340     },
17341
17342     /**
17343      * Collapse this node.
17344      * @param {Boolean} deep (optional) True to collapse all children as well
17345      * @param {Boolean} anim (optional) false to cancel the default animation
17346      */
17347     collapse : function(deep, anim){
17348         if(this.expanded && !this.isHiddenRoot()){
17349             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17350                 return;
17351             }
17352             this.expanded = false;
17353             if((this.getOwnerTree().animate && anim !== false) || anim){
17354                 this.ui.animCollapse(function(){
17355                     this.fireEvent("collapse", this);
17356                     if(deep === true){
17357                         this.collapseChildNodes(true);
17358                     }
17359                 }.createDelegate(this));
17360                 return;
17361             }else{
17362                 this.ui.collapse();
17363                 this.fireEvent("collapse", this);
17364             }
17365         }
17366         if(deep === true){
17367             var cs = this.childNodes;
17368             for(var i = 0, len = cs.length; i < len; i++) {
17369                 cs[i].collapse(true, false);
17370             }
17371         }
17372     },
17373
17374     // private
17375     delayedExpand : function(delay){
17376         if(!this.expandProcId){
17377             this.expandProcId = this.expand.defer(delay, this);
17378         }
17379     },
17380
17381     // private
17382     cancelExpand : function(){
17383         if(this.expandProcId){
17384             clearTimeout(this.expandProcId);
17385         }
17386         this.expandProcId = false;
17387     },
17388
17389     /**
17390      * Toggles expanded/collapsed state of the node
17391      */
17392     toggle : function(){
17393         if(this.expanded){
17394             this.collapse();
17395         }else{
17396             this.expand();
17397         }
17398     },
17399
17400     /**
17401      * Ensures all parent nodes are expanded
17402      */
17403     ensureVisible : function(callback){
17404         var tree = this.getOwnerTree();
17405         tree.expandPath(this.parentNode.getPath(), false, function(){
17406             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17407             Roo.callback(callback);
17408         }.createDelegate(this));
17409     },
17410
17411     /**
17412      * Expand all child nodes
17413      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17414      */
17415     expandChildNodes : function(deep){
17416         var cs = this.childNodes;
17417         for(var i = 0, len = cs.length; i < len; i++) {
17418                 cs[i].expand(deep);
17419         }
17420     },
17421
17422     /**
17423      * Collapse all child nodes
17424      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17425      */
17426     collapseChildNodes : function(deep){
17427         var cs = this.childNodes;
17428         for(var i = 0, len = cs.length; i < len; i++) {
17429                 cs[i].collapse(deep);
17430         }
17431     },
17432
17433     /**
17434      * Disables this node
17435      */
17436     disable : function(){
17437         this.disabled = true;
17438         this.unselect();
17439         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17440             this.ui.onDisableChange(this, true);
17441         }
17442         this.fireEvent("disabledchange", this, true);
17443     },
17444
17445     /**
17446      * Enables this node
17447      */
17448     enable : function(){
17449         this.disabled = false;
17450         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17451             this.ui.onDisableChange(this, false);
17452         }
17453         this.fireEvent("disabledchange", this, false);
17454     },
17455
17456     // private
17457     renderChildren : function(suppressEvent){
17458         if(suppressEvent !== false){
17459             this.fireEvent("beforechildrenrendered", this);
17460         }
17461         var cs = this.childNodes;
17462         for(var i = 0, len = cs.length; i < len; i++){
17463             cs[i].render(true);
17464         }
17465         this.childrenRendered = true;
17466     },
17467
17468     // private
17469     sort : function(fn, scope){
17470         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17471         if(this.childrenRendered){
17472             var cs = this.childNodes;
17473             for(var i = 0, len = cs.length; i < len; i++){
17474                 cs[i].render(true);
17475             }
17476         }
17477     },
17478
17479     // private
17480     render : function(bulkRender){
17481         this.ui.render(bulkRender);
17482         if(!this.rendered){
17483             this.rendered = true;
17484             if(this.expanded){
17485                 this.expanded = false;
17486                 this.expand(false, false);
17487             }
17488         }
17489     },
17490
17491     // private
17492     renderIndent : function(deep, refresh){
17493         if(refresh){
17494             this.ui.childIndent = null;
17495         }
17496         this.ui.renderIndent();
17497         if(deep === true && this.childrenRendered){
17498             var cs = this.childNodes;
17499             for(var i = 0, len = cs.length; i < len; i++){
17500                 cs[i].renderIndent(true, refresh);
17501             }
17502         }
17503     }
17504 });/*
17505  * Based on:
17506  * Ext JS Library 1.1.1
17507  * Copyright(c) 2006-2007, Ext JS, LLC.
17508  *
17509  * Originally Released Under LGPL - original licence link has changed is not relivant.
17510  *
17511  * Fork - LGPL
17512  * <script type="text/javascript">
17513  */
17514  
17515 /**
17516  * @class Roo.tree.AsyncTreeNode
17517  * @extends Roo.tree.TreeNode
17518  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17519  * @constructor
17520  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17521  */
17522  Roo.tree.AsyncTreeNode = function(config){
17523     this.loaded = false;
17524     this.loading = false;
17525     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17526     /**
17527     * @event beforeload
17528     * Fires before this node is loaded, return false to cancel
17529     * @param {Node} this This node
17530     */
17531     this.addEvents({'beforeload':true, 'load': true});
17532     /**
17533     * @event load
17534     * Fires when this node is loaded
17535     * @param {Node} this This node
17536     */
17537     /**
17538      * The loader used by this node (defaults to using the tree's defined loader)
17539      * @type TreeLoader
17540      * @property loader
17541      */
17542 };
17543 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17544     expand : function(deep, anim, callback){
17545         if(this.loading){ // if an async load is already running, waiting til it's done
17546             var timer;
17547             var f = function(){
17548                 if(!this.loading){ // done loading
17549                     clearInterval(timer);
17550                     this.expand(deep, anim, callback);
17551                 }
17552             }.createDelegate(this);
17553             timer = setInterval(f, 200);
17554             return;
17555         }
17556         if(!this.loaded){
17557             if(this.fireEvent("beforeload", this) === false){
17558                 return;
17559             }
17560             this.loading = true;
17561             this.ui.beforeLoad(this);
17562             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17563             if(loader){
17564                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17565                 return;
17566             }
17567         }
17568         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17569     },
17570     
17571     /**
17572      * Returns true if this node is currently loading
17573      * @return {Boolean}
17574      */
17575     isLoading : function(){
17576         return this.loading;  
17577     },
17578     
17579     loadComplete : function(deep, anim, callback){
17580         this.loading = false;
17581         this.loaded = true;
17582         this.ui.afterLoad(this);
17583         this.fireEvent("load", this);
17584         this.expand(deep, anim, callback);
17585     },
17586     
17587     /**
17588      * Returns true if this node has been loaded
17589      * @return {Boolean}
17590      */
17591     isLoaded : function(){
17592         return this.loaded;
17593     },
17594     
17595     hasChildNodes : function(){
17596         if(!this.isLeaf() && !this.loaded){
17597             return true;
17598         }else{
17599             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17600         }
17601     },
17602
17603     /**
17604      * Trigger a reload for this node
17605      * @param {Function} callback
17606      */
17607     reload : function(callback){
17608         this.collapse(false, false);
17609         while(this.firstChild){
17610             this.removeChild(this.firstChild);
17611         }
17612         this.childrenRendered = false;
17613         this.loaded = false;
17614         if(this.isHiddenRoot()){
17615             this.expanded = false;
17616         }
17617         this.expand(false, false, callback);
17618     }
17619 });/*
17620  * Based on:
17621  * Ext JS Library 1.1.1
17622  * Copyright(c) 2006-2007, Ext JS, LLC.
17623  *
17624  * Originally Released Under LGPL - original licence link has changed is not relivant.
17625  *
17626  * Fork - LGPL
17627  * <script type="text/javascript">
17628  */
17629  
17630 /**
17631  * @class Roo.tree.TreeNodeUI
17632  * @constructor
17633  * @param {Object} node The node to render
17634  * The TreeNode UI implementation is separate from the
17635  * tree implementation. Unless you are customizing the tree UI,
17636  * you should never have to use this directly.
17637  */
17638 Roo.tree.TreeNodeUI = function(node){
17639     this.node = node;
17640     this.rendered = false;
17641     this.animating = false;
17642     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17643 };
17644
17645 Roo.tree.TreeNodeUI.prototype = {
17646     removeChild : function(node){
17647         if(this.rendered){
17648             this.ctNode.removeChild(node.ui.getEl());
17649         }
17650     },
17651
17652     beforeLoad : function(){
17653          this.addClass("x-tree-node-loading");
17654     },
17655
17656     afterLoad : function(){
17657          this.removeClass("x-tree-node-loading");
17658     },
17659
17660     onTextChange : function(node, text, oldText){
17661         if(this.rendered){
17662             this.textNode.innerHTML = text;
17663         }
17664     },
17665
17666     onDisableChange : function(node, state){
17667         this.disabled = state;
17668         if(state){
17669             this.addClass("x-tree-node-disabled");
17670         }else{
17671             this.removeClass("x-tree-node-disabled");
17672         }
17673     },
17674
17675     onSelectedChange : function(state){
17676         if(state){
17677             this.focus();
17678             this.addClass("x-tree-selected");
17679         }else{
17680             //this.blur();
17681             this.removeClass("x-tree-selected");
17682         }
17683     },
17684
17685     onMove : function(tree, node, oldParent, newParent, index, refNode){
17686         this.childIndent = null;
17687         if(this.rendered){
17688             var targetNode = newParent.ui.getContainer();
17689             if(!targetNode){//target not rendered
17690                 this.holder = document.createElement("div");
17691                 this.holder.appendChild(this.wrap);
17692                 return;
17693             }
17694             var insertBefore = refNode ? refNode.ui.getEl() : null;
17695             if(insertBefore){
17696                 targetNode.insertBefore(this.wrap, insertBefore);
17697             }else{
17698                 targetNode.appendChild(this.wrap);
17699             }
17700             this.node.renderIndent(true);
17701         }
17702     },
17703
17704     addClass : function(cls){
17705         if(this.elNode){
17706             Roo.fly(this.elNode).addClass(cls);
17707         }
17708     },
17709
17710     removeClass : function(cls){
17711         if(this.elNode){
17712             Roo.fly(this.elNode).removeClass(cls);
17713         }
17714     },
17715
17716     remove : function(){
17717         if(this.rendered){
17718             this.holder = document.createElement("div");
17719             this.holder.appendChild(this.wrap);
17720         }
17721     },
17722
17723     fireEvent : function(){
17724         return this.node.fireEvent.apply(this.node, arguments);
17725     },
17726
17727     initEvents : function(){
17728         this.node.on("move", this.onMove, this);
17729         var E = Roo.EventManager;
17730         var a = this.anchor;
17731
17732         var el = Roo.fly(a, '_treeui');
17733
17734         if(Roo.isOpera){ // opera render bug ignores the CSS
17735             el.setStyle("text-decoration", "none");
17736         }
17737
17738         el.on("click", this.onClick, this);
17739         el.on("dblclick", this.onDblClick, this);
17740
17741         if(this.checkbox){
17742             Roo.EventManager.on(this.checkbox,
17743                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17744         }
17745
17746         el.on("contextmenu", this.onContextMenu, this);
17747
17748         var icon = Roo.fly(this.iconNode);
17749         icon.on("click", this.onClick, this);
17750         icon.on("dblclick", this.onDblClick, this);
17751         icon.on("contextmenu", this.onContextMenu, this);
17752         E.on(this.ecNode, "click", this.ecClick, this, true);
17753
17754         if(this.node.disabled){
17755             this.addClass("x-tree-node-disabled");
17756         }
17757         if(this.node.hidden){
17758             this.addClass("x-tree-node-disabled");
17759         }
17760         var ot = this.node.getOwnerTree();
17761         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17762         if(dd && (!this.node.isRoot || ot.rootVisible)){
17763             Roo.dd.Registry.register(this.elNode, {
17764                 node: this.node,
17765                 handles: this.getDDHandles(),
17766                 isHandle: false
17767             });
17768         }
17769     },
17770
17771     getDDHandles : function(){
17772         return [this.iconNode, this.textNode];
17773     },
17774
17775     hide : function(){
17776         if(this.rendered){
17777             this.wrap.style.display = "none";
17778         }
17779     },
17780
17781     show : function(){
17782         if(this.rendered){
17783             this.wrap.style.display = "";
17784         }
17785     },
17786
17787     onContextMenu : function(e){
17788         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17789             e.preventDefault();
17790             this.focus();
17791             this.fireEvent("contextmenu", this.node, e);
17792         }
17793     },
17794
17795     onClick : function(e){
17796         if(this.dropping){
17797             e.stopEvent();
17798             return;
17799         }
17800         if(this.fireEvent("beforeclick", this.node, e) !== false){
17801             if(!this.disabled && this.node.attributes.href){
17802                 this.fireEvent("click", this.node, e);
17803                 return;
17804             }
17805             e.preventDefault();
17806             if(this.disabled){
17807                 return;
17808             }
17809
17810             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17811                 this.node.toggle();
17812             }
17813
17814             this.fireEvent("click", this.node, e);
17815         }else{
17816             e.stopEvent();
17817         }
17818     },
17819
17820     onDblClick : function(e){
17821         e.preventDefault();
17822         if(this.disabled){
17823             return;
17824         }
17825         if(this.checkbox){
17826             this.toggleCheck();
17827         }
17828         if(!this.animating && this.node.hasChildNodes()){
17829             this.node.toggle();
17830         }
17831         this.fireEvent("dblclick", this.node, e);
17832     },
17833
17834     onCheckChange : function(){
17835         var checked = this.checkbox.checked;
17836         this.node.attributes.checked = checked;
17837         this.fireEvent('checkchange', this.node, checked);
17838     },
17839
17840     ecClick : function(e){
17841         if(!this.animating && this.node.hasChildNodes()){
17842             this.node.toggle();
17843         }
17844     },
17845
17846     startDrop : function(){
17847         this.dropping = true;
17848     },
17849
17850     // delayed drop so the click event doesn't get fired on a drop
17851     endDrop : function(){
17852        setTimeout(function(){
17853            this.dropping = false;
17854        }.createDelegate(this), 50);
17855     },
17856
17857     expand : function(){
17858         this.updateExpandIcon();
17859         this.ctNode.style.display = "";
17860     },
17861
17862     focus : function(){
17863         if(!this.node.preventHScroll){
17864             try{this.anchor.focus();
17865             }catch(e){}
17866         }else if(!Roo.isIE){
17867             try{
17868                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17869                 var l = noscroll.scrollLeft;
17870                 this.anchor.focus();
17871                 noscroll.scrollLeft = l;
17872             }catch(e){}
17873         }
17874     },
17875
17876     toggleCheck : function(value){
17877         var cb = this.checkbox;
17878         if(cb){
17879             cb.checked = (value === undefined ? !cb.checked : value);
17880         }
17881     },
17882
17883     blur : function(){
17884         try{
17885             this.anchor.blur();
17886         }catch(e){}
17887     },
17888
17889     animExpand : function(callback){
17890         var ct = Roo.get(this.ctNode);
17891         ct.stopFx();
17892         if(!this.node.hasChildNodes()){
17893             this.updateExpandIcon();
17894             this.ctNode.style.display = "";
17895             Roo.callback(callback);
17896             return;
17897         }
17898         this.animating = true;
17899         this.updateExpandIcon();
17900
17901         ct.slideIn('t', {
17902            callback : function(){
17903                this.animating = false;
17904                Roo.callback(callback);
17905             },
17906             scope: this,
17907             duration: this.node.ownerTree.duration || .25
17908         });
17909     },
17910
17911     highlight : function(){
17912         var tree = this.node.getOwnerTree();
17913         Roo.fly(this.wrap).highlight(
17914             tree.hlColor || "C3DAF9",
17915             {endColor: tree.hlBaseColor}
17916         );
17917     },
17918
17919     collapse : function(){
17920         this.updateExpandIcon();
17921         this.ctNode.style.display = "none";
17922     },
17923
17924     animCollapse : function(callback){
17925         var ct = Roo.get(this.ctNode);
17926         ct.enableDisplayMode('block');
17927         ct.stopFx();
17928
17929         this.animating = true;
17930         this.updateExpandIcon();
17931
17932         ct.slideOut('t', {
17933             callback : function(){
17934                this.animating = false;
17935                Roo.callback(callback);
17936             },
17937             scope: this,
17938             duration: this.node.ownerTree.duration || .25
17939         });
17940     },
17941
17942     getContainer : function(){
17943         return this.ctNode;
17944     },
17945
17946     getEl : function(){
17947         return this.wrap;
17948     },
17949
17950     appendDDGhost : function(ghostNode){
17951         ghostNode.appendChild(this.elNode.cloneNode(true));
17952     },
17953
17954     getDDRepairXY : function(){
17955         return Roo.lib.Dom.getXY(this.iconNode);
17956     },
17957
17958     onRender : function(){
17959         this.render();
17960     },
17961
17962     render : function(bulkRender){
17963         var n = this.node, a = n.attributes;
17964         var targetNode = n.parentNode ?
17965               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17966
17967         if(!this.rendered){
17968             this.rendered = true;
17969
17970             this.renderElements(n, a, targetNode, bulkRender);
17971
17972             if(a.qtip){
17973                if(this.textNode.setAttributeNS){
17974                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17975                    if(a.qtipTitle){
17976                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17977                    }
17978                }else{
17979                    this.textNode.setAttribute("ext:qtip", a.qtip);
17980                    if(a.qtipTitle){
17981                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17982                    }
17983                }
17984             }else if(a.qtipCfg){
17985                 a.qtipCfg.target = Roo.id(this.textNode);
17986                 Roo.QuickTips.register(a.qtipCfg);
17987             }
17988             this.initEvents();
17989             if(!this.node.expanded){
17990                 this.updateExpandIcon();
17991             }
17992         }else{
17993             if(bulkRender === true) {
17994                 targetNode.appendChild(this.wrap);
17995             }
17996         }
17997     },
17998
17999     renderElements : function(n, a, targetNode, bulkRender){
18000         // add some indent caching, this helps performance when rendering a large tree
18001         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18002         var t = n.getOwnerTree();
18003         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18004         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18005         var cb = typeof a.checked == 'boolean';
18006         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18007         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18008             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18009             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18010             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18011             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18012             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18013              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18014                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18015             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18016             "</li>"];
18017
18018         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18019             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18020                                 n.nextSibling.ui.getEl(), buf.join(""));
18021         }else{
18022             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18023         }
18024
18025         this.elNode = this.wrap.childNodes[0];
18026         this.ctNode = this.wrap.childNodes[1];
18027         var cs = this.elNode.childNodes;
18028         this.indentNode = cs[0];
18029         this.ecNode = cs[1];
18030         this.iconNode = cs[2];
18031         var index = 3;
18032         if(cb){
18033             this.checkbox = cs[3];
18034             index++;
18035         }
18036         this.anchor = cs[index];
18037         this.textNode = cs[index].firstChild;
18038     },
18039
18040     getAnchor : function(){
18041         return this.anchor;
18042     },
18043
18044     getTextEl : function(){
18045         return this.textNode;
18046     },
18047
18048     getIconEl : function(){
18049         return this.iconNode;
18050     },
18051
18052     isChecked : function(){
18053         return this.checkbox ? this.checkbox.checked : false;
18054     },
18055
18056     updateExpandIcon : function(){
18057         if(this.rendered){
18058             var n = this.node, c1, c2;
18059             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18060             var hasChild = n.hasChildNodes();
18061             if(hasChild){
18062                 if(n.expanded){
18063                     cls += "-minus";
18064                     c1 = "x-tree-node-collapsed";
18065                     c2 = "x-tree-node-expanded";
18066                 }else{
18067                     cls += "-plus";
18068                     c1 = "x-tree-node-expanded";
18069                     c2 = "x-tree-node-collapsed";
18070                 }
18071                 if(this.wasLeaf){
18072                     this.removeClass("x-tree-node-leaf");
18073                     this.wasLeaf = false;
18074                 }
18075                 if(this.c1 != c1 || this.c2 != c2){
18076                     Roo.fly(this.elNode).replaceClass(c1, c2);
18077                     this.c1 = c1; this.c2 = c2;
18078                 }
18079             }else{
18080                 if(!this.wasLeaf){
18081                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18082                     delete this.c1;
18083                     delete this.c2;
18084                     this.wasLeaf = true;
18085                 }
18086             }
18087             var ecc = "x-tree-ec-icon "+cls;
18088             if(this.ecc != ecc){
18089                 this.ecNode.className = ecc;
18090                 this.ecc = ecc;
18091             }
18092         }
18093     },
18094
18095     getChildIndent : function(){
18096         if(!this.childIndent){
18097             var buf = [];
18098             var p = this.node;
18099             while(p){
18100                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18101                     if(!p.isLast()) {
18102                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18103                     } else {
18104                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18105                     }
18106                 }
18107                 p = p.parentNode;
18108             }
18109             this.childIndent = buf.join("");
18110         }
18111         return this.childIndent;
18112     },
18113
18114     renderIndent : function(){
18115         if(this.rendered){
18116             var indent = "";
18117             var p = this.node.parentNode;
18118             if(p){
18119                 indent = p.ui.getChildIndent();
18120             }
18121             if(this.indentMarkup != indent){ // don't rerender if not required
18122                 this.indentNode.innerHTML = indent;
18123                 this.indentMarkup = indent;
18124             }
18125             this.updateExpandIcon();
18126         }
18127     }
18128 };
18129
18130 Roo.tree.RootTreeNodeUI = function(){
18131     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18132 };
18133 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18134     render : function(){
18135         if(!this.rendered){
18136             var targetNode = this.node.ownerTree.innerCt.dom;
18137             this.node.expanded = true;
18138             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18139             this.wrap = this.ctNode = targetNode.firstChild;
18140         }
18141     },
18142     collapse : function(){
18143     },
18144     expand : function(){
18145     }
18146 });/*
18147  * Based on:
18148  * Ext JS Library 1.1.1
18149  * Copyright(c) 2006-2007, Ext JS, LLC.
18150  *
18151  * Originally Released Under LGPL - original licence link has changed is not relivant.
18152  *
18153  * Fork - LGPL
18154  * <script type="text/javascript">
18155  */
18156 /**
18157  * @class Roo.tree.TreeLoader
18158  * @extends Roo.util.Observable
18159  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18160  * nodes from a specified URL. The response must be a javascript Array definition
18161  * who's elements are node definition objects. eg:
18162  * <pre><code>
18163    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18164     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18165 </code></pre>
18166  * <br><br>
18167  * A server request is sent, and child nodes are loaded only when a node is expanded.
18168  * The loading node's id is passed to the server under the parameter name "node" to
18169  * enable the server to produce the correct child nodes.
18170  * <br><br>
18171  * To pass extra parameters, an event handler may be attached to the "beforeload"
18172  * event, and the parameters specified in the TreeLoader's baseParams property:
18173  * <pre><code>
18174     myTreeLoader.on("beforeload", function(treeLoader, node) {
18175         this.baseParams.category = node.attributes.category;
18176     }, this);
18177 </code></pre><
18178  * This would pass an HTTP parameter called "category" to the server containing
18179  * the value of the Node's "category" attribute.
18180  * @constructor
18181  * Creates a new Treeloader.
18182  * @param {Object} config A config object containing config properties.
18183  */
18184 Roo.tree.TreeLoader = function(config){
18185     this.baseParams = {};
18186     this.requestMethod = "POST";
18187     Roo.apply(this, config);
18188
18189     this.addEvents({
18190     
18191         /**
18192          * @event beforeload
18193          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18194          * @param {Object} This TreeLoader object.
18195          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18196          * @param {Object} callback The callback function specified in the {@link #load} call.
18197          */
18198         beforeload : true,
18199         /**
18200          * @event load
18201          * Fires when the node has been successfuly loaded.
18202          * @param {Object} This TreeLoader object.
18203          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18204          * @param {Object} response The response object containing the data from the server.
18205          */
18206         load : true,
18207         /**
18208          * @event loadexception
18209          * Fires if the network request failed.
18210          * @param {Object} This TreeLoader object.
18211          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18212          * @param {Object} response The response object containing the data from the server.
18213          */
18214         loadexception : true,
18215         /**
18216          * @event create
18217          * Fires before a node is created, enabling you to return custom Node types 
18218          * @param {Object} This TreeLoader object.
18219          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18220          */
18221         create : true
18222     });
18223
18224     Roo.tree.TreeLoader.superclass.constructor.call(this);
18225 };
18226
18227 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18228     /**
18229     * @cfg {String} dataUrl The URL from which to request a Json string which
18230     * specifies an array of node definition object representing the child nodes
18231     * to be loaded.
18232     */
18233     /**
18234     * @cfg {Object} baseParams (optional) An object containing properties which
18235     * specify HTTP parameters to be passed to each request for child nodes.
18236     */
18237     /**
18238     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18239     * created by this loader. If the attributes sent by the server have an attribute in this object,
18240     * they take priority.
18241     */
18242     /**
18243     * @cfg {Object} uiProviders (optional) An object containing properties which
18244     * 
18245     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18246     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18247     * <i>uiProvider</i> attribute of a returned child node is a string rather
18248     * than a reference to a TreeNodeUI implementation, this that string value
18249     * is used as a property name in the uiProviders object. You can define the provider named
18250     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18251     */
18252     uiProviders : {},
18253
18254     /**
18255     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18256     * child nodes before loading.
18257     */
18258     clearOnLoad : true,
18259
18260     /**
18261     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18262     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18263     * Grid query { data : [ .....] }
18264     */
18265     
18266     root : false,
18267      /**
18268     * @cfg {String} queryParam (optional) 
18269     * Name of the query as it will be passed on the querystring (defaults to 'node')
18270     * eg. the request will be ?node=[id]
18271     */
18272     
18273     
18274     queryParam: false,
18275     
18276     /**
18277      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18278      * This is called automatically when a node is expanded, but may be used to reload
18279      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18280      * @param {Roo.tree.TreeNode} node
18281      * @param {Function} callback
18282      */
18283     load : function(node, callback){
18284         if(this.clearOnLoad){
18285             while(node.firstChild){
18286                 node.removeChild(node.firstChild);
18287             }
18288         }
18289         if(node.attributes.children){ // preloaded json children
18290             var cs = node.attributes.children;
18291             for(var i = 0, len = cs.length; i < len; i++){
18292                 node.appendChild(this.createNode(cs[i]));
18293             }
18294             if(typeof callback == "function"){
18295                 callback();
18296             }
18297         }else if(this.dataUrl){
18298             this.requestData(node, callback);
18299         }
18300     },
18301
18302     getParams: function(node){
18303         var buf = [], bp = this.baseParams;
18304         for(var key in bp){
18305             if(typeof bp[key] != "function"){
18306                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18307             }
18308         }
18309         var n = this.queryParam === false ? 'node' : this.queryParam;
18310         buf.push(n + "=", encodeURIComponent(node.id));
18311         return buf.join("");
18312     },
18313
18314     requestData : function(node, callback){
18315         if(this.fireEvent("beforeload", this, node, callback) !== false){
18316             this.transId = Roo.Ajax.request({
18317                 method:this.requestMethod,
18318                 url: this.dataUrl||this.url,
18319                 success: this.handleResponse,
18320                 failure: this.handleFailure,
18321                 scope: this,
18322                 argument: {callback: callback, node: node},
18323                 params: this.getParams(node)
18324             });
18325         }else{
18326             // if the load is cancelled, make sure we notify
18327             // the node that we are done
18328             if(typeof callback == "function"){
18329                 callback();
18330             }
18331         }
18332     },
18333
18334     isLoading : function(){
18335         return this.transId ? true : false;
18336     },
18337
18338     abort : function(){
18339         if(this.isLoading()){
18340             Roo.Ajax.abort(this.transId);
18341         }
18342     },
18343
18344     // private
18345     createNode : function(attr){
18346         // apply baseAttrs, nice idea Corey!
18347         if(this.baseAttrs){
18348             Roo.applyIf(attr, this.baseAttrs);
18349         }
18350         if(this.applyLoader !== false){
18351             attr.loader = this;
18352         }
18353         // uiProvider = depreciated..
18354         
18355         if(typeof(attr.uiProvider) == 'string'){
18356            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18357                 /**  eval:var:attr */ eval(attr.uiProvider);
18358         }
18359         if(typeof(this.uiProviders['default']) != 'undefined') {
18360             attr.uiProvider = this.uiProviders['default'];
18361         }
18362         
18363         this.fireEvent('create', this, attr);
18364         
18365         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18366         return(attr.leaf ?
18367                         new Roo.tree.TreeNode(attr) :
18368                         new Roo.tree.AsyncTreeNode(attr));
18369     },
18370
18371     processResponse : function(response, node, callback){
18372         var json = response.responseText;
18373         try {
18374             
18375             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18376             if (this.root !== false) {
18377                 o = o[this.root];
18378             }
18379             
18380             for(var i = 0, len = o.length; i < len; i++){
18381                 var n = this.createNode(o[i]);
18382                 if(n){
18383                     node.appendChild(n);
18384                 }
18385             }
18386             if(typeof callback == "function"){
18387                 callback(this, node);
18388             }
18389         }catch(e){
18390             this.handleFailure(response);
18391         }
18392     },
18393
18394     handleResponse : function(response){
18395         this.transId = false;
18396         var a = response.argument;
18397         this.processResponse(response, a.node, a.callback);
18398         this.fireEvent("load", this, a.node, response);
18399     },
18400
18401     handleFailure : function(response){
18402         this.transId = false;
18403         var a = response.argument;
18404         this.fireEvent("loadexception", this, a.node, response);
18405         if(typeof a.callback == "function"){
18406             a.callback(this, a.node);
18407         }
18408     }
18409 });/*
18410  * Based on:
18411  * Ext JS Library 1.1.1
18412  * Copyright(c) 2006-2007, Ext JS, LLC.
18413  *
18414  * Originally Released Under LGPL - original licence link has changed is not relivant.
18415  *
18416  * Fork - LGPL
18417  * <script type="text/javascript">
18418  */
18419
18420 /**
18421 * @class Roo.tree.TreeFilter
18422 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18423 * @param {TreePanel} tree
18424 * @param {Object} config (optional)
18425  */
18426 Roo.tree.TreeFilter = function(tree, config){
18427     this.tree = tree;
18428     this.filtered = {};
18429     Roo.apply(this, config);
18430 };
18431
18432 Roo.tree.TreeFilter.prototype = {
18433     clearBlank:false,
18434     reverse:false,
18435     autoClear:false,
18436     remove:false,
18437
18438      /**
18439      * Filter the data by a specific attribute.
18440      * @param {String/RegExp} value Either string that the attribute value
18441      * should start with or a RegExp to test against the attribute
18442      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18443      * @param {TreeNode} startNode (optional) The node to start the filter at.
18444      */
18445     filter : function(value, attr, startNode){
18446         attr = attr || "text";
18447         var f;
18448         if(typeof value == "string"){
18449             var vlen = value.length;
18450             // auto clear empty filter
18451             if(vlen == 0 && this.clearBlank){
18452                 this.clear();
18453                 return;
18454             }
18455             value = value.toLowerCase();
18456             f = function(n){
18457                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18458             };
18459         }else if(value.exec){ // regex?
18460             f = function(n){
18461                 return value.test(n.attributes[attr]);
18462             };
18463         }else{
18464             throw 'Illegal filter type, must be string or regex';
18465         }
18466         this.filterBy(f, null, startNode);
18467         },
18468
18469     /**
18470      * Filter by a function. The passed function will be called with each
18471      * node in the tree (or from the startNode). If the function returns true, the node is kept
18472      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18473      * @param {Function} fn The filter function
18474      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18475      */
18476     filterBy : function(fn, scope, startNode){
18477         startNode = startNode || this.tree.root;
18478         if(this.autoClear){
18479             this.clear();
18480         }
18481         var af = this.filtered, rv = this.reverse;
18482         var f = function(n){
18483             if(n == startNode){
18484                 return true;
18485             }
18486             if(af[n.id]){
18487                 return false;
18488             }
18489             var m = fn.call(scope || n, n);
18490             if(!m || rv){
18491                 af[n.id] = n;
18492                 n.ui.hide();
18493                 return false;
18494             }
18495             return true;
18496         };
18497         startNode.cascade(f);
18498         if(this.remove){
18499            for(var id in af){
18500                if(typeof id != "function"){
18501                    var n = af[id];
18502                    if(n && n.parentNode){
18503                        n.parentNode.removeChild(n);
18504                    }
18505                }
18506            }
18507         }
18508     },
18509
18510     /**
18511      * Clears the current filter. Note: with the "remove" option
18512      * set a filter cannot be cleared.
18513      */
18514     clear : function(){
18515         var t = this.tree;
18516         var af = this.filtered;
18517         for(var id in af){
18518             if(typeof id != "function"){
18519                 var n = af[id];
18520                 if(n){
18521                     n.ui.show();
18522                 }
18523             }
18524         }
18525         this.filtered = {};
18526     }
18527 };
18528 /*
18529  * Based on:
18530  * Ext JS Library 1.1.1
18531  * Copyright(c) 2006-2007, Ext JS, LLC.
18532  *
18533  * Originally Released Under LGPL - original licence link has changed is not relivant.
18534  *
18535  * Fork - LGPL
18536  * <script type="text/javascript">
18537  */
18538  
18539
18540 /**
18541  * @class Roo.tree.TreeSorter
18542  * Provides sorting of nodes in a TreePanel
18543  * 
18544  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18545  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18546  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18547  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18548  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18549  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18550  * @constructor
18551  * @param {TreePanel} tree
18552  * @param {Object} config
18553  */
18554 Roo.tree.TreeSorter = function(tree, config){
18555     Roo.apply(this, config);
18556     tree.on("beforechildrenrendered", this.doSort, this);
18557     tree.on("append", this.updateSort, this);
18558     tree.on("insert", this.updateSort, this);
18559     
18560     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18561     var p = this.property || "text";
18562     var sortType = this.sortType;
18563     var fs = this.folderSort;
18564     var cs = this.caseSensitive === true;
18565     var leafAttr = this.leafAttr || 'leaf';
18566
18567     this.sortFn = function(n1, n2){
18568         if(fs){
18569             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18570                 return 1;
18571             }
18572             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18573                 return -1;
18574             }
18575         }
18576         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18577         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18578         if(v1 < v2){
18579                         return dsc ? +1 : -1;
18580                 }else if(v1 > v2){
18581                         return dsc ? -1 : +1;
18582         }else{
18583                 return 0;
18584         }
18585     };
18586 };
18587
18588 Roo.tree.TreeSorter.prototype = {
18589     doSort : function(node){
18590         node.sort(this.sortFn);
18591     },
18592     
18593     compareNodes : function(n1, n2){
18594         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18595     },
18596     
18597     updateSort : function(tree, node){
18598         if(node.childrenRendered){
18599             this.doSort.defer(1, this, [node]);
18600         }
18601     }
18602 };/*
18603  * Based on:
18604  * Ext JS Library 1.1.1
18605  * Copyright(c) 2006-2007, Ext JS, LLC.
18606  *
18607  * Originally Released Under LGPL - original licence link has changed is not relivant.
18608  *
18609  * Fork - LGPL
18610  * <script type="text/javascript">
18611  */
18612
18613 if(Roo.dd.DropZone){
18614     
18615 Roo.tree.TreeDropZone = function(tree, config){
18616     this.allowParentInsert = false;
18617     this.allowContainerDrop = false;
18618     this.appendOnly = false;
18619     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18620     this.tree = tree;
18621     this.lastInsertClass = "x-tree-no-status";
18622     this.dragOverData = {};
18623 };
18624
18625 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18626     ddGroup : "TreeDD",
18627     
18628     expandDelay : 1000,
18629     
18630     expandNode : function(node){
18631         if(node.hasChildNodes() && !node.isExpanded()){
18632             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18633         }
18634     },
18635     
18636     queueExpand : function(node){
18637         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18638     },
18639     
18640     cancelExpand : function(){
18641         if(this.expandProcId){
18642             clearTimeout(this.expandProcId);
18643             this.expandProcId = false;
18644         }
18645     },
18646     
18647     isValidDropPoint : function(n, pt, dd, e, data){
18648         if(!n || !data){ return false; }
18649         var targetNode = n.node;
18650         var dropNode = data.node;
18651         // default drop rules
18652         if(!(targetNode && targetNode.isTarget && pt)){
18653             return false;
18654         }
18655         if(pt == "append" && targetNode.allowChildren === false){
18656             return false;
18657         }
18658         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18659             return false;
18660         }
18661         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18662             return false;
18663         }
18664         // reuse the object
18665         var overEvent = this.dragOverData;
18666         overEvent.tree = this.tree;
18667         overEvent.target = targetNode;
18668         overEvent.data = data;
18669         overEvent.point = pt;
18670         overEvent.source = dd;
18671         overEvent.rawEvent = e;
18672         overEvent.dropNode = dropNode;
18673         overEvent.cancel = false;  
18674         var result = this.tree.fireEvent("nodedragover", overEvent);
18675         return overEvent.cancel === false && result !== false;
18676     },
18677     
18678     getDropPoint : function(e, n, dd){
18679         var tn = n.node;
18680         if(tn.isRoot){
18681             return tn.allowChildren !== false ? "append" : false; // always append for root
18682         }
18683         var dragEl = n.ddel;
18684         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18685         var y = Roo.lib.Event.getPageY(e);
18686         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18687         
18688         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18689         var noAppend = tn.allowChildren === false;
18690         if(this.appendOnly || tn.parentNode.allowChildren === false){
18691             return noAppend ? false : "append";
18692         }
18693         var noBelow = false;
18694         if(!this.allowParentInsert){
18695             noBelow = tn.hasChildNodes() && tn.isExpanded();
18696         }
18697         var q = (b - t) / (noAppend ? 2 : 3);
18698         if(y >= t && y < (t + q)){
18699             return "above";
18700         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18701             return "below";
18702         }else{
18703             return "append";
18704         }
18705     },
18706     
18707     onNodeEnter : function(n, dd, e, data){
18708         this.cancelExpand();
18709     },
18710     
18711     onNodeOver : function(n, dd, e, data){
18712         var pt = this.getDropPoint(e, n, dd);
18713         var node = n.node;
18714         
18715         // auto node expand check
18716         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18717             this.queueExpand(node);
18718         }else if(pt != "append"){
18719             this.cancelExpand();
18720         }
18721         
18722         // set the insert point style on the target node
18723         var returnCls = this.dropNotAllowed;
18724         if(this.isValidDropPoint(n, pt, dd, e, data)){
18725            if(pt){
18726                var el = n.ddel;
18727                var cls;
18728                if(pt == "above"){
18729                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18730                    cls = "x-tree-drag-insert-above";
18731                }else if(pt == "below"){
18732                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18733                    cls = "x-tree-drag-insert-below";
18734                }else{
18735                    returnCls = "x-tree-drop-ok-append";
18736                    cls = "x-tree-drag-append";
18737                }
18738                if(this.lastInsertClass != cls){
18739                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18740                    this.lastInsertClass = cls;
18741                }
18742            }
18743        }
18744        return returnCls;
18745     },
18746     
18747     onNodeOut : function(n, dd, e, data){
18748         this.cancelExpand();
18749         this.removeDropIndicators(n);
18750     },
18751     
18752     onNodeDrop : function(n, dd, e, data){
18753         var point = this.getDropPoint(e, n, dd);
18754         var targetNode = n.node;
18755         targetNode.ui.startDrop();
18756         if(!this.isValidDropPoint(n, point, dd, e, data)){
18757             targetNode.ui.endDrop();
18758             return false;
18759         }
18760         // first try to find the drop node
18761         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18762         var dropEvent = {
18763             tree : this.tree,
18764             target: targetNode,
18765             data: data,
18766             point: point,
18767             source: dd,
18768             rawEvent: e,
18769             dropNode: dropNode,
18770             cancel: !dropNode   
18771         };
18772         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18773         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18774             targetNode.ui.endDrop();
18775             return false;
18776         }
18777         // allow target changing
18778         targetNode = dropEvent.target;
18779         if(point == "append" && !targetNode.isExpanded()){
18780             targetNode.expand(false, null, function(){
18781                 this.completeDrop(dropEvent);
18782             }.createDelegate(this));
18783         }else{
18784             this.completeDrop(dropEvent);
18785         }
18786         return true;
18787     },
18788     
18789     completeDrop : function(de){
18790         var ns = de.dropNode, p = de.point, t = de.target;
18791         if(!(ns instanceof Array)){
18792             ns = [ns];
18793         }
18794         var n;
18795         for(var i = 0, len = ns.length; i < len; i++){
18796             n = ns[i];
18797             if(p == "above"){
18798                 t.parentNode.insertBefore(n, t);
18799             }else if(p == "below"){
18800                 t.parentNode.insertBefore(n, t.nextSibling);
18801             }else{
18802                 t.appendChild(n);
18803             }
18804         }
18805         n.ui.focus();
18806         if(this.tree.hlDrop){
18807             n.ui.highlight();
18808         }
18809         t.ui.endDrop();
18810         this.tree.fireEvent("nodedrop", de);
18811     },
18812     
18813     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18814         if(this.tree.hlDrop){
18815             dropNode.ui.focus();
18816             dropNode.ui.highlight();
18817         }
18818         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18819     },
18820     
18821     getTree : function(){
18822         return this.tree;
18823     },
18824     
18825     removeDropIndicators : function(n){
18826         if(n && n.ddel){
18827             var el = n.ddel;
18828             Roo.fly(el).removeClass([
18829                     "x-tree-drag-insert-above",
18830                     "x-tree-drag-insert-below",
18831                     "x-tree-drag-append"]);
18832             this.lastInsertClass = "_noclass";
18833         }
18834     },
18835     
18836     beforeDragDrop : function(target, e, id){
18837         this.cancelExpand();
18838         return true;
18839     },
18840     
18841     afterRepair : function(data){
18842         if(data && Roo.enableFx){
18843             data.node.ui.highlight();
18844         }
18845         this.hideProxy();
18846     }    
18847 });
18848
18849 }
18850 /*
18851  * Based on:
18852  * Ext JS Library 1.1.1
18853  * Copyright(c) 2006-2007, Ext JS, LLC.
18854  *
18855  * Originally Released Under LGPL - original licence link has changed is not relivant.
18856  *
18857  * Fork - LGPL
18858  * <script type="text/javascript">
18859  */
18860  
18861
18862 if(Roo.dd.DragZone){
18863 Roo.tree.TreeDragZone = function(tree, config){
18864     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18865     this.tree = tree;
18866 };
18867
18868 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18869     ddGroup : "TreeDD",
18870     
18871     onBeforeDrag : function(data, e){
18872         var n = data.node;
18873         return n && n.draggable && !n.disabled;
18874     },
18875     
18876     onInitDrag : function(e){
18877         var data = this.dragData;
18878         this.tree.getSelectionModel().select(data.node);
18879         this.proxy.update("");
18880         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18881         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18882     },
18883     
18884     getRepairXY : function(e, data){
18885         return data.node.ui.getDDRepairXY();
18886     },
18887     
18888     onEndDrag : function(data, e){
18889         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18890     },
18891     
18892     onValidDrop : function(dd, e, id){
18893         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18894         this.hideProxy();
18895     },
18896     
18897     beforeInvalidDrop : function(e, id){
18898         // this scrolls the original position back into view
18899         var sm = this.tree.getSelectionModel();
18900         sm.clearSelections();
18901         sm.select(this.dragData.node);
18902     }
18903 });
18904 }/*
18905  * Based on:
18906  * Ext JS Library 1.1.1
18907  * Copyright(c) 2006-2007, Ext JS, LLC.
18908  *
18909  * Originally Released Under LGPL - original licence link has changed is not relivant.
18910  *
18911  * Fork - LGPL
18912  * <script type="text/javascript">
18913  */
18914 /**
18915  * @class Roo.tree.TreeEditor
18916  * @extends Roo.Editor
18917  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18918  * as the editor field.
18919  * @constructor
18920  * @param {TreePanel} tree
18921  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18922  */
18923 Roo.tree.TreeEditor = function(tree, config){
18924     config = config || {};
18925     var field = config.events ? config : new Roo.form.TextField(config);
18926     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18927
18928     this.tree = tree;
18929
18930     tree.on('beforeclick', this.beforeNodeClick, this);
18931     tree.getTreeEl().on('mousedown', this.hide, this);
18932     this.on('complete', this.updateNode, this);
18933     this.on('beforestartedit', this.fitToTree, this);
18934     this.on('startedit', this.bindScroll, this, {delay:10});
18935     this.on('specialkey', this.onSpecialKey, this);
18936 };
18937
18938 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18939     /**
18940      * @cfg {String} alignment
18941      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18942      */
18943     alignment: "l-l",
18944     // inherit
18945     autoSize: false,
18946     /**
18947      * @cfg {Boolean} hideEl
18948      * True to hide the bound element while the editor is displayed (defaults to false)
18949      */
18950     hideEl : false,
18951     /**
18952      * @cfg {String} cls
18953      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18954      */
18955     cls: "x-small-editor x-tree-editor",
18956     /**
18957      * @cfg {Boolean} shim
18958      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18959      */
18960     shim:false,
18961     // inherit
18962     shadow:"frame",
18963     /**
18964      * @cfg {Number} maxWidth
18965      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18966      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18967      * scroll and client offsets into account prior to each edit.
18968      */
18969     maxWidth: 250,
18970
18971     editDelay : 350,
18972
18973     // private
18974     fitToTree : function(ed, el){
18975         var td = this.tree.getTreeEl().dom, nd = el.dom;
18976         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18977             td.scrollLeft = nd.offsetLeft;
18978         }
18979         var w = Math.min(
18980                 this.maxWidth,
18981                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18982         this.setSize(w, '');
18983     },
18984
18985     // private
18986     triggerEdit : function(node){
18987         this.completeEdit();
18988         this.editNode = node;
18989         this.startEdit(node.ui.textNode, node.text);
18990     },
18991
18992     // private
18993     bindScroll : function(){
18994         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
18995     },
18996
18997     // private
18998     beforeNodeClick : function(node, e){
18999         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19000         this.lastClick = new Date();
19001         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19002             e.stopEvent();
19003             this.triggerEdit(node);
19004             return false;
19005         }
19006     },
19007
19008     // private
19009     updateNode : function(ed, value){
19010         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19011         this.editNode.setText(value);
19012     },
19013
19014     // private
19015     onHide : function(){
19016         Roo.tree.TreeEditor.superclass.onHide.call(this);
19017         if(this.editNode){
19018             this.editNode.ui.focus();
19019         }
19020     },
19021
19022     // private
19023     onSpecialKey : function(field, e){
19024         var k = e.getKey();
19025         if(k == e.ESC){
19026             e.stopEvent();
19027             this.cancelEdit();
19028         }else if(k == e.ENTER && !e.hasModifier()){
19029             e.stopEvent();
19030             this.completeEdit();
19031         }
19032     }
19033 });//<Script type="text/javascript">
19034 /*
19035  * Based on:
19036  * Ext JS Library 1.1.1
19037  * Copyright(c) 2006-2007, Ext JS, LLC.
19038  *
19039  * Originally Released Under LGPL - original licence link has changed is not relivant.
19040  *
19041  * Fork - LGPL
19042  * <script type="text/javascript">
19043  */
19044  
19045 /**
19046  * Not documented??? - probably should be...
19047  */
19048
19049 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19050     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19051     
19052     renderElements : function(n, a, targetNode, bulkRender){
19053         //consel.log("renderElements?");
19054         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19055
19056         var t = n.getOwnerTree();
19057         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19058         
19059         var cols = t.columns;
19060         var bw = t.borderWidth;
19061         var c = cols[0];
19062         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19063          var cb = typeof a.checked == "boolean";
19064         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19065         var colcls = 'x-t-' + tid + '-c0';
19066         var buf = [
19067             '<li class="x-tree-node">',
19068             
19069                 
19070                 '<div class="x-tree-node-el ', a.cls,'">',
19071                     // extran...
19072                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19073                 
19074                 
19075                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19076                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19077                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19078                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19079                            (a.iconCls ? ' '+a.iconCls : ''),
19080                            '" unselectable="on" />',
19081                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19082                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19083                              
19084                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19085                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19086                             '<span unselectable="on" qtip="' + tx + '">',
19087                              tx,
19088                              '</span></a>' ,
19089                     '</div>',
19090                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19091                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19092                  ];
19093         for(var i = 1, len = cols.length; i < len; i++){
19094             c = cols[i];
19095             colcls = 'x-t-' + tid + '-c' +i;
19096             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19097             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19098                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19099                       "</div>");
19100          }
19101          
19102          buf.push(
19103             '</a>',
19104             '<div class="x-clear"></div></div>',
19105             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19106             "</li>");
19107         
19108         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19109             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19110                                 n.nextSibling.ui.getEl(), buf.join(""));
19111         }else{
19112             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19113         }
19114         var el = this.wrap.firstChild;
19115         this.elRow = el;
19116         this.elNode = el.firstChild;
19117         this.ranchor = el.childNodes[1];
19118         this.ctNode = this.wrap.childNodes[1];
19119         var cs = el.firstChild.childNodes;
19120         this.indentNode = cs[0];
19121         this.ecNode = cs[1];
19122         this.iconNode = cs[2];
19123         var index = 3;
19124         if(cb){
19125             this.checkbox = cs[3];
19126             index++;
19127         }
19128         this.anchor = cs[index];
19129         
19130         this.textNode = cs[index].firstChild;
19131         
19132         //el.on("click", this.onClick, this);
19133         //el.on("dblclick", this.onDblClick, this);
19134         
19135         
19136        // console.log(this);
19137     },
19138     initEvents : function(){
19139         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19140         
19141             
19142         var a = this.ranchor;
19143
19144         var el = Roo.get(a);
19145
19146         if(Roo.isOpera){ // opera render bug ignores the CSS
19147             el.setStyle("text-decoration", "none");
19148         }
19149
19150         el.on("click", this.onClick, this);
19151         el.on("dblclick", this.onDblClick, this);
19152         el.on("contextmenu", this.onContextMenu, this);
19153         
19154     },
19155     
19156     /*onSelectedChange : function(state){
19157         if(state){
19158             this.focus();
19159             this.addClass("x-tree-selected");
19160         }else{
19161             //this.blur();
19162             this.removeClass("x-tree-selected");
19163         }
19164     },*/
19165     addClass : function(cls){
19166         if(this.elRow){
19167             Roo.fly(this.elRow).addClass(cls);
19168         }
19169         
19170     },
19171     
19172     
19173     removeClass : function(cls){
19174         if(this.elRow){
19175             Roo.fly(this.elRow).removeClass(cls);
19176         }
19177     }
19178
19179     
19180     
19181 });//<Script type="text/javascript">
19182
19183 /*
19184  * Based on:
19185  * Ext JS Library 1.1.1
19186  * Copyright(c) 2006-2007, Ext JS, LLC.
19187  *
19188  * Originally Released Under LGPL - original licence link has changed is not relivant.
19189  *
19190  * Fork - LGPL
19191  * <script type="text/javascript">
19192  */
19193  
19194
19195 /**
19196  * @class Roo.tree.ColumnTree
19197  * @extends Roo.data.TreePanel
19198  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19199  * @cfg {int} borderWidth  compined right/left border allowance
19200  * @constructor
19201  * @param {String/HTMLElement/Element} el The container element
19202  * @param {Object} config
19203  */
19204 Roo.tree.ColumnTree =  function(el, config)
19205 {
19206    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19207    this.addEvents({
19208         /**
19209         * @event resize
19210         * Fire this event on a container when it resizes
19211         * @param {int} w Width
19212         * @param {int} h Height
19213         */
19214        "resize" : true
19215     });
19216     this.on('resize', this.onResize, this);
19217 };
19218
19219 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19220     //lines:false,
19221     
19222     
19223     borderWidth: Roo.isBorderBox ? 0 : 2, 
19224     headEls : false,
19225     
19226     render : function(){
19227         // add the header.....
19228        
19229         Roo.tree.ColumnTree.superclass.render.apply(this);
19230         
19231         this.el.addClass('x-column-tree');
19232         
19233         this.headers = this.el.createChild(
19234             {cls:'x-tree-headers'},this.innerCt.dom);
19235    
19236         var cols = this.columns, c;
19237         var totalWidth = 0;
19238         this.headEls = [];
19239         var  len = cols.length;
19240         for(var i = 0; i < len; i++){
19241              c = cols[i];
19242              totalWidth += c.width;
19243             this.headEls.push(this.headers.createChild({
19244                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19245                  cn: {
19246                      cls:'x-tree-hd-text',
19247                      html: c.header
19248                  },
19249                  style:'width:'+(c.width-this.borderWidth)+'px;'
19250              }));
19251         }
19252         this.headers.createChild({cls:'x-clear'});
19253         // prevent floats from wrapping when clipped
19254         this.headers.setWidth(totalWidth);
19255         //this.innerCt.setWidth(totalWidth);
19256         this.innerCt.setStyle({ overflow: 'auto' });
19257         this.onResize(this.width, this.height);
19258              
19259         
19260     },
19261     onResize : function(w,h)
19262     {
19263         this.height = h;
19264         this.width = w;
19265         // resize cols..
19266         this.innerCt.setWidth(this.width);
19267         this.innerCt.setHeight(this.height-20);
19268         
19269         // headers...
19270         var cols = this.columns, c;
19271         var totalWidth = 0;
19272         var expEl = false;
19273         var len = cols.length;
19274         for(var i = 0; i < len; i++){
19275             c = cols[i];
19276             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19277                 // it's the expander..
19278                 expEl  = this.headEls[i];
19279                 continue;
19280             }
19281             totalWidth += c.width;
19282             
19283         }
19284         if (expEl) {
19285             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19286         }
19287         this.headers.setWidth(w-20);
19288
19289         
19290         
19291         
19292     }
19293 });
19294 /*
19295  * Based on:
19296  * Ext JS Library 1.1.1
19297  * Copyright(c) 2006-2007, Ext JS, LLC.
19298  *
19299  * Originally Released Under LGPL - original licence link has changed is not relivant.
19300  *
19301  * Fork - LGPL
19302  * <script type="text/javascript">
19303  */
19304  
19305 /**
19306  * @class Roo.menu.Menu
19307  * @extends Roo.util.Observable
19308  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19309  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19310  * @constructor
19311  * Creates a new Menu
19312  * @param {Object} config Configuration options
19313  */
19314 Roo.menu.Menu = function(config){
19315     Roo.apply(this, config);
19316     this.id = this.id || Roo.id();
19317     this.addEvents({
19318         /**
19319          * @event beforeshow
19320          * Fires before this menu is displayed
19321          * @param {Roo.menu.Menu} this
19322          */
19323         beforeshow : true,
19324         /**
19325          * @event beforehide
19326          * Fires before this menu is hidden
19327          * @param {Roo.menu.Menu} this
19328          */
19329         beforehide : true,
19330         /**
19331          * @event show
19332          * Fires after this menu is displayed
19333          * @param {Roo.menu.Menu} this
19334          */
19335         show : true,
19336         /**
19337          * @event hide
19338          * Fires after this menu is hidden
19339          * @param {Roo.menu.Menu} this
19340          */
19341         hide : true,
19342         /**
19343          * @event click
19344          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19345          * @param {Roo.menu.Menu} this
19346          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19347          * @param {Roo.EventObject} e
19348          */
19349         click : true,
19350         /**
19351          * @event mouseover
19352          * Fires when the mouse is hovering over this menu
19353          * @param {Roo.menu.Menu} this
19354          * @param {Roo.EventObject} e
19355          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19356          */
19357         mouseover : true,
19358         /**
19359          * @event mouseout
19360          * Fires when the mouse exits this menu
19361          * @param {Roo.menu.Menu} this
19362          * @param {Roo.EventObject} e
19363          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19364          */
19365         mouseout : true,
19366         /**
19367          * @event itemclick
19368          * Fires when a menu item contained in this menu is clicked
19369          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19370          * @param {Roo.EventObject} e
19371          */
19372         itemclick: true
19373     });
19374     if (this.registerMenu) {
19375         Roo.menu.MenuMgr.register(this);
19376     }
19377     
19378     var mis = this.items;
19379     this.items = new Roo.util.MixedCollection();
19380     if(mis){
19381         this.add.apply(this, mis);
19382     }
19383 };
19384
19385 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19386     /**
19387      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19388      */
19389     minWidth : 120,
19390     /**
19391      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19392      * for bottom-right shadow (defaults to "sides")
19393      */
19394     shadow : "sides",
19395     /**
19396      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19397      * this menu (defaults to "tl-tr?")
19398      */
19399     subMenuAlign : "tl-tr?",
19400     /**
19401      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19402      * relative to its element of origin (defaults to "tl-bl?")
19403      */
19404     defaultAlign : "tl-bl?",
19405     /**
19406      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19407      */
19408     allowOtherMenus : false,
19409     /**
19410      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19411      */
19412     registerMenu : true,
19413
19414     hidden:true,
19415
19416     // private
19417     render : function(){
19418         if(this.el){
19419             return;
19420         }
19421         var el = this.el = new Roo.Layer({
19422             cls: "x-menu",
19423             shadow:this.shadow,
19424             constrain: false,
19425             parentEl: this.parentEl || document.body,
19426             zindex:15000
19427         });
19428
19429         this.keyNav = new Roo.menu.MenuNav(this);
19430
19431         if(this.plain){
19432             el.addClass("x-menu-plain");
19433         }
19434         if(this.cls){
19435             el.addClass(this.cls);
19436         }
19437         // generic focus element
19438         this.focusEl = el.createChild({
19439             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19440         });
19441         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19442         ul.on("click", this.onClick, this);
19443         ul.on("mouseover", this.onMouseOver, this);
19444         ul.on("mouseout", this.onMouseOut, this);
19445         this.items.each(function(item){
19446             var li = document.createElement("li");
19447             li.className = "x-menu-list-item";
19448             ul.dom.appendChild(li);
19449             item.render(li, this);
19450         }, this);
19451         this.ul = ul;
19452         this.autoWidth();
19453     },
19454
19455     // private
19456     autoWidth : function(){
19457         var el = this.el, ul = this.ul;
19458         if(!el){
19459             return;
19460         }
19461         var w = this.width;
19462         if(w){
19463             el.setWidth(w);
19464         }else if(Roo.isIE){
19465             el.setWidth(this.minWidth);
19466             var t = el.dom.offsetWidth; // force recalc
19467             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19468         }
19469     },
19470
19471     // private
19472     delayAutoWidth : function(){
19473         if(this.rendered){
19474             if(!this.awTask){
19475                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19476             }
19477             this.awTask.delay(20);
19478         }
19479     },
19480
19481     // private
19482     findTargetItem : function(e){
19483         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19484         if(t && t.menuItemId){
19485             return this.items.get(t.menuItemId);
19486         }
19487     },
19488
19489     // private
19490     onClick : function(e){
19491         var t;
19492         if(t = this.findTargetItem(e)){
19493             t.onClick(e);
19494             this.fireEvent("click", this, t, e);
19495         }
19496     },
19497
19498     // private
19499     setActiveItem : function(item, autoExpand){
19500         if(item != this.activeItem){
19501             if(this.activeItem){
19502                 this.activeItem.deactivate();
19503             }
19504             this.activeItem = item;
19505             item.activate(autoExpand);
19506         }else if(autoExpand){
19507             item.expandMenu();
19508         }
19509     },
19510
19511     // private
19512     tryActivate : function(start, step){
19513         var items = this.items;
19514         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19515             var item = items.get(i);
19516             if(!item.disabled && item.canActivate){
19517                 this.setActiveItem(item, false);
19518                 return item;
19519             }
19520         }
19521         return false;
19522     },
19523
19524     // private
19525     onMouseOver : function(e){
19526         var t;
19527         if(t = this.findTargetItem(e)){
19528             if(t.canActivate && !t.disabled){
19529                 this.setActiveItem(t, true);
19530             }
19531         }
19532         this.fireEvent("mouseover", this, e, t);
19533     },
19534
19535     // private
19536     onMouseOut : function(e){
19537         var t;
19538         if(t = this.findTargetItem(e)){
19539             if(t == this.activeItem && t.shouldDeactivate(e)){
19540                 this.activeItem.deactivate();
19541                 delete this.activeItem;
19542             }
19543         }
19544         this.fireEvent("mouseout", this, e, t);
19545     },
19546
19547     /**
19548      * Read-only.  Returns true if the menu is currently displayed, else false.
19549      * @type Boolean
19550      */
19551     isVisible : function(){
19552         return this.el && !this.hidden;
19553     },
19554
19555     /**
19556      * Displays this menu relative to another element
19557      * @param {String/HTMLElement/Roo.Element} element The element to align to
19558      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19559      * the element (defaults to this.defaultAlign)
19560      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19561      */
19562     show : function(el, pos, parentMenu){
19563         this.parentMenu = parentMenu;
19564         if(!this.el){
19565             this.render();
19566         }
19567         this.fireEvent("beforeshow", this);
19568         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19569     },
19570
19571     /**
19572      * Displays this menu at a specific xy position
19573      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19574      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19575      */
19576     showAt : function(xy, parentMenu, /* private: */_e){
19577         this.parentMenu = parentMenu;
19578         if(!this.el){
19579             this.render();
19580         }
19581         if(_e !== false){
19582             this.fireEvent("beforeshow", this);
19583             xy = this.el.adjustForConstraints(xy);
19584         }
19585         this.el.setXY(xy);
19586         this.el.show();
19587         this.hidden = false;
19588         this.focus();
19589         this.fireEvent("show", this);
19590     },
19591
19592     focus : function(){
19593         if(!this.hidden){
19594             this.doFocus.defer(50, this);
19595         }
19596     },
19597
19598     doFocus : function(){
19599         if(!this.hidden){
19600             this.focusEl.focus();
19601         }
19602     },
19603
19604     /**
19605      * Hides this menu and optionally all parent menus
19606      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19607      */
19608     hide : function(deep){
19609         if(this.el && this.isVisible()){
19610             this.fireEvent("beforehide", this);
19611             if(this.activeItem){
19612                 this.activeItem.deactivate();
19613                 this.activeItem = null;
19614             }
19615             this.el.hide();
19616             this.hidden = true;
19617             this.fireEvent("hide", this);
19618         }
19619         if(deep === true && this.parentMenu){
19620             this.parentMenu.hide(true);
19621         }
19622     },
19623
19624     /**
19625      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19626      * Any of the following are valid:
19627      * <ul>
19628      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19629      * <li>An HTMLElement object which will be converted to a menu item</li>
19630      * <li>A menu item config object that will be created as a new menu item</li>
19631      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19632      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19633      * </ul>
19634      * Usage:
19635      * <pre><code>
19636 // Create the menu
19637 var menu = new Roo.menu.Menu();
19638
19639 // Create a menu item to add by reference
19640 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19641
19642 // Add a bunch of items at once using different methods.
19643 // Only the last item added will be returned.
19644 var item = menu.add(
19645     menuItem,                // add existing item by ref
19646     'Dynamic Item',          // new TextItem
19647     '-',                     // new separator
19648     { text: 'Config Item' }  // new item by config
19649 );
19650 </code></pre>
19651      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19652      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19653      */
19654     add : function(){
19655         var a = arguments, l = a.length, item;
19656         for(var i = 0; i < l; i++){
19657             var el = a[i];
19658             if ((typeof(el) == "object") && el.xtype && el.xns) {
19659                 el = Roo.factory(el, Roo.menu);
19660             }
19661             
19662             if(el.render){ // some kind of Item
19663                 item = this.addItem(el);
19664             }else if(typeof el == "string"){ // string
19665                 if(el == "separator" || el == "-"){
19666                     item = this.addSeparator();
19667                 }else{
19668                     item = this.addText(el);
19669                 }
19670             }else if(el.tagName || el.el){ // element
19671                 item = this.addElement(el);
19672             }else if(typeof el == "object"){ // must be menu item config?
19673                 item = this.addMenuItem(el);
19674             }
19675         }
19676         return item;
19677     },
19678
19679     /**
19680      * Returns this menu's underlying {@link Roo.Element} object
19681      * @return {Roo.Element} The element
19682      */
19683     getEl : function(){
19684         if(!this.el){
19685             this.render();
19686         }
19687         return this.el;
19688     },
19689
19690     /**
19691      * Adds a separator bar to the menu
19692      * @return {Roo.menu.Item} The menu item that was added
19693      */
19694     addSeparator : function(){
19695         return this.addItem(new Roo.menu.Separator());
19696     },
19697
19698     /**
19699      * Adds an {@link Roo.Element} object to the menu
19700      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19701      * @return {Roo.menu.Item} The menu item that was added
19702      */
19703     addElement : function(el){
19704         return this.addItem(new Roo.menu.BaseItem(el));
19705     },
19706
19707     /**
19708      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19709      * @param {Roo.menu.Item} item The menu item to add
19710      * @return {Roo.menu.Item} The menu item that was added
19711      */
19712     addItem : function(item){
19713         this.items.add(item);
19714         if(this.ul){
19715             var li = document.createElement("li");
19716             li.className = "x-menu-list-item";
19717             this.ul.dom.appendChild(li);
19718             item.render(li, this);
19719             this.delayAutoWidth();
19720         }
19721         return item;
19722     },
19723
19724     /**
19725      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19726      * @param {Object} config A MenuItem config object
19727      * @return {Roo.menu.Item} The menu item that was added
19728      */
19729     addMenuItem : function(config){
19730         if(!(config instanceof Roo.menu.Item)){
19731             if(typeof config.checked == "boolean"){ // must be check menu item config?
19732                 config = new Roo.menu.CheckItem(config);
19733             }else{
19734                 config = new Roo.menu.Item(config);
19735             }
19736         }
19737         return this.addItem(config);
19738     },
19739
19740     /**
19741      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19742      * @param {String} text The text to display in the menu item
19743      * @return {Roo.menu.Item} The menu item that was added
19744      */
19745     addText : function(text){
19746         return this.addItem(new Roo.menu.TextItem({ text : text }));
19747     },
19748
19749     /**
19750      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19751      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19752      * @param {Roo.menu.Item} item The menu item to add
19753      * @return {Roo.menu.Item} The menu item that was added
19754      */
19755     insert : function(index, item){
19756         this.items.insert(index, item);
19757         if(this.ul){
19758             var li = document.createElement("li");
19759             li.className = "x-menu-list-item";
19760             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19761             item.render(li, this);
19762             this.delayAutoWidth();
19763         }
19764         return item;
19765     },
19766
19767     /**
19768      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19769      * @param {Roo.menu.Item} item The menu item to remove
19770      */
19771     remove : function(item){
19772         this.items.removeKey(item.id);
19773         item.destroy();
19774     },
19775
19776     /**
19777      * Removes and destroys all items in the menu
19778      */
19779     removeAll : function(){
19780         var f;
19781         while(f = this.items.first()){
19782             this.remove(f);
19783         }
19784     }
19785 });
19786
19787 // MenuNav is a private utility class used internally by the Menu
19788 Roo.menu.MenuNav = function(menu){
19789     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19790     this.scope = this.menu = menu;
19791 };
19792
19793 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19794     doRelay : function(e, h){
19795         var k = e.getKey();
19796         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19797             this.menu.tryActivate(0, 1);
19798             return false;
19799         }
19800         return h.call(this.scope || this, e, this.menu);
19801     },
19802
19803     up : function(e, m){
19804         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19805             m.tryActivate(m.items.length-1, -1);
19806         }
19807     },
19808
19809     down : function(e, m){
19810         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19811             m.tryActivate(0, 1);
19812         }
19813     },
19814
19815     right : function(e, m){
19816         if(m.activeItem){
19817             m.activeItem.expandMenu(true);
19818         }
19819     },
19820
19821     left : function(e, m){
19822         m.hide();
19823         if(m.parentMenu && m.parentMenu.activeItem){
19824             m.parentMenu.activeItem.activate();
19825         }
19826     },
19827
19828     enter : function(e, m){
19829         if(m.activeItem){
19830             e.stopPropagation();
19831             m.activeItem.onClick(e);
19832             m.fireEvent("click", this, m.activeItem);
19833             return true;
19834         }
19835     }
19836 });/*
19837  * Based on:
19838  * Ext JS Library 1.1.1
19839  * Copyright(c) 2006-2007, Ext JS, LLC.
19840  *
19841  * Originally Released Under LGPL - original licence link has changed is not relivant.
19842  *
19843  * Fork - LGPL
19844  * <script type="text/javascript">
19845  */
19846  
19847 /**
19848  * @class Roo.menu.MenuMgr
19849  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19850  * @singleton
19851  */
19852 Roo.menu.MenuMgr = function(){
19853    var menus, active, groups = {}, attached = false, lastShow = new Date();
19854
19855    // private - called when first menu is created
19856    function init(){
19857        menus = {};
19858        active = new Roo.util.MixedCollection();
19859        Roo.get(document).addKeyListener(27, function(){
19860            if(active.length > 0){
19861                hideAll();
19862            }
19863        });
19864    }
19865
19866    // private
19867    function hideAll(){
19868        if(active && active.length > 0){
19869            var c = active.clone();
19870            c.each(function(m){
19871                m.hide();
19872            });
19873        }
19874    }
19875
19876    // private
19877    function onHide(m){
19878        active.remove(m);
19879        if(active.length < 1){
19880            Roo.get(document).un("mousedown", onMouseDown);
19881            attached = false;
19882        }
19883    }
19884
19885    // private
19886    function onShow(m){
19887        var last = active.last();
19888        lastShow = new Date();
19889        active.add(m);
19890        if(!attached){
19891            Roo.get(document).on("mousedown", onMouseDown);
19892            attached = true;
19893        }
19894        if(m.parentMenu){
19895           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19896           m.parentMenu.activeChild = m;
19897        }else if(last && last.isVisible()){
19898           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19899        }
19900    }
19901
19902    // private
19903    function onBeforeHide(m){
19904        if(m.activeChild){
19905            m.activeChild.hide();
19906        }
19907        if(m.autoHideTimer){
19908            clearTimeout(m.autoHideTimer);
19909            delete m.autoHideTimer;
19910        }
19911    }
19912
19913    // private
19914    function onBeforeShow(m){
19915        var pm = m.parentMenu;
19916        if(!pm && !m.allowOtherMenus){
19917            hideAll();
19918        }else if(pm && pm.activeChild && active != m){
19919            pm.activeChild.hide();
19920        }
19921    }
19922
19923    // private
19924    function onMouseDown(e){
19925        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19926            hideAll();
19927        }
19928    }
19929
19930    // private
19931    function onBeforeCheck(mi, state){
19932        if(state){
19933            var g = groups[mi.group];
19934            for(var i = 0, l = g.length; i < l; i++){
19935                if(g[i] != mi){
19936                    g[i].setChecked(false);
19937                }
19938            }
19939        }
19940    }
19941
19942    return {
19943
19944        /**
19945         * Hides all menus that are currently visible
19946         */
19947        hideAll : function(){
19948             hideAll();  
19949        },
19950
19951        // private
19952        register : function(menu){
19953            if(!menus){
19954                init();
19955            }
19956            menus[menu.id] = menu;
19957            menu.on("beforehide", onBeforeHide);
19958            menu.on("hide", onHide);
19959            menu.on("beforeshow", onBeforeShow);
19960            menu.on("show", onShow);
19961            var g = menu.group;
19962            if(g && menu.events["checkchange"]){
19963                if(!groups[g]){
19964                    groups[g] = [];
19965                }
19966                groups[g].push(menu);
19967                menu.on("checkchange", onCheck);
19968            }
19969        },
19970
19971         /**
19972          * Returns a {@link Roo.menu.Menu} object
19973          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19974          * be used to generate and return a new Menu instance.
19975          */
19976        get : function(menu){
19977            if(typeof menu == "string"){ // menu id
19978                return menus[menu];
19979            }else if(menu.events){  // menu instance
19980                return menu;
19981            }else if(typeof menu.length == 'number'){ // array of menu items?
19982                return new Roo.menu.Menu({items:menu});
19983            }else{ // otherwise, must be a config
19984                return new Roo.menu.Menu(menu);
19985            }
19986        },
19987
19988        // private
19989        unregister : function(menu){
19990            delete menus[menu.id];
19991            menu.un("beforehide", onBeforeHide);
19992            menu.un("hide", onHide);
19993            menu.un("beforeshow", onBeforeShow);
19994            menu.un("show", onShow);
19995            var g = menu.group;
19996            if(g && menu.events["checkchange"]){
19997                groups[g].remove(menu);
19998                menu.un("checkchange", onCheck);
19999            }
20000        },
20001
20002        // private
20003        registerCheckable : function(menuItem){
20004            var g = menuItem.group;
20005            if(g){
20006                if(!groups[g]){
20007                    groups[g] = [];
20008                }
20009                groups[g].push(menuItem);
20010                menuItem.on("beforecheckchange", onBeforeCheck);
20011            }
20012        },
20013
20014        // private
20015        unregisterCheckable : function(menuItem){
20016            var g = menuItem.group;
20017            if(g){
20018                groups[g].remove(menuItem);
20019                menuItem.un("beforecheckchange", onBeforeCheck);
20020            }
20021        }
20022    };
20023 }();/*
20024  * Based on:
20025  * Ext JS Library 1.1.1
20026  * Copyright(c) 2006-2007, Ext JS, LLC.
20027  *
20028  * Originally Released Under LGPL - original licence link has changed is not relivant.
20029  *
20030  * Fork - LGPL
20031  * <script type="text/javascript">
20032  */
20033  
20034
20035 /**
20036  * @class Roo.menu.BaseItem
20037  * @extends Roo.Component
20038  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20039  * management and base configuration options shared by all menu components.
20040  * @constructor
20041  * Creates a new BaseItem
20042  * @param {Object} config Configuration options
20043  */
20044 Roo.menu.BaseItem = function(config){
20045     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20046
20047     this.addEvents({
20048         /**
20049          * @event click
20050          * Fires when this item is clicked
20051          * @param {Roo.menu.BaseItem} this
20052          * @param {Roo.EventObject} e
20053          */
20054         click: true,
20055         /**
20056          * @event activate
20057          * Fires when this item is activated
20058          * @param {Roo.menu.BaseItem} this
20059          */
20060         activate : true,
20061         /**
20062          * @event deactivate
20063          * Fires when this item is deactivated
20064          * @param {Roo.menu.BaseItem} this
20065          */
20066         deactivate : true
20067     });
20068
20069     if(this.handler){
20070         this.on("click", this.handler, this.scope, true);
20071     }
20072 };
20073
20074 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20075     /**
20076      * @cfg {Function} handler
20077      * A function that will handle the click event of this menu item (defaults to undefined)
20078      */
20079     /**
20080      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20081      */
20082     canActivate : false,
20083     /**
20084      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20085      */
20086     activeClass : "x-menu-item-active",
20087     /**
20088      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20089      */
20090     hideOnClick : true,
20091     /**
20092      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20093      */
20094     hideDelay : 100,
20095
20096     // private
20097     ctype: "Roo.menu.BaseItem",
20098
20099     // private
20100     actionMode : "container",
20101
20102     // private
20103     render : function(container, parentMenu){
20104         this.parentMenu = parentMenu;
20105         Roo.menu.BaseItem.superclass.render.call(this, container);
20106         this.container.menuItemId = this.id;
20107     },
20108
20109     // private
20110     onRender : function(container, position){
20111         this.el = Roo.get(this.el);
20112         container.dom.appendChild(this.el.dom);
20113     },
20114
20115     // private
20116     onClick : function(e){
20117         if(!this.disabled && this.fireEvent("click", this, e) !== false
20118                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20119             this.handleClick(e);
20120         }else{
20121             e.stopEvent();
20122         }
20123     },
20124
20125     // private
20126     activate : function(){
20127         if(this.disabled){
20128             return false;
20129         }
20130         var li = this.container;
20131         li.addClass(this.activeClass);
20132         this.region = li.getRegion().adjust(2, 2, -2, -2);
20133         this.fireEvent("activate", this);
20134         return true;
20135     },
20136
20137     // private
20138     deactivate : function(){
20139         this.container.removeClass(this.activeClass);
20140         this.fireEvent("deactivate", this);
20141     },
20142
20143     // private
20144     shouldDeactivate : function(e){
20145         return !this.region || !this.region.contains(e.getPoint());
20146     },
20147
20148     // private
20149     handleClick : function(e){
20150         if(this.hideOnClick){
20151             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20152         }
20153     },
20154
20155     // private
20156     expandMenu : function(autoActivate){
20157         // do nothing
20158     },
20159
20160     // private
20161     hideMenu : function(){
20162         // do nothing
20163     }
20164 });/*
20165  * Based on:
20166  * Ext JS Library 1.1.1
20167  * Copyright(c) 2006-2007, Ext JS, LLC.
20168  *
20169  * Originally Released Under LGPL - original licence link has changed is not relivant.
20170  *
20171  * Fork - LGPL
20172  * <script type="text/javascript">
20173  */
20174  
20175 /**
20176  * @class Roo.menu.Adapter
20177  * @extends Roo.menu.BaseItem
20178  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
20179  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20180  * @constructor
20181  * Creates a new Adapter
20182  * @param {Object} config Configuration options
20183  */
20184 Roo.menu.Adapter = function(component, config){
20185     Roo.menu.Adapter.superclass.constructor.call(this, config);
20186     this.component = component;
20187 };
20188 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20189     // private
20190     canActivate : true,
20191
20192     // private
20193     onRender : function(container, position){
20194         this.component.render(container);
20195         this.el = this.component.getEl();
20196     },
20197
20198     // private
20199     activate : function(){
20200         if(this.disabled){
20201             return false;
20202         }
20203         this.component.focus();
20204         this.fireEvent("activate", this);
20205         return true;
20206     },
20207
20208     // private
20209     deactivate : function(){
20210         this.fireEvent("deactivate", this);
20211     },
20212
20213     // private
20214     disable : function(){
20215         this.component.disable();
20216         Roo.menu.Adapter.superclass.disable.call(this);
20217     },
20218
20219     // private
20220     enable : function(){
20221         this.component.enable();
20222         Roo.menu.Adapter.superclass.enable.call(this);
20223     }
20224 });/*
20225  * Based on:
20226  * Ext JS Library 1.1.1
20227  * Copyright(c) 2006-2007, Ext JS, LLC.
20228  *
20229  * Originally Released Under LGPL - original licence link has changed is not relivant.
20230  *
20231  * Fork - LGPL
20232  * <script type="text/javascript">
20233  */
20234
20235 /**
20236  * @class Roo.menu.TextItem
20237  * @extends Roo.menu.BaseItem
20238  * Adds a static text string to a menu, usually used as either a heading or group separator.
20239  * Note: old style constructor with text is still supported.
20240  * 
20241  * @constructor
20242  * Creates a new TextItem
20243  * @param {Object} cfg Configuration
20244  */
20245 Roo.menu.TextItem = function(cfg){
20246     if (typeof(cfg) == 'string') {
20247         this.text = cfg;
20248     } else {
20249         Roo.apply(this,cfg);
20250     }
20251     
20252     Roo.menu.TextItem.superclass.constructor.call(this);
20253 };
20254
20255 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20256     /**
20257      * @cfg {Boolean} text Text to show on item.
20258      */
20259     text : '',
20260     
20261     /**
20262      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20263      */
20264     hideOnClick : false,
20265     /**
20266      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20267      */
20268     itemCls : "x-menu-text",
20269
20270     // private
20271     onRender : function(){
20272         var s = document.createElement("span");
20273         s.className = this.itemCls;
20274         s.innerHTML = this.text;
20275         this.el = s;
20276         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20277     }
20278 });/*
20279  * Based on:
20280  * Ext JS Library 1.1.1
20281  * Copyright(c) 2006-2007, Ext JS, LLC.
20282  *
20283  * Originally Released Under LGPL - original licence link has changed is not relivant.
20284  *
20285  * Fork - LGPL
20286  * <script type="text/javascript">
20287  */
20288
20289 /**
20290  * @class Roo.menu.Separator
20291  * @extends Roo.menu.BaseItem
20292  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20293  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20294  * @constructor
20295  * @param {Object} config Configuration options
20296  */
20297 Roo.menu.Separator = function(config){
20298     Roo.menu.Separator.superclass.constructor.call(this, config);
20299 };
20300
20301 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20302     /**
20303      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20304      */
20305     itemCls : "x-menu-sep",
20306     /**
20307      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20308      */
20309     hideOnClick : false,
20310
20311     // private
20312     onRender : function(li){
20313         var s = document.createElement("span");
20314         s.className = this.itemCls;
20315         s.innerHTML = "&#160;";
20316         this.el = s;
20317         li.addClass("x-menu-sep-li");
20318         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20319     }
20320 });/*
20321  * Based on:
20322  * Ext JS Library 1.1.1
20323  * Copyright(c) 2006-2007, Ext JS, LLC.
20324  *
20325  * Originally Released Under LGPL - original licence link has changed is not relivant.
20326  *
20327  * Fork - LGPL
20328  * <script type="text/javascript">
20329  */
20330 /**
20331  * @class Roo.menu.Item
20332  * @extends Roo.menu.BaseItem
20333  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20334  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20335  * activation and click handling.
20336  * @constructor
20337  * Creates a new Item
20338  * @param {Object} config Configuration options
20339  */
20340 Roo.menu.Item = function(config){
20341     Roo.menu.Item.superclass.constructor.call(this, config);
20342     if(this.menu){
20343         this.menu = Roo.menu.MenuMgr.get(this.menu);
20344     }
20345 };
20346 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20347     
20348     /**
20349      * @cfg {String} text
20350      * The text to show on the menu item.
20351      */
20352     text: '',
20353      /**
20354      * @cfg {String} HTML to render in menu
20355      * The text to show on the menu item (HTML version).
20356      */
20357     html: '',
20358     /**
20359      * @cfg {String} icon
20360      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20361      */
20362     icon: undefined,
20363     /**
20364      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20365      */
20366     itemCls : "x-menu-item",
20367     /**
20368      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20369      */
20370     canActivate : true,
20371     /**
20372      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20373      */
20374     showDelay: 200,
20375     // doc'd in BaseItem
20376     hideDelay: 200,
20377
20378     // private
20379     ctype: "Roo.menu.Item",
20380     
20381     // private
20382     onRender : function(container, position){
20383         var el = document.createElement("a");
20384         el.hideFocus = true;
20385         el.unselectable = "on";
20386         el.href = this.href || "#";
20387         if(this.hrefTarget){
20388             el.target = this.hrefTarget;
20389         }
20390         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20391         
20392         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20393         
20394         el.innerHTML = String.format(
20395                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20396                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20397         this.el = el;
20398         Roo.menu.Item.superclass.onRender.call(this, container, position);
20399     },
20400
20401     /**
20402      * Sets the text to display in this menu item
20403      * @param {String} text The text to display
20404      * @param {Boolean} isHTML true to indicate text is pure html.
20405      */
20406     setText : function(text, isHTML){
20407         if (isHTML) {
20408             this.html = text;
20409         } else {
20410             this.text = text;
20411             this.html = '';
20412         }
20413         if(this.rendered){
20414             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20415      
20416             this.el.update(String.format(
20417                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20418                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20419             this.parentMenu.autoWidth();
20420         }
20421     },
20422
20423     // private
20424     handleClick : function(e){
20425         if(!this.href){ // if no link defined, stop the event automatically
20426             e.stopEvent();
20427         }
20428         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20429     },
20430
20431     // private
20432     activate : function(autoExpand){
20433         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20434             this.focus();
20435             if(autoExpand){
20436                 this.expandMenu();
20437             }
20438         }
20439         return true;
20440     },
20441
20442     // private
20443     shouldDeactivate : function(e){
20444         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20445             if(this.menu && this.menu.isVisible()){
20446                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20447             }
20448             return true;
20449         }
20450         return false;
20451     },
20452
20453     // private
20454     deactivate : function(){
20455         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20456         this.hideMenu();
20457     },
20458
20459     // private
20460     expandMenu : function(autoActivate){
20461         if(!this.disabled && this.menu){
20462             clearTimeout(this.hideTimer);
20463             delete this.hideTimer;
20464             if(!this.menu.isVisible() && !this.showTimer){
20465                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20466             }else if (this.menu.isVisible() && autoActivate){
20467                 this.menu.tryActivate(0, 1);
20468             }
20469         }
20470     },
20471
20472     // private
20473     deferExpand : function(autoActivate){
20474         delete this.showTimer;
20475         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20476         if(autoActivate){
20477             this.menu.tryActivate(0, 1);
20478         }
20479     },
20480
20481     // private
20482     hideMenu : function(){
20483         clearTimeout(this.showTimer);
20484         delete this.showTimer;
20485         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20486             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20487         }
20488     },
20489
20490     // private
20491     deferHide : function(){
20492         delete this.hideTimer;
20493         this.menu.hide();
20494     }
20495 });/*
20496  * Based on:
20497  * Ext JS Library 1.1.1
20498  * Copyright(c) 2006-2007, Ext JS, LLC.
20499  *
20500  * Originally Released Under LGPL - original licence link has changed is not relivant.
20501  *
20502  * Fork - LGPL
20503  * <script type="text/javascript">
20504  */
20505  
20506 /**
20507  * @class Roo.menu.CheckItem
20508  * @extends Roo.menu.Item
20509  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20510  * @constructor
20511  * Creates a new CheckItem
20512  * @param {Object} config Configuration options
20513  */
20514 Roo.menu.CheckItem = function(config){
20515     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20516     this.addEvents({
20517         /**
20518          * @event beforecheckchange
20519          * Fires before the checked value is set, providing an opportunity to cancel if needed
20520          * @param {Roo.menu.CheckItem} this
20521          * @param {Boolean} checked The new checked value that will be set
20522          */
20523         "beforecheckchange" : true,
20524         /**
20525          * @event checkchange
20526          * Fires after the checked value has been set
20527          * @param {Roo.menu.CheckItem} this
20528          * @param {Boolean} checked The checked value that was set
20529          */
20530         "checkchange" : true
20531     });
20532     if(this.checkHandler){
20533         this.on('checkchange', this.checkHandler, this.scope);
20534     }
20535 };
20536 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20537     /**
20538      * @cfg {String} group
20539      * All check items with the same group name will automatically be grouped into a single-select
20540      * radio button group (defaults to '')
20541      */
20542     /**
20543      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20544      */
20545     itemCls : "x-menu-item x-menu-check-item",
20546     /**
20547      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20548      */
20549     groupClass : "x-menu-group-item",
20550
20551     /**
20552      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20553      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20554      * initialized with checked = true will be rendered as checked.
20555      */
20556     checked: false,
20557
20558     // private
20559     ctype: "Roo.menu.CheckItem",
20560
20561     // private
20562     onRender : function(c){
20563         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20564         if(this.group){
20565             this.el.addClass(this.groupClass);
20566         }
20567         Roo.menu.MenuMgr.registerCheckable(this);
20568         if(this.checked){
20569             this.checked = false;
20570             this.setChecked(true, true);
20571         }
20572     },
20573
20574     // private
20575     destroy : function(){
20576         if(this.rendered){
20577             Roo.menu.MenuMgr.unregisterCheckable(this);
20578         }
20579         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20580     },
20581
20582     /**
20583      * Set the checked state of this item
20584      * @param {Boolean} checked The new checked value
20585      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20586      */
20587     setChecked : function(state, suppressEvent){
20588         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20589             if(this.container){
20590                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20591             }
20592             this.checked = state;
20593             if(suppressEvent !== true){
20594                 this.fireEvent("checkchange", this, state);
20595             }
20596         }
20597     },
20598
20599     // private
20600     handleClick : function(e){
20601        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20602            this.setChecked(!this.checked);
20603        }
20604        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20605     }
20606 });/*
20607  * Based on:
20608  * Ext JS Library 1.1.1
20609  * Copyright(c) 2006-2007, Ext JS, LLC.
20610  *
20611  * Originally Released Under LGPL - original licence link has changed is not relivant.
20612  *
20613  * Fork - LGPL
20614  * <script type="text/javascript">
20615  */
20616  
20617 /**
20618  * @class Roo.menu.DateItem
20619  * @extends Roo.menu.Adapter
20620  * A menu item that wraps the {@link Roo.DatPicker} component.
20621  * @constructor
20622  * Creates a new DateItem
20623  * @param {Object} config Configuration options
20624  */
20625 Roo.menu.DateItem = function(config){
20626     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20627     /** The Roo.DatePicker object @type Roo.DatePicker */
20628     this.picker = this.component;
20629     this.addEvents({select: true});
20630     
20631     this.picker.on("render", function(picker){
20632         picker.getEl().swallowEvent("click");
20633         picker.container.addClass("x-menu-date-item");
20634     });
20635
20636     this.picker.on("select", this.onSelect, this);
20637 };
20638
20639 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20640     // private
20641     onSelect : function(picker, date){
20642         this.fireEvent("select", this, date, picker);
20643         Roo.menu.DateItem.superclass.handleClick.call(this);
20644     }
20645 });/*
20646  * Based on:
20647  * Ext JS Library 1.1.1
20648  * Copyright(c) 2006-2007, Ext JS, LLC.
20649  *
20650  * Originally Released Under LGPL - original licence link has changed is not relivant.
20651  *
20652  * Fork - LGPL
20653  * <script type="text/javascript">
20654  */
20655  
20656 /**
20657  * @class Roo.menu.ColorItem
20658  * @extends Roo.menu.Adapter
20659  * A menu item that wraps the {@link Roo.ColorPalette} component.
20660  * @constructor
20661  * Creates a new ColorItem
20662  * @param {Object} config Configuration options
20663  */
20664 Roo.menu.ColorItem = function(config){
20665     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20666     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20667     this.palette = this.component;
20668     this.relayEvents(this.palette, ["select"]);
20669     if(this.selectHandler){
20670         this.on('select', this.selectHandler, this.scope);
20671     }
20672 };
20673 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20674  * Based on:
20675  * Ext JS Library 1.1.1
20676  * Copyright(c) 2006-2007, Ext JS, LLC.
20677  *
20678  * Originally Released Under LGPL - original licence link has changed is not relivant.
20679  *
20680  * Fork - LGPL
20681  * <script type="text/javascript">
20682  */
20683  
20684
20685 /**
20686  * @class Roo.menu.DateMenu
20687  * @extends Roo.menu.Menu
20688  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20689  * @constructor
20690  * Creates a new DateMenu
20691  * @param {Object} config Configuration options
20692  */
20693 Roo.menu.DateMenu = function(config){
20694     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20695     this.plain = true;
20696     var di = new Roo.menu.DateItem(config);
20697     this.add(di);
20698     /**
20699      * The {@link Roo.DatePicker} instance for this DateMenu
20700      * @type DatePicker
20701      */
20702     this.picker = di.picker;
20703     /**
20704      * @event select
20705      * @param {DatePicker} picker
20706      * @param {Date} date
20707      */
20708     this.relayEvents(di, ["select"]);
20709
20710     this.on('beforeshow', function(){
20711         if(this.picker){
20712             this.picker.hideMonthPicker(true);
20713         }
20714     }, this);
20715 };
20716 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20717     cls:'x-date-menu'
20718 });/*
20719  * Based on:
20720  * Ext JS Library 1.1.1
20721  * Copyright(c) 2006-2007, Ext JS, LLC.
20722  *
20723  * Originally Released Under LGPL - original licence link has changed is not relivant.
20724  *
20725  * Fork - LGPL
20726  * <script type="text/javascript">
20727  */
20728  
20729
20730 /**
20731  * @class Roo.menu.ColorMenu
20732  * @extends Roo.menu.Menu
20733  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20734  * @constructor
20735  * Creates a new ColorMenu
20736  * @param {Object} config Configuration options
20737  */
20738 Roo.menu.ColorMenu = function(config){
20739     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20740     this.plain = true;
20741     var ci = new Roo.menu.ColorItem(config);
20742     this.add(ci);
20743     /**
20744      * The {@link Roo.ColorPalette} instance for this ColorMenu
20745      * @type ColorPalette
20746      */
20747     this.palette = ci.palette;
20748     /**
20749      * @event select
20750      * @param {ColorPalette} palette
20751      * @param {String} color
20752      */
20753     this.relayEvents(ci, ["select"]);
20754 };
20755 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20756  * Based on:
20757  * Ext JS Library 1.1.1
20758  * Copyright(c) 2006-2007, Ext JS, LLC.
20759  *
20760  * Originally Released Under LGPL - original licence link has changed is not relivant.
20761  *
20762  * Fork - LGPL
20763  * <script type="text/javascript">
20764  */
20765  
20766 /**
20767  * @class Roo.form.Field
20768  * @extends Roo.BoxComponent
20769  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20770  * @constructor
20771  * Creates a new Field
20772  * @param {Object} config Configuration options
20773  */
20774 Roo.form.Field = function(config){
20775     Roo.form.Field.superclass.constructor.call(this, config);
20776 };
20777
20778 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20779     /**
20780      * @cfg {String} fieldLabel Label to use when rendering a form.
20781      */
20782        /**
20783      * @cfg {String} qtip Mouse over tip
20784      */
20785      
20786     /**
20787      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20788      */
20789     invalidClass : "x-form-invalid",
20790     /**
20791      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
20792      */
20793     invalidText : "The value in this field is invalid",
20794     /**
20795      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20796      */
20797     focusClass : "x-form-focus",
20798     /**
20799      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20800       automatic validation (defaults to "keyup").
20801      */
20802     validationEvent : "keyup",
20803     /**
20804      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20805      */
20806     validateOnBlur : true,
20807     /**
20808      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20809      */
20810     validationDelay : 250,
20811     /**
20812      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20813      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20814      */
20815     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20816     /**
20817      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20818      */
20819     fieldClass : "x-form-field",
20820     /**
20821      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20822      *<pre>
20823 Value         Description
20824 -----------   ----------------------------------------------------------------------
20825 qtip          Display a quick tip when the user hovers over the field
20826 title         Display a default browser title attribute popup
20827 under         Add a block div beneath the field containing the error text
20828 side          Add an error icon to the right of the field with a popup on hover
20829 [element id]  Add the error text directly to the innerHTML of the specified element
20830 </pre>
20831      */
20832     msgTarget : 'qtip',
20833     /**
20834      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20835      */
20836     msgFx : 'normal',
20837
20838     /**
20839      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
20840      */
20841     readOnly : false,
20842
20843     /**
20844      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20845      */
20846     disabled : false,
20847
20848     /**
20849      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20850      */
20851     inputType : undefined,
20852     
20853     /**
20854      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
20855          */
20856         tabIndex : undefined,
20857         
20858     // private
20859     isFormField : true,
20860
20861     // private
20862     hasFocus : false,
20863     /**
20864      * @property {Roo.Element} fieldEl
20865      * Element Containing the rendered Field (with label etc.)
20866      */
20867     /**
20868      * @cfg {Mixed} value A value to initialize this field with.
20869      */
20870     value : undefined,
20871
20872     /**
20873      * @cfg {String} name The field's HTML name attribute.
20874      */
20875     /**
20876      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20877      */
20878
20879         // private ??
20880         initComponent : function(){
20881         Roo.form.Field.superclass.initComponent.call(this);
20882         this.addEvents({
20883             /**
20884              * @event focus
20885              * Fires when this field receives input focus.
20886              * @param {Roo.form.Field} this
20887              */
20888             focus : true,
20889             /**
20890              * @event blur
20891              * Fires when this field loses input focus.
20892              * @param {Roo.form.Field} this
20893              */
20894             blur : true,
20895             /**
20896              * @event specialkey
20897              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20898              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20899              * @param {Roo.form.Field} this
20900              * @param {Roo.EventObject} e The event object
20901              */
20902             specialkey : true,
20903             /**
20904              * @event change
20905              * Fires just before the field blurs if the field value has changed.
20906              * @param {Roo.form.Field} this
20907              * @param {Mixed} newValue The new value
20908              * @param {Mixed} oldValue The original value
20909              */
20910             change : true,
20911             /**
20912              * @event invalid
20913              * Fires after the field has been marked as invalid.
20914              * @param {Roo.form.Field} this
20915              * @param {String} msg The validation message
20916              */
20917             invalid : true,
20918             /**
20919              * @event valid
20920              * Fires after the field has been validated with no errors.
20921              * @param {Roo.form.Field} this
20922              */
20923             valid : true,
20924              /**
20925              * @event keyup
20926              * Fires after the key up
20927              * @param {Roo.form.Field} this
20928              * @param {Roo.EventObject}  e The event Object
20929              */
20930             keyup : true
20931         });
20932     },
20933
20934     /**
20935      * Returns the name attribute of the field if available
20936      * @return {String} name The field name
20937      */
20938     getName: function(){
20939          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20940     },
20941
20942     // private
20943     onRender : function(ct, position){
20944         Roo.form.Field.superclass.onRender.call(this, ct, position);
20945         if(!this.el){
20946             var cfg = this.getAutoCreate();
20947             if(!cfg.name){
20948                 cfg.name = this.name || this.id;
20949             }
20950             if(this.inputType){
20951                 cfg.type = this.inputType;
20952             }
20953             this.el = ct.createChild(cfg, position);
20954         }
20955         var type = this.el.dom.type;
20956         if(type){
20957             if(type == 'password'){
20958                 type = 'text';
20959             }
20960             this.el.addClass('x-form-'+type);
20961         }
20962         if(this.readOnly){
20963             this.el.dom.readOnly = true;
20964         }
20965         if(this.tabIndex !== undefined){
20966             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20967         }
20968
20969         this.el.addClass([this.fieldClass, this.cls]);
20970         this.initValue();
20971     },
20972
20973     /**
20974      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20975      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20976      * @return {Roo.form.Field} this
20977      */
20978     applyTo : function(target){
20979         this.allowDomMove = false;
20980         this.el = Roo.get(target);
20981         this.render(this.el.dom.parentNode);
20982         return this;
20983     },
20984
20985     // private
20986     initValue : function(){
20987         if(this.value !== undefined){
20988             this.setValue(this.value);
20989         }else if(this.el.dom.value.length > 0){
20990             this.setValue(this.el.dom.value);
20991         }
20992     },
20993
20994     /**
20995      * Returns true if this field has been changed since it was originally loaded and is not disabled.
20996      */
20997     isDirty : function() {
20998         if(this.disabled) {
20999             return false;
21000         }
21001         return String(this.getValue()) !== String(this.originalValue);
21002     },
21003
21004     // private
21005     afterRender : function(){
21006         Roo.form.Field.superclass.afterRender.call(this);
21007         this.initEvents();
21008     },
21009
21010     // private
21011     fireKey : function(e){
21012         //Roo.log('field ' + e.getKey());
21013         if(e.isNavKeyPress()){
21014             this.fireEvent("specialkey", this, e);
21015         }
21016     },
21017
21018     /**
21019      * Resets the current field value to the originally loaded value and clears any validation messages
21020      */
21021     reset : function(){
21022         this.setValue(this.originalValue);
21023         this.clearInvalid();
21024     },
21025
21026     // private
21027     initEvents : function(){
21028         // safari killled keypress - so keydown is now used..
21029         this.el.on("keydown" , this.fireKey,  this);
21030         this.el.on("focus", this.onFocus,  this);
21031         this.el.on("blur", this.onBlur,  this);
21032         this.el.relayEvent('keyup', this);
21033
21034         // reference to original value for reset
21035         this.originalValue = this.getValue();
21036     },
21037
21038     // private
21039     onFocus : function(){
21040         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21041             this.el.addClass(this.focusClass);
21042         }
21043         if(!this.hasFocus){
21044             this.hasFocus = true;
21045             this.startValue = this.getValue();
21046             this.fireEvent("focus", this);
21047         }
21048     },
21049
21050     beforeBlur : Roo.emptyFn,
21051
21052     // private
21053     onBlur : function(){
21054         this.beforeBlur();
21055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21056             this.el.removeClass(this.focusClass);
21057         }
21058         this.hasFocus = false;
21059         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21060             this.validate();
21061         }
21062         var v = this.getValue();
21063         if(String(v) !== String(this.startValue)){
21064             this.fireEvent('change', this, v, this.startValue);
21065         }
21066         this.fireEvent("blur", this);
21067     },
21068
21069     /**
21070      * Returns whether or not the field value is currently valid
21071      * @param {Boolean} preventMark True to disable marking the field invalid
21072      * @return {Boolean} True if the value is valid, else false
21073      */
21074     isValid : function(preventMark){
21075         if(this.disabled){
21076             return true;
21077         }
21078         var restore = this.preventMark;
21079         this.preventMark = preventMark === true;
21080         var v = this.validateValue(this.processValue(this.getRawValue()));
21081         this.preventMark = restore;
21082         return v;
21083     },
21084
21085     /**
21086      * Validates the field value
21087      * @return {Boolean} True if the value is valid, else false
21088      */
21089     validate : function(){
21090         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21091             this.clearInvalid();
21092             return true;
21093         }
21094         return false;
21095     },
21096
21097     processValue : function(value){
21098         return value;
21099     },
21100
21101     // private
21102     // Subclasses should provide the validation implementation by overriding this
21103     validateValue : function(value){
21104         return true;
21105     },
21106
21107     /**
21108      * Mark this field as invalid
21109      * @param {String} msg The validation message
21110      */
21111     markInvalid : function(msg){
21112         if(!this.rendered || this.preventMark){ // not rendered
21113             return;
21114         }
21115         this.el.addClass(this.invalidClass);
21116         msg = msg || this.invalidText;
21117         switch(this.msgTarget){
21118             case 'qtip':
21119                 this.el.dom.qtip = msg;
21120                 this.el.dom.qclass = 'x-form-invalid-tip';
21121                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21122                     Roo.QuickTips.enable();
21123                 }
21124                 break;
21125             case 'title':
21126                 this.el.dom.title = msg;
21127                 break;
21128             case 'under':
21129                 if(!this.errorEl){
21130                     var elp = this.el.findParent('.x-form-element', 5, true);
21131                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21132                     this.errorEl.setWidth(elp.getWidth(true)-20);
21133                 }
21134                 this.errorEl.update(msg);
21135                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21136                 break;
21137             case 'side':
21138                 if(!this.errorIcon){
21139                     var elp = this.el.findParent('.x-form-element', 5, true);
21140                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21141                 }
21142                 this.alignErrorIcon();
21143                 this.errorIcon.dom.qtip = msg;
21144                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21145                 this.errorIcon.show();
21146                 this.on('resize', this.alignErrorIcon, this);
21147                 break;
21148             default:
21149                 var t = Roo.getDom(this.msgTarget);
21150                 t.innerHTML = msg;
21151                 t.style.display = this.msgDisplay;
21152                 break;
21153         }
21154         this.fireEvent('invalid', this, msg);
21155     },
21156
21157     // private
21158     alignErrorIcon : function(){
21159         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21160     },
21161
21162     /**
21163      * Clear any invalid styles/messages for this field
21164      */
21165     clearInvalid : function(){
21166         if(!this.rendered || this.preventMark){ // not rendered
21167             return;
21168         }
21169         this.el.removeClass(this.invalidClass);
21170         switch(this.msgTarget){
21171             case 'qtip':
21172                 this.el.dom.qtip = '';
21173                 break;
21174             case 'title':
21175                 this.el.dom.title = '';
21176                 break;
21177             case 'under':
21178                 if(this.errorEl){
21179                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21180                 }
21181                 break;
21182             case 'side':
21183                 if(this.errorIcon){
21184                     this.errorIcon.dom.qtip = '';
21185                     this.errorIcon.hide();
21186                     this.un('resize', this.alignErrorIcon, this);
21187                 }
21188                 break;
21189             default:
21190                 var t = Roo.getDom(this.msgTarget);
21191                 t.innerHTML = '';
21192                 t.style.display = 'none';
21193                 break;
21194         }
21195         this.fireEvent('valid', this);
21196     },
21197
21198     /**
21199      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21200      * @return {Mixed} value The field value
21201      */
21202     getRawValue : function(){
21203         var v = this.el.getValue();
21204         if(v === this.emptyText){
21205             v = '';
21206         }
21207         return v;
21208     },
21209
21210     /**
21211      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21212      * @return {Mixed} value The field value
21213      */
21214     getValue : function(){
21215         var v = this.el.getValue();
21216         if(v === this.emptyText || v === undefined){
21217             v = '';
21218         }
21219         return v;
21220     },
21221
21222     /**
21223      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21224      * @param {Mixed} value The value to set
21225      */
21226     setRawValue : function(v){
21227         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21228     },
21229
21230     /**
21231      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21232      * @param {Mixed} value The value to set
21233      */
21234     setValue : function(v){
21235         this.value = v;
21236         if(this.rendered){
21237             this.el.dom.value = (v === null || v === undefined ? '' : v);
21238             this.validate();
21239         }
21240     },
21241
21242     adjustSize : function(w, h){
21243         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21244         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21245         return s;
21246     },
21247
21248     adjustWidth : function(tag, w){
21249         tag = tag.toLowerCase();
21250         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21251             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21252                 if(tag == 'input'){
21253                     return w + 2;
21254                 }
21255                 if(tag = 'textarea'){
21256                     return w-2;
21257                 }
21258             }else if(Roo.isOpera){
21259                 if(tag == 'input'){
21260                     return w + 2;
21261                 }
21262                 if(tag = 'textarea'){
21263                     return w-2;
21264                 }
21265             }
21266         }
21267         return w;
21268     }
21269 });
21270
21271
21272 // anything other than normal should be considered experimental
21273 Roo.form.Field.msgFx = {
21274     normal : {
21275         show: function(msgEl, f){
21276             msgEl.setDisplayed('block');
21277         },
21278
21279         hide : function(msgEl, f){
21280             msgEl.setDisplayed(false).update('');
21281         }
21282     },
21283
21284     slide : {
21285         show: function(msgEl, f){
21286             msgEl.slideIn('t', {stopFx:true});
21287         },
21288
21289         hide : function(msgEl, f){
21290             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21291         }
21292     },
21293
21294     slideRight : {
21295         show: function(msgEl, f){
21296             msgEl.fixDisplay();
21297             msgEl.alignTo(f.el, 'tl-tr');
21298             msgEl.slideIn('l', {stopFx:true});
21299         },
21300
21301         hide : function(msgEl, f){
21302             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21303         }
21304     }
21305 };/*
21306  * Based on:
21307  * Ext JS Library 1.1.1
21308  * Copyright(c) 2006-2007, Ext JS, LLC.
21309  *
21310  * Originally Released Under LGPL - original licence link has changed is not relivant.
21311  *
21312  * Fork - LGPL
21313  * <script type="text/javascript">
21314  */
21315  
21316
21317 /**
21318  * @class Roo.form.TextField
21319  * @extends Roo.form.Field
21320  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21321  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21322  * @constructor
21323  * Creates a new TextField
21324  * @param {Object} config Configuration options
21325  */
21326 Roo.form.TextField = function(config){
21327     Roo.form.TextField.superclass.constructor.call(this, config);
21328     this.addEvents({
21329         /**
21330          * @event autosize
21331          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21332          * according to the default logic, but this event provides a hook for the developer to apply additional
21333          * logic at runtime to resize the field if needed.
21334              * @param {Roo.form.Field} this This text field
21335              * @param {Number} width The new field width
21336              */
21337         autosize : true
21338     });
21339 };
21340
21341 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21342     /**
21343      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21344      */
21345     grow : false,
21346     /**
21347      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21348      */
21349     growMin : 30,
21350     /**
21351      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21352      */
21353     growMax : 800,
21354     /**
21355      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21356      */
21357     vtype : null,
21358     /**
21359      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21360      */
21361     maskRe : null,
21362     /**
21363      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21364      */
21365     disableKeyFilter : false,
21366     /**
21367      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21368      */
21369     allowBlank : true,
21370     /**
21371      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21372      */
21373     minLength : 0,
21374     /**
21375      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21376      */
21377     maxLength : Number.MAX_VALUE,
21378     /**
21379      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21380      */
21381     minLengthText : "The minimum length for this field is {0}",
21382     /**
21383      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21384      */
21385     maxLengthText : "The maximum length for this field is {0}",
21386     /**
21387      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21388      */
21389     selectOnFocus : false,
21390     /**
21391      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21392      */
21393     blankText : "This field is required",
21394     /**
21395      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21396      * If available, this function will be called only after the basic validators all return true, and will be passed the
21397      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21398      */
21399     validator : null,
21400     /**
21401      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21402      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21403      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21404      */
21405     regex : null,
21406     /**
21407      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21408      */
21409     regexText : "",
21410     /**
21411      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21412      */
21413     emptyText : null,
21414     /**
21415      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21416      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21417      */
21418     emptyClass : 'x-form-empty-field',
21419
21420     // private
21421     initEvents : function(){
21422         Roo.form.TextField.superclass.initEvents.call(this);
21423         if(this.validationEvent == 'keyup'){
21424             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21425             this.el.on('keyup', this.filterValidation, this);
21426         }
21427         else if(this.validationEvent !== false){
21428             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21429         }
21430         if(this.selectOnFocus || this.emptyText){
21431             this.on("focus", this.preFocus, this);
21432             if(this.emptyText){
21433                 this.on('blur', this.postBlur, this);
21434                 this.applyEmptyText();
21435             }
21436         }
21437         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21438             this.el.on("keypress", this.filterKeys, this);
21439         }
21440         if(this.grow){
21441             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21442             this.el.on("click", this.autoSize,  this);
21443         }
21444     },
21445
21446     processValue : function(value){
21447         if(this.stripCharsRe){
21448             var newValue = value.replace(this.stripCharsRe, '');
21449             if(newValue !== value){
21450                 this.setRawValue(newValue);
21451                 return newValue;
21452             }
21453         }
21454         return value;
21455     },
21456
21457     filterValidation : function(e){
21458         if(!e.isNavKeyPress()){
21459             this.validationTask.delay(this.validationDelay);
21460         }
21461     },
21462
21463     // private
21464     onKeyUp : function(e){
21465         if(!e.isNavKeyPress()){
21466             this.autoSize();
21467         }
21468     },
21469
21470     /**
21471      * Resets the current field value to the originally-loaded value and clears any validation messages.
21472      * Also adds emptyText and emptyClass if the original value was blank.
21473      */
21474     reset : function(){
21475         Roo.form.TextField.superclass.reset.call(this);
21476         this.applyEmptyText();
21477     },
21478
21479     applyEmptyText : function(){
21480         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21481             this.setRawValue(this.emptyText);
21482             this.el.addClass(this.emptyClass);
21483         }
21484     },
21485
21486     // private
21487     preFocus : function(){
21488         if(this.emptyText){
21489             if(this.el.dom.value == this.emptyText){
21490                 this.setRawValue('');
21491             }
21492             this.el.removeClass(this.emptyClass);
21493         }
21494         if(this.selectOnFocus){
21495             this.el.dom.select();
21496         }
21497     },
21498
21499     // private
21500     postBlur : function(){
21501         this.applyEmptyText();
21502     },
21503
21504     // private
21505     filterKeys : function(e){
21506         var k = e.getKey();
21507         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21508             return;
21509         }
21510         var c = e.getCharCode(), cc = String.fromCharCode(c);
21511         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21512             return;
21513         }
21514         if(!this.maskRe.test(cc)){
21515             e.stopEvent();
21516         }
21517     },
21518
21519     setValue : function(v){
21520         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21521             this.el.removeClass(this.emptyClass);
21522         }
21523         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21524         this.applyEmptyText();
21525         this.autoSize();
21526     },
21527
21528     /**
21529      * Validates a value according to the field's validation rules and marks the field as invalid
21530      * if the validation fails
21531      * @param {Mixed} value The value to validate
21532      * @return {Boolean} True if the value is valid, else false
21533      */
21534     validateValue : function(value){
21535         if(value.length < 1 || value === this.emptyText){ // if it's blank
21536              if(this.allowBlank){
21537                 this.clearInvalid();
21538                 return true;
21539              }else{
21540                 this.markInvalid(this.blankText);
21541                 return false;
21542              }
21543         }
21544         if(value.length < this.minLength){
21545             this.markInvalid(String.format(this.minLengthText, this.minLength));
21546             return false;
21547         }
21548         if(value.length > this.maxLength){
21549             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21550             return false;
21551         }
21552         if(this.vtype){
21553             var vt = Roo.form.VTypes;
21554             if(!vt[this.vtype](value, this)){
21555                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21556                 return false;
21557             }
21558         }
21559         if(typeof this.validator == "function"){
21560             var msg = this.validator(value);
21561             if(msg !== true){
21562                 this.markInvalid(msg);
21563                 return false;
21564             }
21565         }
21566         if(this.regex && !this.regex.test(value)){
21567             this.markInvalid(this.regexText);
21568             return false;
21569         }
21570         return true;
21571     },
21572
21573     /**
21574      * Selects text in this field
21575      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21576      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21577      */
21578     selectText : function(start, end){
21579         var v = this.getRawValue();
21580         if(v.length > 0){
21581             start = start === undefined ? 0 : start;
21582             end = end === undefined ? v.length : end;
21583             var d = this.el.dom;
21584             if(d.setSelectionRange){
21585                 d.setSelectionRange(start, end);
21586             }else if(d.createTextRange){
21587                 var range = d.createTextRange();
21588                 range.moveStart("character", start);
21589                 range.moveEnd("character", v.length-end);
21590                 range.select();
21591             }
21592         }
21593     },
21594
21595     /**
21596      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21597      * This only takes effect if grow = true, and fires the autosize event.
21598      */
21599     autoSize : function(){
21600         if(!this.grow || !this.rendered){
21601             return;
21602         }
21603         if(!this.metrics){
21604             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21605         }
21606         var el = this.el;
21607         var v = el.dom.value;
21608         var d = document.createElement('div');
21609         d.appendChild(document.createTextNode(v));
21610         v = d.innerHTML;
21611         d = null;
21612         v += "&#160;";
21613         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21614         this.el.setWidth(w);
21615         this.fireEvent("autosize", this, w);
21616     }
21617 });/*
21618  * Based on:
21619  * Ext JS Library 1.1.1
21620  * Copyright(c) 2006-2007, Ext JS, LLC.
21621  *
21622  * Originally Released Under LGPL - original licence link has changed is not relivant.
21623  *
21624  * Fork - LGPL
21625  * <script type="text/javascript">
21626  */
21627  
21628 /**
21629  * @class Roo.form.Hidden
21630  * @extends Roo.form.TextField
21631  * Simple Hidden element used on forms 
21632  * 
21633  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21634  * 
21635  * @constructor
21636  * Creates a new Hidden form element.
21637  * @param {Object} config Configuration options
21638  */
21639
21640
21641
21642 // easy hidden field...
21643 Roo.form.Hidden = function(config){
21644     Roo.form.Hidden.superclass.constructor.call(this, config);
21645 };
21646   
21647 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21648     fieldLabel:      '',
21649     inputType:      'hidden',
21650     width:          50,
21651     allowBlank:     true,
21652     labelSeparator: '',
21653     hidden:         true,
21654     itemCls :       'x-form-item-display-none'
21655
21656
21657 });
21658
21659
21660 /*
21661  * Based on:
21662  * Ext JS Library 1.1.1
21663  * Copyright(c) 2006-2007, Ext JS, LLC.
21664  *
21665  * Originally Released Under LGPL - original licence link has changed is not relivant.
21666  *
21667  * Fork - LGPL
21668  * <script type="text/javascript">
21669  */
21670  
21671 /**
21672  * @class Roo.form.TriggerField
21673  * @extends Roo.form.TextField
21674  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21675  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21676  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21677  * for which you can provide a custom implementation.  For example:
21678  * <pre><code>
21679 var trigger = new Roo.form.TriggerField();
21680 trigger.onTriggerClick = myTriggerFn;
21681 trigger.applyTo('my-field');
21682 </code></pre>
21683  *
21684  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21685  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21686  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21687  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21688  * @constructor
21689  * Create a new TriggerField.
21690  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21691  * to the base TextField)
21692  */
21693 Roo.form.TriggerField = function(config){
21694     this.mimicing = false;
21695     Roo.form.TriggerField.superclass.constructor.call(this, config);
21696 };
21697
21698 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21699     /**
21700      * @cfg {String} triggerClass A CSS class to apply to the trigger
21701      */
21702     /**
21703      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21704      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21705      */
21706     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21707     /**
21708      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21709      */
21710     hideTrigger:false,
21711
21712     /** @cfg {Boolean} grow @hide */
21713     /** @cfg {Number} growMin @hide */
21714     /** @cfg {Number} growMax @hide */
21715
21716     /**
21717      * @hide 
21718      * @method
21719      */
21720     autoSize: Roo.emptyFn,
21721     // private
21722     monitorTab : true,
21723     // private
21724     deferHeight : true,
21725
21726     
21727     actionMode : 'wrap',
21728     // private
21729     onResize : function(w, h){
21730         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21731         if(typeof w == 'number'){
21732             var x = w - this.trigger.getWidth();
21733             this.el.setWidth(this.adjustWidth('input', x));
21734             this.trigger.setStyle('left', x+'px');
21735         }
21736     },
21737
21738     // private
21739     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21740
21741     // private
21742     getResizeEl : function(){
21743         return this.wrap;
21744     },
21745
21746     // private
21747     getPositionEl : function(){
21748         return this.wrap;
21749     },
21750
21751     // private
21752     alignErrorIcon : function(){
21753         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21754     },
21755
21756     // private
21757     onRender : function(ct, position){
21758         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21759         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21760         this.trigger = this.wrap.createChild(this.triggerConfig ||
21761                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21762         if(this.hideTrigger){
21763             this.trigger.setDisplayed(false);
21764         }
21765         this.initTrigger();
21766         if(!this.width){
21767             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21768         }
21769     },
21770
21771     // private
21772     initTrigger : function(){
21773         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21774         this.trigger.addClassOnOver('x-form-trigger-over');
21775         this.trigger.addClassOnClick('x-form-trigger-click');
21776     },
21777
21778     // private
21779     onDestroy : function(){
21780         if(this.trigger){
21781             this.trigger.removeAllListeners();
21782             this.trigger.remove();
21783         }
21784         if(this.wrap){
21785             this.wrap.remove();
21786         }
21787         Roo.form.TriggerField.superclass.onDestroy.call(this);
21788     },
21789
21790     // private
21791     onFocus : function(){
21792         Roo.form.TriggerField.superclass.onFocus.call(this);
21793         if(!this.mimicing){
21794             this.wrap.addClass('x-trigger-wrap-focus');
21795             this.mimicing = true;
21796             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21797             if(this.monitorTab){
21798                 this.el.on("keydown", this.checkTab, this);
21799             }
21800         }
21801     },
21802
21803     // private
21804     checkTab : function(e){
21805         if(e.getKey() == e.TAB){
21806             this.triggerBlur();
21807         }
21808     },
21809
21810     // private
21811     onBlur : function(){
21812         // do nothing
21813     },
21814
21815     // private
21816     mimicBlur : function(e, t){
21817         if(!this.wrap.contains(t) && this.validateBlur()){
21818             this.triggerBlur();
21819         }
21820     },
21821
21822     // private
21823     triggerBlur : function(){
21824         this.mimicing = false;
21825         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21826         if(this.monitorTab){
21827             this.el.un("keydown", this.checkTab, this);
21828         }
21829         this.wrap.removeClass('x-trigger-wrap-focus');
21830         Roo.form.TriggerField.superclass.onBlur.call(this);
21831     },
21832
21833     // private
21834     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21835     validateBlur : function(e, t){
21836         return true;
21837     },
21838
21839     // private
21840     onDisable : function(){
21841         Roo.form.TriggerField.superclass.onDisable.call(this);
21842         if(this.wrap){
21843             this.wrap.addClass('x-item-disabled');
21844         }
21845     },
21846
21847     // private
21848     onEnable : function(){
21849         Roo.form.TriggerField.superclass.onEnable.call(this);
21850         if(this.wrap){
21851             this.wrap.removeClass('x-item-disabled');
21852         }
21853     },
21854
21855     // private
21856     onShow : function(){
21857         var ae = this.getActionEl();
21858         
21859         if(ae){
21860             ae.dom.style.display = '';
21861             ae.dom.style.visibility = 'visible';
21862         }
21863     },
21864
21865     // private
21866     
21867     onHide : function(){
21868         var ae = this.getActionEl();
21869         ae.dom.style.display = 'none';
21870     },
21871
21872     /**
21873      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21874      * by an implementing function.
21875      * @method
21876      * @param {EventObject} e
21877      */
21878     onTriggerClick : Roo.emptyFn
21879 });
21880
21881 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21882 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21883 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21884 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21885     initComponent : function(){
21886         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21887
21888         this.triggerConfig = {
21889             tag:'span', cls:'x-form-twin-triggers', cn:[
21890             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21891             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21892         ]};
21893     },
21894
21895     getTrigger : function(index){
21896         return this.triggers[index];
21897     },
21898
21899     initTrigger : function(){
21900         var ts = this.trigger.select('.x-form-trigger', true);
21901         this.wrap.setStyle('overflow', 'hidden');
21902         var triggerField = this;
21903         ts.each(function(t, all, index){
21904             t.hide = function(){
21905                 var w = triggerField.wrap.getWidth();
21906                 this.dom.style.display = 'none';
21907                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21908             };
21909             t.show = function(){
21910                 var w = triggerField.wrap.getWidth();
21911                 this.dom.style.display = '';
21912                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21913             };
21914             var triggerIndex = 'Trigger'+(index+1);
21915
21916             if(this['hide'+triggerIndex]){
21917                 t.dom.style.display = 'none';
21918             }
21919             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21920             t.addClassOnOver('x-form-trigger-over');
21921             t.addClassOnClick('x-form-trigger-click');
21922         }, this);
21923         this.triggers = ts.elements;
21924     },
21925
21926     onTrigger1Click : Roo.emptyFn,
21927     onTrigger2Click : Roo.emptyFn
21928 });/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938  
21939 /**
21940  * @class Roo.form.TextArea
21941  * @extends Roo.form.TextField
21942  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21943  * support for auto-sizing.
21944  * @constructor
21945  * Creates a new TextArea
21946  * @param {Object} config Configuration options
21947  */
21948 Roo.form.TextArea = function(config){
21949     Roo.form.TextArea.superclass.constructor.call(this, config);
21950     // these are provided exchanges for backwards compat
21951     // minHeight/maxHeight were replaced by growMin/growMax to be
21952     // compatible with TextField growing config values
21953     if(this.minHeight !== undefined){
21954         this.growMin = this.minHeight;
21955     }
21956     if(this.maxHeight !== undefined){
21957         this.growMax = this.maxHeight;
21958     }
21959 };
21960
21961 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21962     /**
21963      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21964      */
21965     growMin : 60,
21966     /**
21967      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21968      */
21969     growMax: 1000,
21970     /**
21971      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21972      * in the field (equivalent to setting overflow: hidden, defaults to false)
21973      */
21974     preventScrollbars: false,
21975     /**
21976      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21977      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21978      */
21979
21980     // private
21981     onRender : function(ct, position){
21982         if(!this.el){
21983             this.defaultAutoCreate = {
21984                 tag: "textarea",
21985                 style:"width:300px;height:60px;",
21986                 autocomplete: "off"
21987             };
21988         }
21989         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
21990         if(this.grow){
21991             this.textSizeEl = Roo.DomHelper.append(document.body, {
21992                 tag: "pre", cls: "x-form-grow-sizer"
21993             });
21994             if(this.preventScrollbars){
21995                 this.el.setStyle("overflow", "hidden");
21996             }
21997             this.el.setHeight(this.growMin);
21998         }
21999     },
22000
22001     onDestroy : function(){
22002         if(this.textSizeEl){
22003             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22004         }
22005         Roo.form.TextArea.superclass.onDestroy.call(this);
22006     },
22007
22008     // private
22009     onKeyUp : function(e){
22010         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22011             this.autoSize();
22012         }
22013     },
22014
22015     /**
22016      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22017      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22018      */
22019     autoSize : function(){
22020         if(!this.grow || !this.textSizeEl){
22021             return;
22022         }
22023         var el = this.el;
22024         var v = el.dom.value;
22025         var ts = this.textSizeEl;
22026
22027         ts.innerHTML = '';
22028         ts.appendChild(document.createTextNode(v));
22029         v = ts.innerHTML;
22030
22031         Roo.fly(ts).setWidth(this.el.getWidth());
22032         if(v.length < 1){
22033             v = "&#160;&#160;";
22034         }else{
22035             if(Roo.isIE){
22036                 v = v.replace(/\n/g, '<p>&#160;</p>');
22037             }
22038             v += "&#160;\n&#160;";
22039         }
22040         ts.innerHTML = v;
22041         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22042         if(h != this.lastHeight){
22043             this.lastHeight = h;
22044             this.el.setHeight(h);
22045             this.fireEvent("autosize", this, h);
22046         }
22047     }
22048 });/*
22049  * Based on:
22050  * Ext JS Library 1.1.1
22051  * Copyright(c) 2006-2007, Ext JS, LLC.
22052  *
22053  * Originally Released Under LGPL - original licence link has changed is not relivant.
22054  *
22055  * Fork - LGPL
22056  * <script type="text/javascript">
22057  */
22058  
22059
22060 /**
22061  * @class Roo.form.NumberField
22062  * @extends Roo.form.TextField
22063  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22064  * @constructor
22065  * Creates a new NumberField
22066  * @param {Object} config Configuration options
22067  */
22068 Roo.form.NumberField = function(config){
22069     Roo.form.NumberField.superclass.constructor.call(this, config);
22070 };
22071
22072 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22073     /**
22074      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22075      */
22076     fieldClass: "x-form-field x-form-num-field",
22077     /**
22078      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22079      */
22080     allowDecimals : true,
22081     /**
22082      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22083      */
22084     decimalSeparator : ".",
22085     /**
22086      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22087      */
22088     decimalPrecision : 2,
22089     /**
22090      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22091      */
22092     allowNegative : true,
22093     /**
22094      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22095      */
22096     minValue : Number.NEGATIVE_INFINITY,
22097     /**
22098      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22099      */
22100     maxValue : Number.MAX_VALUE,
22101     /**
22102      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22103      */
22104     minText : "The minimum value for this field is {0}",
22105     /**
22106      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22107      */
22108     maxText : "The maximum value for this field is {0}",
22109     /**
22110      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22111      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22112      */
22113     nanText : "{0} is not a valid number",
22114
22115     // private
22116     initEvents : function(){
22117         Roo.form.NumberField.superclass.initEvents.call(this);
22118         var allowed = "0123456789";
22119         if(this.allowDecimals){
22120             allowed += this.decimalSeparator;
22121         }
22122         if(this.allowNegative){
22123             allowed += "-";
22124         }
22125         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22126         var keyPress = function(e){
22127             var k = e.getKey();
22128             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22129                 return;
22130             }
22131             var c = e.getCharCode();
22132             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22133                 e.stopEvent();
22134             }
22135         };
22136         this.el.on("keypress", keyPress, this);
22137     },
22138
22139     // private
22140     validateValue : function(value){
22141         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22142             return false;
22143         }
22144         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22145              return true;
22146         }
22147         var num = this.parseValue(value);
22148         if(isNaN(num)){
22149             this.markInvalid(String.format(this.nanText, value));
22150             return false;
22151         }
22152         if(num < this.minValue){
22153             this.markInvalid(String.format(this.minText, this.minValue));
22154             return false;
22155         }
22156         if(num > this.maxValue){
22157             this.markInvalid(String.format(this.maxText, this.maxValue));
22158             return false;
22159         }
22160         return true;
22161     },
22162
22163     getValue : function(){
22164         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22165     },
22166
22167     // private
22168     parseValue : function(value){
22169         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22170         return isNaN(value) ? '' : value;
22171     },
22172
22173     // private
22174     fixPrecision : function(value){
22175         var nan = isNaN(value);
22176         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22177             return nan ? '' : value;
22178         }
22179         return parseFloat(value).toFixed(this.decimalPrecision);
22180     },
22181
22182     setValue : function(v){
22183         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22184     },
22185
22186     // private
22187     decimalPrecisionFcn : function(v){
22188         return Math.floor(v);
22189     },
22190
22191     beforeBlur : function(){
22192         var v = this.parseValue(this.getRawValue());
22193         if(v){
22194             this.setValue(this.fixPrecision(v));
22195         }
22196     }
22197 });/*
22198  * Based on:
22199  * Ext JS Library 1.1.1
22200  * Copyright(c) 2006-2007, Ext JS, LLC.
22201  *
22202  * Originally Released Under LGPL - original licence link has changed is not relivant.
22203  *
22204  * Fork - LGPL
22205  * <script type="text/javascript">
22206  */
22207  
22208 /**
22209  * @class Roo.form.DateField
22210  * @extends Roo.form.TriggerField
22211  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22212 * @constructor
22213 * Create a new DateField
22214 * @param {Object} config
22215  */
22216 Roo.form.DateField = function(config){
22217     Roo.form.DateField.superclass.constructor.call(this, config);
22218     
22219       this.addEvents({
22220          
22221         /**
22222          * @event select
22223          * Fires when a date is selected
22224              * @param {Roo.form.DateField} combo This combo box
22225              * @param {Date} date The date selected
22226              */
22227         'select' : true
22228          
22229     });
22230     
22231     
22232     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22233     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22234     this.ddMatch = null;
22235     if(this.disabledDates){
22236         var dd = this.disabledDates;
22237         var re = "(?:";
22238         for(var i = 0; i < dd.length; i++){
22239             re += dd[i];
22240             if(i != dd.length-1) re += "|";
22241         }
22242         this.ddMatch = new RegExp(re + ")");
22243     }
22244 };
22245
22246 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22247     /**
22248      * @cfg {String} format
22249      * The default date format string which can be overriden for localization support.  The format must be
22250      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22251      */
22252     format : "m/d/y",
22253     /**
22254      * @cfg {String} altFormats
22255      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22256      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22257      */
22258     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22259     /**
22260      * @cfg {Array} disabledDays
22261      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22262      */
22263     disabledDays : null,
22264     /**
22265      * @cfg {String} disabledDaysText
22266      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22267      */
22268     disabledDaysText : "Disabled",
22269     /**
22270      * @cfg {Array} disabledDates
22271      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22272      * expression so they are very powerful. Some examples:
22273      * <ul>
22274      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22275      * <li>["03/08", "09/16"] would disable those days for every year</li>
22276      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22277      * <li>["03/../2006"] would disable every day in March 2006</li>
22278      * <li>["^03"] would disable every day in every March</li>
22279      * </ul>
22280      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22281      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22282      */
22283     disabledDates : null,
22284     /**
22285      * @cfg {String} disabledDatesText
22286      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22287      */
22288     disabledDatesText : "Disabled",
22289     /**
22290      * @cfg {Date/String} minValue
22291      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22292      * valid format (defaults to null).
22293      */
22294     minValue : null,
22295     /**
22296      * @cfg {Date/String} maxValue
22297      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22298      * valid format (defaults to null).
22299      */
22300     maxValue : null,
22301     /**
22302      * @cfg {String} minText
22303      * The error text to display when the date in the cell is before minValue (defaults to
22304      * 'The date in this field must be after {minValue}').
22305      */
22306     minText : "The date in this field must be equal to or after {0}",
22307     /**
22308      * @cfg {String} maxText
22309      * The error text to display when the date in the cell is after maxValue (defaults to
22310      * 'The date in this field must be before {maxValue}').
22311      */
22312     maxText : "The date in this field must be equal to or before {0}",
22313     /**
22314      * @cfg {String} invalidText
22315      * The error text to display when the date in the field is invalid (defaults to
22316      * '{value} is not a valid date - it must be in the format {format}').
22317      */
22318     invalidText : "{0} is not a valid date - it must be in the format {1}",
22319     /**
22320      * @cfg {String} triggerClass
22321      * An additional CSS class used to style the trigger button.  The trigger will always get the
22322      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22323      * which displays a calendar icon).
22324      */
22325     triggerClass : 'x-form-date-trigger',
22326     
22327
22328     /**
22329      * @cfg {bool} useIso
22330      * if enabled, then the date field will use a hidden field to store the 
22331      * real value as iso formated date. default (false)
22332      */ 
22333     useIso : false,
22334     /**
22335      * @cfg {String/Object} autoCreate
22336      * A DomHelper element spec, or true for a default element spec (defaults to
22337      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22338      */ 
22339     // private
22340     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22341     
22342     // private
22343     hiddenField: false,
22344     
22345     onRender : function(ct, position)
22346     {
22347         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22348         if (this.useIso) {
22349             this.el.dom.removeAttribute('name'); 
22350             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22351                     'before', true);
22352             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22353             // prevent input submission
22354             this.hiddenName = this.name;
22355         }
22356             
22357             
22358     },
22359     
22360     // private
22361     validateValue : function(value)
22362     {
22363         value = this.formatDate(value);
22364         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22365             return false;
22366         }
22367         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22368              return true;
22369         }
22370         var svalue = value;
22371         value = this.parseDate(value);
22372         if(!value){
22373             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22374             return false;
22375         }
22376         var time = value.getTime();
22377         if(this.minValue && time < this.minValue.getTime()){
22378             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22379             return false;
22380         }
22381         if(this.maxValue && time > this.maxValue.getTime()){
22382             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22383             return false;
22384         }
22385         if(this.disabledDays){
22386             var day = value.getDay();
22387             for(var i = 0; i < this.disabledDays.length; i++) {
22388                 if(day === this.disabledDays[i]){
22389                     this.markInvalid(this.disabledDaysText);
22390                     return false;
22391                 }
22392             }
22393         }
22394         var fvalue = this.formatDate(value);
22395         if(this.ddMatch && this.ddMatch.test(fvalue)){
22396             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22397             return false;
22398         }
22399         return true;
22400     },
22401
22402     // private
22403     // Provides logic to override the default TriggerField.validateBlur which just returns true
22404     validateBlur : function(){
22405         return !this.menu || !this.menu.isVisible();
22406     },
22407
22408     /**
22409      * Returns the current date value of the date field.
22410      * @return {Date} The date value
22411      */
22412     getValue : function(){
22413         
22414         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22415     },
22416
22417     /**
22418      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22419      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22420      * (the default format used is "m/d/y").
22421      * <br />Usage:
22422      * <pre><code>
22423 //All of these calls set the same date value (May 4, 2006)
22424
22425 //Pass a date object:
22426 var dt = new Date('5/4/06');
22427 dateField.setValue(dt);
22428
22429 //Pass a date string (default format):
22430 dateField.setValue('5/4/06');
22431
22432 //Pass a date string (custom format):
22433 dateField.format = 'Y-m-d';
22434 dateField.setValue('2006-5-4');
22435 </code></pre>
22436      * @param {String/Date} date The date or valid date string
22437      */
22438     setValue : function(date){
22439         if (this.hiddenField) {
22440             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22441         }
22442         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22443     },
22444
22445     // private
22446     parseDate : function(value){
22447         if(!value || value instanceof Date){
22448             return value;
22449         }
22450         var v = Date.parseDate(value, this.format);
22451         if(!v && this.altFormats){
22452             if(!this.altFormatsArray){
22453                 this.altFormatsArray = this.altFormats.split("|");
22454             }
22455             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22456                 v = Date.parseDate(value, this.altFormatsArray[i]);
22457             }
22458         }
22459         return v;
22460     },
22461
22462     // private
22463     formatDate : function(date, fmt){
22464         return (!date || !(date instanceof Date)) ?
22465                date : date.dateFormat(fmt || this.format);
22466     },
22467
22468     // private
22469     menuListeners : {
22470         select: function(m, d){
22471             this.setValue(d);
22472             this.fireEvent('select', this, d);
22473         },
22474         show : function(){ // retain focus styling
22475             this.onFocus();
22476         },
22477         hide : function(){
22478             this.focus.defer(10, this);
22479             var ml = this.menuListeners;
22480             this.menu.un("select", ml.select,  this);
22481             this.menu.un("show", ml.show,  this);
22482             this.menu.un("hide", ml.hide,  this);
22483         }
22484     },
22485
22486     // private
22487     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22488     onTriggerClick : function(){
22489         if(this.disabled){
22490             return;
22491         }
22492         if(this.menu == null){
22493             this.menu = new Roo.menu.DateMenu();
22494         }
22495         Roo.apply(this.menu.picker,  {
22496             showClear: this.allowBlank,
22497             minDate : this.minValue,
22498             maxDate : this.maxValue,
22499             disabledDatesRE : this.ddMatch,
22500             disabledDatesText : this.disabledDatesText,
22501             disabledDays : this.disabledDays,
22502             disabledDaysText : this.disabledDaysText,
22503             format : this.format,
22504             minText : String.format(this.minText, this.formatDate(this.minValue)),
22505             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22506         });
22507         this.menu.on(Roo.apply({}, this.menuListeners, {
22508             scope:this
22509         }));
22510         this.menu.picker.setValue(this.getValue() || new Date());
22511         this.menu.show(this.el, "tl-bl?");
22512     },
22513
22514     beforeBlur : function(){
22515         var v = this.parseDate(this.getRawValue());
22516         if(v){
22517             this.setValue(v);
22518         }
22519     }
22520
22521     /** @cfg {Boolean} grow @hide */
22522     /** @cfg {Number} growMin @hide */
22523     /** @cfg {Number} growMax @hide */
22524     /**
22525      * @hide
22526      * @method autoSize
22527      */
22528 });/*
22529  * Based on:
22530  * Ext JS Library 1.1.1
22531  * Copyright(c) 2006-2007, Ext JS, LLC.
22532  *
22533  * Originally Released Under LGPL - original licence link has changed is not relivant.
22534  *
22535  * Fork - LGPL
22536  * <script type="text/javascript">
22537  */
22538  
22539
22540 /**
22541  * @class Roo.form.ComboBox
22542  * @extends Roo.form.TriggerField
22543  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22544  * @constructor
22545  * Create a new ComboBox.
22546  * @param {Object} config Configuration options
22547  */
22548 Roo.form.ComboBox = function(config){
22549     Roo.form.ComboBox.superclass.constructor.call(this, config);
22550     this.addEvents({
22551         /**
22552          * @event expand
22553          * Fires when the dropdown list is expanded
22554              * @param {Roo.form.ComboBox} combo This combo box
22555              */
22556         'expand' : true,
22557         /**
22558          * @event collapse
22559          * Fires when the dropdown list is collapsed
22560              * @param {Roo.form.ComboBox} combo This combo box
22561              */
22562         'collapse' : true,
22563         /**
22564          * @event beforeselect
22565          * Fires before a list item is selected. Return false to cancel the selection.
22566              * @param {Roo.form.ComboBox} combo This combo box
22567              * @param {Roo.data.Record} record The data record returned from the underlying store
22568              * @param {Number} index The index of the selected item in the dropdown list
22569              */
22570         'beforeselect' : true,
22571         /**
22572          * @event select
22573          * Fires when a list item is selected
22574              * @param {Roo.form.ComboBox} combo This combo box
22575              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22576              * @param {Number} index The index of the selected item in the dropdown list
22577              */
22578         'select' : true,
22579         /**
22580          * @event beforequery
22581          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22582          * The event object passed has these properties:
22583              * @param {Roo.form.ComboBox} combo This combo box
22584              * @param {String} query The query
22585              * @param {Boolean} forceAll true to force "all" query
22586              * @param {Boolean} cancel true to cancel the query
22587              * @param {Object} e The query event object
22588              */
22589         'beforequery': true,
22590          /**
22591          * @event add
22592          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22593              * @param {Roo.form.ComboBox} combo This combo box
22594              */
22595         'add' : true,
22596         /**
22597          * @event edit
22598          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22599              * @param {Roo.form.ComboBox} combo This combo box
22600              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22601              */
22602         'edit' : true
22603         
22604         
22605     });
22606     if(this.transform){
22607         this.allowDomMove = false;
22608         var s = Roo.getDom(this.transform);
22609         if(!this.hiddenName){
22610             this.hiddenName = s.name;
22611         }
22612         if(!this.store){
22613             this.mode = 'local';
22614             var d = [], opts = s.options;
22615             for(var i = 0, len = opts.length;i < len; i++){
22616                 var o = opts[i];
22617                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22618                 if(o.selected) {
22619                     this.value = value;
22620                 }
22621                 d.push([value, o.text]);
22622             }
22623             this.store = new Roo.data.SimpleStore({
22624                 'id': 0,
22625                 fields: ['value', 'text'],
22626                 data : d
22627             });
22628             this.valueField = 'value';
22629             this.displayField = 'text';
22630         }
22631         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22632         if(!this.lazyRender){
22633             this.target = true;
22634             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22635             s.parentNode.removeChild(s); // remove it
22636             this.render(this.el.parentNode);
22637         }else{
22638             s.parentNode.removeChild(s); // remove it
22639         }
22640
22641     }
22642     if (this.store) {
22643         this.store = Roo.factory(this.store, Roo.data);
22644     }
22645     
22646     this.selectedIndex = -1;
22647     if(this.mode == 'local'){
22648         if(config.queryDelay === undefined){
22649             this.queryDelay = 10;
22650         }
22651         if(config.minChars === undefined){
22652             this.minChars = 0;
22653         }
22654     }
22655 };
22656
22657 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22658     /**
22659      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22660      */
22661     /**
22662      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22663      * rendering into an Roo.Editor, defaults to false)
22664      */
22665     /**
22666      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22667      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22668      */
22669     /**
22670      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22671      */
22672     /**
22673      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22674      * the dropdown list (defaults to undefined, with no header element)
22675      */
22676
22677      /**
22678      * @cfg {String/Roo.Template} tpl The template to use to render the output
22679      */
22680      
22681     // private
22682     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22683     /**
22684      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22685      */
22686     listWidth: undefined,
22687     /**
22688      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22689      * mode = 'remote' or 'text' if mode = 'local')
22690      */
22691     displayField: undefined,
22692     /**
22693      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22694      * mode = 'remote' or 'value' if mode = 'local'). 
22695      * Note: use of a valueField requires the user make a selection
22696      * in order for a value to be mapped.
22697      */
22698     valueField: undefined,
22699     /**
22700      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22701      * field's data value (defaults to the underlying DOM element's name)
22702      */
22703     hiddenName: undefined,
22704     /**
22705      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22706      */
22707     listClass: '',
22708     /**
22709      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22710      */
22711     selectedClass: 'x-combo-selected',
22712     /**
22713      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22714      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22715      * which displays a downward arrow icon).
22716      */
22717     triggerClass : 'x-form-arrow-trigger',
22718     /**
22719      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22720      */
22721     shadow:'sides',
22722     /**
22723      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22724      * anchor positions (defaults to 'tl-bl')
22725      */
22726     listAlign: 'tl-bl?',
22727     /**
22728      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22729      */
22730     maxHeight: 300,
22731     /**
22732      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22733      * query specified by the allQuery config option (defaults to 'query')
22734      */
22735     triggerAction: 'query',
22736     /**
22737      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22738      * (defaults to 4, does not apply if editable = false)
22739      */
22740     minChars : 4,
22741     /**
22742      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22743      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22744      */
22745     typeAhead: false,
22746     /**
22747      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22748      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22749      */
22750     queryDelay: 500,
22751     /**
22752      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22753      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22754      */
22755     pageSize: 0,
22756     /**
22757      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22758      * when editable = true (defaults to false)
22759      */
22760     selectOnFocus:false,
22761     /**
22762      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22763      */
22764     queryParam: 'query',
22765     /**
22766      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22767      * when mode = 'remote' (defaults to 'Loading...')
22768      */
22769     loadingText: 'Loading...',
22770     /**
22771      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22772      */
22773     resizable: false,
22774     /**
22775      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22776      */
22777     handleHeight : 8,
22778     /**
22779      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22780      * traditional select (defaults to true)
22781      */
22782     editable: true,
22783     /**
22784      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22785      */
22786     allQuery: '',
22787     /**
22788      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22789      */
22790     mode: 'remote',
22791     /**
22792      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22793      * listWidth has a higher value)
22794      */
22795     minListWidth : 70,
22796     /**
22797      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22798      * allow the user to set arbitrary text into the field (defaults to false)
22799      */
22800     forceSelection:false,
22801     /**
22802      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22803      * if typeAhead = true (defaults to 250)
22804      */
22805     typeAheadDelay : 250,
22806     /**
22807      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22808      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22809      */
22810     valueNotFoundText : undefined,
22811     /**
22812      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22813      */
22814     blockFocus : false,
22815     
22816     /**
22817      * @cfg {Boolean} disableClear Disable showing of clear button.
22818      */
22819     disableClear : false,
22820     /**
22821      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22822      */
22823     alwaysQuery : false,
22824     
22825     //private
22826     addicon : false,
22827     editicon: false,
22828     
22829     
22830     // private
22831     onRender : function(ct, position){
22832         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22833         if(this.hiddenName){
22834             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22835                     'before', true);
22836             this.hiddenField.value =
22837                 this.hiddenValue !== undefined ? this.hiddenValue :
22838                 this.value !== undefined ? this.value : '';
22839
22840             // prevent input submission
22841             this.el.dom.removeAttribute('name');
22842         }
22843         if(Roo.isGecko){
22844             this.el.dom.setAttribute('autocomplete', 'off');
22845         }
22846
22847         var cls = 'x-combo-list';
22848
22849         this.list = new Roo.Layer({
22850             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22851         });
22852
22853         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22854         this.list.setWidth(lw);
22855         this.list.swallowEvent('mousewheel');
22856         this.assetHeight = 0;
22857
22858         if(this.title){
22859             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22860             this.assetHeight += this.header.getHeight();
22861         }
22862
22863         this.innerList = this.list.createChild({cls:cls+'-inner'});
22864         this.innerList.on('mouseover', this.onViewOver, this);
22865         this.innerList.on('mousemove', this.onViewMove, this);
22866         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22867         
22868         if(this.allowBlank && !this.pageSize && !this.disableClear){
22869             this.footer = this.list.createChild({cls:cls+'-ft'});
22870             this.pageTb = new Roo.Toolbar(this.footer);
22871            
22872         }
22873         if(this.pageSize){
22874             this.footer = this.list.createChild({cls:cls+'-ft'});
22875             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22876                     {pageSize: this.pageSize});
22877             
22878         }
22879         
22880         if (this.pageTb && this.allowBlank && !this.disableClear) {
22881             var _this = this;
22882             this.pageTb.add(new Roo.Toolbar.Fill(), {
22883                 cls: 'x-btn-icon x-btn-clear',
22884                 text: '&#160;',
22885                 handler: function()
22886                 {
22887                     _this.collapse();
22888                     _this.clearValue();
22889                     _this.onSelect(false, -1);
22890                 }
22891             });
22892         }
22893         if (this.footer) {
22894             this.assetHeight += this.footer.getHeight();
22895         }
22896         
22897
22898         if(!this.tpl){
22899             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22900         }
22901
22902         this.view = new Roo.View(this.innerList, this.tpl, {
22903             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22904         });
22905
22906         this.view.on('click', this.onViewClick, this);
22907
22908         this.store.on('beforeload', this.onBeforeLoad, this);
22909         this.store.on('load', this.onLoad, this);
22910         this.store.on('loadexception', this.collapse, this);
22911
22912         if(this.resizable){
22913             this.resizer = new Roo.Resizable(this.list,  {
22914                pinned:true, handles:'se'
22915             });
22916             this.resizer.on('resize', function(r, w, h){
22917                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22918                 this.listWidth = w;
22919                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22920                 this.restrictHeight();
22921             }, this);
22922             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22923         }
22924         if(!this.editable){
22925             this.editable = true;
22926             this.setEditable(false);
22927         }  
22928         
22929         
22930         if (typeof(this.events.add.listeners) != 'undefined') {
22931             
22932             this.addicon = this.wrap.createChild(
22933                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22934        
22935             this.addicon.on('click', function(e) {
22936                 this.fireEvent('add', this);
22937             }, this);
22938         }
22939         if (typeof(this.events.edit.listeners) != 'undefined') {
22940             
22941             this.editicon = this.wrap.createChild(
22942                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22943             if (this.addicon) {
22944                 this.editicon.setStyle('margin-left', '40px');
22945             }
22946             this.editicon.on('click', function(e) {
22947                 
22948                 // we fire even  if inothing is selected..
22949                 this.fireEvent('edit', this, this.lastData );
22950                 
22951             }, this);
22952         }
22953         
22954         
22955         
22956     },
22957
22958     // private
22959     initEvents : function(){
22960         Roo.form.ComboBox.superclass.initEvents.call(this);
22961
22962         this.keyNav = new Roo.KeyNav(this.el, {
22963             "up" : function(e){
22964                 this.inKeyMode = true;
22965                 this.selectPrev();
22966             },
22967
22968             "down" : function(e){
22969                 if(!this.isExpanded()){
22970                     this.onTriggerClick();
22971                 }else{
22972                     this.inKeyMode = true;
22973                     this.selectNext();
22974                 }
22975             },
22976
22977             "enter" : function(e){
22978                 this.onViewClick();
22979                 //return true;
22980             },
22981
22982             "esc" : function(e){
22983                 this.collapse();
22984             },
22985
22986             "tab" : function(e){
22987                 this.onViewClick(false);
22988                 return true;
22989             },
22990
22991             scope : this,
22992
22993             doRelay : function(foo, bar, hname){
22994                 if(hname == 'down' || this.scope.isExpanded()){
22995                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22996                 }
22997                 return true;
22998             },
22999
23000             forceKeyDown: true
23001         });
23002         this.queryDelay = Math.max(this.queryDelay || 10,
23003                 this.mode == 'local' ? 10 : 250);
23004         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23005         if(this.typeAhead){
23006             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23007         }
23008         if(this.editable !== false){
23009             this.el.on("keyup", this.onKeyUp, this);
23010         }
23011         if(this.forceSelection){
23012             this.on('blur', this.doForce, this);
23013         }
23014     },
23015
23016     onDestroy : function(){
23017         if(this.view){
23018             this.view.setStore(null);
23019             this.view.el.removeAllListeners();
23020             this.view.el.remove();
23021             this.view.purgeListeners();
23022         }
23023         if(this.list){
23024             this.list.destroy();
23025         }
23026         if(this.store){
23027             this.store.un('beforeload', this.onBeforeLoad, this);
23028             this.store.un('load', this.onLoad, this);
23029             this.store.un('loadexception', this.collapse, this);
23030         }
23031         Roo.form.ComboBox.superclass.onDestroy.call(this);
23032     },
23033
23034     // private
23035     fireKey : function(e){
23036         if(e.isNavKeyPress() && !this.list.isVisible()){
23037             this.fireEvent("specialkey", this, e);
23038         }
23039     },
23040
23041     // private
23042     onResize: function(w, h){
23043         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23044         
23045         if(typeof w != 'number'){
23046             // we do not handle it!?!?
23047             return;
23048         }
23049         var tw = this.trigger.getWidth();
23050         tw += this.addicon ? this.addicon.getWidth() : 0;
23051         tw += this.editicon ? this.editicon.getWidth() : 0;
23052         var x = w - tw;
23053         this.el.setWidth( this.adjustWidth('input', x));
23054             
23055         this.trigger.setStyle('left', x+'px');
23056         
23057         if(this.list && this.listWidth === undefined){
23058             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23059             this.list.setWidth(lw);
23060             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23061         }
23062         
23063     
23064         
23065     },
23066
23067     /**
23068      * Allow or prevent the user from directly editing the field text.  If false is passed,
23069      * the user will only be able to select from the items defined in the dropdown list.  This method
23070      * is the runtime equivalent of setting the 'editable' config option at config time.
23071      * @param {Boolean} value True to allow the user to directly edit the field text
23072      */
23073     setEditable : function(value){
23074         if(value == this.editable){
23075             return;
23076         }
23077         this.editable = value;
23078         if(!value){
23079             this.el.dom.setAttribute('readOnly', true);
23080             this.el.on('mousedown', this.onTriggerClick,  this);
23081             this.el.addClass('x-combo-noedit');
23082         }else{
23083             this.el.dom.setAttribute('readOnly', false);
23084             this.el.un('mousedown', this.onTriggerClick,  this);
23085             this.el.removeClass('x-combo-noedit');
23086         }
23087     },
23088
23089     // private
23090     onBeforeLoad : function(){
23091         if(!this.hasFocus){
23092             return;
23093         }
23094         this.innerList.update(this.loadingText ?
23095                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23096         this.restrictHeight();
23097         this.selectedIndex = -1;
23098     },
23099
23100     // private
23101     onLoad : function(){
23102         if(!this.hasFocus){
23103             return;
23104         }
23105         if(this.store.getCount() > 0){
23106             this.expand();
23107             this.restrictHeight();
23108             if(this.lastQuery == this.allQuery){
23109                 if(this.editable){
23110                     this.el.dom.select();
23111                 }
23112                 if(!this.selectByValue(this.value, true)){
23113                     this.select(0, true);
23114                 }
23115             }else{
23116                 this.selectNext();
23117                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23118                     this.taTask.delay(this.typeAheadDelay);
23119                 }
23120             }
23121         }else{
23122             this.onEmptyResults();
23123         }
23124         //this.el.focus();
23125     },
23126
23127     // private
23128     onTypeAhead : function(){
23129         if(this.store.getCount() > 0){
23130             var r = this.store.getAt(0);
23131             var newValue = r.data[this.displayField];
23132             var len = newValue.length;
23133             var selStart = this.getRawValue().length;
23134             if(selStart != len){
23135                 this.setRawValue(newValue);
23136                 this.selectText(selStart, newValue.length);
23137             }
23138         }
23139     },
23140
23141     // private
23142     onSelect : function(record, index){
23143         if(this.fireEvent('beforeselect', this, record, index) !== false){
23144             this.setFromData(index > -1 ? record.data : false);
23145             this.collapse();
23146             this.fireEvent('select', this, record, index);
23147         }
23148     },
23149
23150     /**
23151      * Returns the currently selected field value or empty string if no value is set.
23152      * @return {String} value The selected value
23153      */
23154     getValue : function(){
23155         if(this.valueField){
23156             return typeof this.value != 'undefined' ? this.value : '';
23157         }else{
23158             return Roo.form.ComboBox.superclass.getValue.call(this);
23159         }
23160     },
23161
23162     /**
23163      * Clears any text/value currently set in the field
23164      */
23165     clearValue : function(){
23166         if(this.hiddenField){
23167             this.hiddenField.value = '';
23168         }
23169         this.value = '';
23170         this.setRawValue('');
23171         this.lastSelectionText = '';
23172         this.applyEmptyText();
23173     },
23174
23175     /**
23176      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23177      * will be displayed in the field.  If the value does not match the data value of an existing item,
23178      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23179      * Otherwise the field will be blank (although the value will still be set).
23180      * @param {String} value The value to match
23181      */
23182     setValue : function(v){
23183         var text = v;
23184         if(this.valueField){
23185             var r = this.findRecord(this.valueField, v);
23186             if(r){
23187                 text = r.data[this.displayField];
23188             }else if(this.valueNotFoundText !== undefined){
23189                 text = this.valueNotFoundText;
23190             }
23191         }
23192         this.lastSelectionText = text;
23193         if(this.hiddenField){
23194             this.hiddenField.value = v;
23195         }
23196         Roo.form.ComboBox.superclass.setValue.call(this, text);
23197         this.value = v;
23198     },
23199     /**
23200      * @property {Object} the last set data for the element
23201      */
23202     
23203     lastData : false,
23204     /**
23205      * Sets the value of the field based on a object which is related to the record format for the store.
23206      * @param {Object} value the value to set as. or false on reset?
23207      */
23208     setFromData : function(o){
23209         var dv = ''; // display value
23210         var vv = ''; // value value..
23211         this.lastData = o;
23212         if (this.displayField) {
23213             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23214         } else {
23215             // this is an error condition!!!
23216             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23217         }
23218         
23219         if(this.valueField){
23220             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23221         }
23222         if(this.hiddenField){
23223             this.hiddenField.value = vv;
23224             
23225             this.lastSelectionText = dv;
23226             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23227             this.value = vv;
23228             return;
23229         }
23230         // no hidden field.. - we store the value in 'value', but still display
23231         // display field!!!!
23232         this.lastSelectionText = dv;
23233         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23234         this.value = vv;
23235         
23236         
23237     },
23238     // private
23239     reset : function(){
23240         // overridden so that last data is reset..
23241         this.setValue(this.originalValue);
23242         this.clearInvalid();
23243         this.lastData = false;
23244     },
23245     // private
23246     findRecord : function(prop, value){
23247         var record;
23248         if(this.store.getCount() > 0){
23249             this.store.each(function(r){
23250                 if(r.data[prop] == value){
23251                     record = r;
23252                     return false;
23253                 }
23254             });
23255         }
23256         return record;
23257     },
23258
23259     // private
23260     onViewMove : function(e, t){
23261         this.inKeyMode = false;
23262     },
23263
23264     // private
23265     onViewOver : function(e, t){
23266         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23267             return;
23268         }
23269         var item = this.view.findItemFromChild(t);
23270         if(item){
23271             var index = this.view.indexOf(item);
23272             this.select(index, false);
23273         }
23274     },
23275
23276     // private
23277     onViewClick : function(doFocus){
23278         var index = this.view.getSelectedIndexes()[0];
23279         var r = this.store.getAt(index);
23280         if(r){
23281             this.onSelect(r, index);
23282         }
23283         if(doFocus !== false && !this.blockFocus){
23284             this.el.focus();
23285         }
23286     },
23287
23288     // private
23289     restrictHeight : function(){
23290         this.innerList.dom.style.height = '';
23291         var inner = this.innerList.dom;
23292         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23293         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23294         this.list.beginUpdate();
23295         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23296         this.list.alignTo(this.el, this.listAlign);
23297         this.list.endUpdate();
23298     },
23299
23300     // private
23301     onEmptyResults : function(){
23302         this.collapse();
23303     },
23304
23305     /**
23306      * Returns true if the dropdown list is expanded, else false.
23307      */
23308     isExpanded : function(){
23309         return this.list.isVisible();
23310     },
23311
23312     /**
23313      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23314      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23315      * @param {String} value The data value of the item to select
23316      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23317      * selected item if it is not currently in view (defaults to true)
23318      * @return {Boolean} True if the value matched an item in the list, else false
23319      */
23320     selectByValue : function(v, scrollIntoView){
23321         if(v !== undefined && v !== null){
23322             var r = this.findRecord(this.valueField || this.displayField, v);
23323             if(r){
23324                 this.select(this.store.indexOf(r), scrollIntoView);
23325                 return true;
23326             }
23327         }
23328         return false;
23329     },
23330
23331     /**
23332      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23333      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23334      * @param {Number} index The zero-based index of the list item to select
23335      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23336      * selected item if it is not currently in view (defaults to true)
23337      */
23338     select : function(index, scrollIntoView){
23339         this.selectedIndex = index;
23340         this.view.select(index);
23341         if(scrollIntoView !== false){
23342             var el = this.view.getNode(index);
23343             if(el){
23344                 this.innerList.scrollChildIntoView(el, false);
23345             }
23346         }
23347     },
23348
23349     // private
23350     selectNext : function(){
23351         var ct = this.store.getCount();
23352         if(ct > 0){
23353             if(this.selectedIndex == -1){
23354                 this.select(0);
23355             }else if(this.selectedIndex < ct-1){
23356                 this.select(this.selectedIndex+1);
23357             }
23358         }
23359     },
23360
23361     // private
23362     selectPrev : function(){
23363         var ct = this.store.getCount();
23364         if(ct > 0){
23365             if(this.selectedIndex == -1){
23366                 this.select(0);
23367             }else if(this.selectedIndex != 0){
23368                 this.select(this.selectedIndex-1);
23369             }
23370         }
23371     },
23372
23373     // private
23374     onKeyUp : function(e){
23375         if(this.editable !== false && !e.isSpecialKey()){
23376             this.lastKey = e.getKey();
23377             this.dqTask.delay(this.queryDelay);
23378         }
23379     },
23380
23381     // private
23382     validateBlur : function(){
23383         return !this.list || !this.list.isVisible();   
23384     },
23385
23386     // private
23387     initQuery : function(){
23388         this.doQuery(this.getRawValue());
23389     },
23390
23391     // private
23392     doForce : function(){
23393         if(this.el.dom.value.length > 0){
23394             this.el.dom.value =
23395                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23396             this.applyEmptyText();
23397         }
23398     },
23399
23400     /**
23401      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23402      * query allowing the query action to be canceled if needed.
23403      * @param {String} query The SQL query to execute
23404      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23405      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23406      * saved in the current store (defaults to false)
23407      */
23408     doQuery : function(q, forceAll){
23409         if(q === undefined || q === null){
23410             q = '';
23411         }
23412         var qe = {
23413             query: q,
23414             forceAll: forceAll,
23415             combo: this,
23416             cancel:false
23417         };
23418         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23419             return false;
23420         }
23421         q = qe.query;
23422         forceAll = qe.forceAll;
23423         if(forceAll === true || (q.length >= this.minChars)){
23424             if(this.lastQuery != q || this.alwaysQuery){
23425                 this.lastQuery = q;
23426                 if(this.mode == 'local'){
23427                     this.selectedIndex = -1;
23428                     if(forceAll){
23429                         this.store.clearFilter();
23430                     }else{
23431                         this.store.filter(this.displayField, q);
23432                     }
23433                     this.onLoad();
23434                 }else{
23435                     this.store.baseParams[this.queryParam] = q;
23436                     this.store.load({
23437                         params: this.getParams(q)
23438                     });
23439                     this.expand();
23440                 }
23441             }else{
23442                 this.selectedIndex = -1;
23443                 this.onLoad();   
23444             }
23445         }
23446     },
23447
23448     // private
23449     getParams : function(q){
23450         var p = {};
23451         //p[this.queryParam] = q;
23452         if(this.pageSize){
23453             p.start = 0;
23454             p.limit = this.pageSize;
23455         }
23456         return p;
23457     },
23458
23459     /**
23460      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23461      */
23462     collapse : function(){
23463         if(!this.isExpanded()){
23464             return;
23465         }
23466         this.list.hide();
23467         Roo.get(document).un('mousedown', this.collapseIf, this);
23468         Roo.get(document).un('mousewheel', this.collapseIf, this);
23469         if (!this.editable) {
23470             Roo.get(document).un('keydown', this.listKeyPress, this);
23471         }
23472         this.fireEvent('collapse', this);
23473     },
23474
23475     // private
23476     collapseIf : function(e){
23477         if(!e.within(this.wrap) && !e.within(this.list)){
23478             this.collapse();
23479         }
23480     },
23481
23482     /**
23483      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23484      */
23485     expand : function(){
23486         if(this.isExpanded() || !this.hasFocus){
23487             return;
23488         }
23489         this.list.alignTo(this.el, this.listAlign);
23490         this.list.show();
23491         Roo.get(document).on('mousedown', this.collapseIf, this);
23492         Roo.get(document).on('mousewheel', this.collapseIf, this);
23493         if (!this.editable) {
23494             Roo.get(document).on('keydown', this.listKeyPress, this);
23495         }
23496         
23497         this.fireEvent('expand', this);
23498     },
23499
23500     // private
23501     // Implements the default empty TriggerField.onTriggerClick function
23502     onTriggerClick : function(){
23503         if(this.disabled){
23504             return;
23505         }
23506         if(this.isExpanded()){
23507             this.collapse();
23508             if (!this.blockFocus) {
23509                 this.el.focus();
23510             }
23511             
23512         }else {
23513             this.hasFocus = true;
23514             if(this.triggerAction == 'all') {
23515                 this.doQuery(this.allQuery, true);
23516             } else {
23517                 this.doQuery(this.getRawValue());
23518             }
23519             if (!this.blockFocus) {
23520                 this.el.focus();
23521             }
23522         }
23523     },
23524     listKeyPress : function(e)
23525     {
23526         //Roo.log('listkeypress');
23527         // scroll to first matching element based on key pres..
23528         if (e.isSpecialKey()) {
23529             return false;
23530         }
23531         var k = String.fromCharCode(e.getKey()).toUpperCase();
23532         //Roo.log(k);
23533         var match  = false;
23534         var csel = this.view.getSelectedNodes();
23535         var cselitem = false;
23536         if (csel.length) {
23537             var ix = this.view.indexOf(csel[0]);
23538             cselitem  = this.store.getAt(ix);
23539             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23540                 cselitem = false;
23541             }
23542             
23543         }
23544         
23545         this.store.each(function(v) { 
23546             if (cselitem) {
23547                 // start at existing selection.
23548                 if (cselitem.id == v.id) {
23549                     cselitem = false;
23550                 }
23551                 return;
23552             }
23553                 
23554             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23555                 match = this.store.indexOf(v);
23556                 return false;
23557             }
23558         }, this);
23559         
23560         if (match === false) {
23561             return true; // no more action?
23562         }
23563         // scroll to?
23564         this.view.select(match);
23565         var sn = Roo.get(this.view.getSelectedNodes()[0])
23566         sn.scrollIntoView(sn.dom.parentNode, false);
23567     }
23568
23569     /** 
23570     * @cfg {Boolean} grow 
23571     * @hide 
23572     */
23573     /** 
23574     * @cfg {Number} growMin 
23575     * @hide 
23576     */
23577     /** 
23578     * @cfg {Number} growMax 
23579     * @hide 
23580     */
23581     /**
23582      * @hide
23583      * @method autoSize
23584      */
23585 });/*
23586  * Based on:
23587  * Ext JS Library 1.1.1
23588  * Copyright(c) 2006-2007, Ext JS, LLC.
23589  *
23590  * Originally Released Under LGPL - original licence link has changed is not relivant.
23591  *
23592  * Fork - LGPL
23593  * <script type="text/javascript">
23594  */
23595 /**
23596  * @class Roo.form.Checkbox
23597  * @extends Roo.form.Field
23598  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23599  * @constructor
23600  * Creates a new Checkbox
23601  * @param {Object} config Configuration options
23602  */
23603 Roo.form.Checkbox = function(config){
23604     Roo.form.Checkbox.superclass.constructor.call(this, config);
23605     this.addEvents({
23606         /**
23607          * @event check
23608          * Fires when the checkbox is checked or unchecked.
23609              * @param {Roo.form.Checkbox} this This checkbox
23610              * @param {Boolean} checked The new checked value
23611              */
23612         check : true
23613     });
23614 };
23615
23616 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23617     /**
23618      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23619      */
23620     focusClass : undefined,
23621     /**
23622      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23623      */
23624     fieldClass: "x-form-field",
23625     /**
23626      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23627      */
23628     checked: false,
23629     /**
23630      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23631      * {tag: "input", type: "checkbox", autocomplete: "off"})
23632      */
23633     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23634     /**
23635      * @cfg {String} boxLabel The text that appears beside the checkbox
23636      */
23637     boxLabel : "",
23638     /**
23639      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23640      */  
23641     inputValue : '1',
23642     /**
23643      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23644      */
23645      valueOff: '0', // value when not checked..
23646
23647     actionMode : 'viewEl', 
23648     //
23649     // private
23650     itemCls : 'x-menu-check-item x-form-item',
23651     groupClass : 'x-menu-group-item',
23652     inputType : 'hidden',
23653     
23654     
23655     inSetChecked: false, // check that we are not calling self...
23656     
23657     inputElement: false, // real input element?
23658     basedOn: false, // ????
23659     
23660     isFormField: true, // not sure where this is needed!!!!
23661
23662     onResize : function(){
23663         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23664         if(!this.boxLabel){
23665             this.el.alignTo(this.wrap, 'c-c');
23666         }
23667     },
23668
23669     initEvents : function(){
23670         Roo.form.Checkbox.superclass.initEvents.call(this);
23671         this.el.on("click", this.onClick,  this);
23672         this.el.on("change", this.onClick,  this);
23673     },
23674
23675
23676     getResizeEl : function(){
23677         return this.wrap;
23678     },
23679
23680     getPositionEl : function(){
23681         return this.wrap;
23682     },
23683
23684     // private
23685     onRender : function(ct, position){
23686         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23687         /*
23688         if(this.inputValue !== undefined){
23689             this.el.dom.value = this.inputValue;
23690         }
23691         */
23692         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23693         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23694         var viewEl = this.wrap.createChild({ 
23695             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23696         this.viewEl = viewEl;   
23697         this.wrap.on('click', this.onClick,  this); 
23698         
23699         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23700         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23701         
23702         
23703         
23704         if(this.boxLabel){
23705             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23706         //    viewEl.on('click', this.onClick,  this); 
23707         }
23708         //if(this.checked){
23709             this.setChecked(this.checked);
23710         //}else{
23711             //this.checked = this.el.dom;
23712         //}
23713
23714     },
23715
23716     // private
23717     initValue : Roo.emptyFn,
23718
23719     /**
23720      * Returns the checked state of the checkbox.
23721      * @return {Boolean} True if checked, else false
23722      */
23723     getValue : function(){
23724         if(this.el){
23725             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23726         }
23727         return this.valueOff;
23728         
23729     },
23730
23731         // private
23732     onClick : function(){ 
23733         this.setChecked(!this.checked);
23734
23735         //if(this.el.dom.checked != this.checked){
23736         //    this.setValue(this.el.dom.checked);
23737        // }
23738     },
23739
23740     /**
23741      * Sets the checked state of the checkbox.
23742      * On is always based on a string comparison between inputValue and the param.
23743      * @param {Boolean/String} value - the value to set 
23744      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23745      */
23746     setValue : function(v,suppressEvent){
23747         
23748         
23749         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23750         //if(this.el && this.el.dom){
23751         //    this.el.dom.checked = this.checked;
23752         //    this.el.dom.defaultChecked = this.checked;
23753         //}
23754         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23755         //this.fireEvent("check", this, this.checked);
23756     },
23757     // private..
23758     setChecked : function(state,suppressEvent)
23759     {
23760         if (this.inSetChecked) {
23761             this.checked = state;
23762             return;
23763         }
23764         
23765     
23766         if(this.wrap){
23767             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23768         }
23769         this.checked = state;
23770         if(suppressEvent !== true){
23771             this.fireEvent('check', this, state);
23772         }
23773         this.inSetChecked = true;
23774         this.el.dom.value = state ? this.inputValue : this.valueOff;
23775         this.inSetChecked = false;
23776         
23777     },
23778     // handle setting of hidden value by some other method!!?!?
23779     setFromHidden: function()
23780     {
23781         if(!this.el){
23782             return;
23783         }
23784         //console.log("SET FROM HIDDEN");
23785         //alert('setFrom hidden');
23786         this.setValue(this.el.dom.value);
23787     },
23788     
23789     onDestroy : function()
23790     {
23791         if(this.viewEl){
23792             Roo.get(this.viewEl).remove();
23793         }
23794          
23795         Roo.form.Checkbox.superclass.onDestroy.call(this);
23796     }
23797
23798 });/*
23799  * Based on:
23800  * Ext JS Library 1.1.1
23801  * Copyright(c) 2006-2007, Ext JS, LLC.
23802  *
23803  * Originally Released Under LGPL - original licence link has changed is not relivant.
23804  *
23805  * Fork - LGPL
23806  * <script type="text/javascript">
23807  */
23808  
23809 /**
23810  * @class Roo.form.Radio
23811  * @extends Roo.form.Checkbox
23812  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23813  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23814  * @constructor
23815  * Creates a new Radio
23816  * @param {Object} config Configuration options
23817  */
23818 Roo.form.Radio = function(){
23819     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23820 };
23821 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23822     inputType: 'radio',
23823
23824     /**
23825      * If this radio is part of a group, it will return the selected value
23826      * @return {String}
23827      */
23828     getGroupValue : function(){
23829         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23830     }
23831 });//<script type="text/javascript">
23832
23833 /*
23834  * Ext JS Library 1.1.1
23835  * Copyright(c) 2006-2007, Ext JS, LLC.
23836  * licensing@extjs.com
23837  * 
23838  * http://www.extjs.com/license
23839  */
23840  
23841  /*
23842   * 
23843   * Known bugs:
23844   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23845   * - IE ? - no idea how much works there.
23846   * 
23847   * 
23848   * 
23849   */
23850  
23851
23852 /**
23853  * @class Ext.form.HtmlEditor
23854  * @extends Ext.form.Field
23855  * Provides a lightweight HTML Editor component.
23856  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23857  * 
23858  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23859  * supported by this editor.</b><br/><br/>
23860  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23861  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23862  */
23863 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23864       /**
23865      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23866      */
23867     toolbars : false,
23868     /**
23869      * @cfg {String} createLinkText The default text for the create link prompt
23870      */
23871     createLinkText : 'Please enter the URL for the link:',
23872     /**
23873      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23874      */
23875     defaultLinkValue : 'http:/'+'/',
23876    
23877     
23878     // id of frame..
23879     frameId: false,
23880     
23881     // private properties
23882     validationEvent : false,
23883     deferHeight: true,
23884     initialized : false,
23885     activated : false,
23886     sourceEditMode : false,
23887     onFocus : Roo.emptyFn,
23888     iframePad:3,
23889     hideMode:'offsets',
23890     defaultAutoCreate : {
23891         tag: "textarea",
23892         style:"width:500px;height:300px;",
23893         autocomplete: "off"
23894     },
23895
23896     // private
23897     initComponent : function(){
23898         this.addEvents({
23899             /**
23900              * @event initialize
23901              * Fires when the editor is fully initialized (including the iframe)
23902              * @param {HtmlEditor} this
23903              */
23904             initialize: true,
23905             /**
23906              * @event activate
23907              * Fires when the editor is first receives the focus. Any insertion must wait
23908              * until after this event.
23909              * @param {HtmlEditor} this
23910              */
23911             activate: true,
23912              /**
23913              * @event beforesync
23914              * Fires before the textarea is updated with content from the editor iframe. Return false
23915              * to cancel the sync.
23916              * @param {HtmlEditor} this
23917              * @param {String} html
23918              */
23919             beforesync: true,
23920              /**
23921              * @event beforepush
23922              * Fires before the iframe editor is updated with content from the textarea. Return false
23923              * to cancel the push.
23924              * @param {HtmlEditor} this
23925              * @param {String} html
23926              */
23927             beforepush: true,
23928              /**
23929              * @event sync
23930              * Fires when the textarea is updated with content from the editor iframe.
23931              * @param {HtmlEditor} this
23932              * @param {String} html
23933              */
23934             sync: true,
23935              /**
23936              * @event push
23937              * Fires when the iframe editor is updated with content from the textarea.
23938              * @param {HtmlEditor} this
23939              * @param {String} html
23940              */
23941             push: true,
23942              /**
23943              * @event editmodechange
23944              * Fires when the editor switches edit modes
23945              * @param {HtmlEditor} this
23946              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23947              */
23948             editmodechange: true,
23949             /**
23950              * @event editorevent
23951              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23952              * @param {HtmlEditor} this
23953              */
23954             editorevent: true
23955         })
23956     },
23957
23958     /**
23959      * Protected method that will not generally be called directly. It
23960      * is called when the editor creates its toolbar. Override this method if you need to
23961      * add custom toolbar buttons.
23962      * @param {HtmlEditor} editor
23963      */
23964     createToolbar : function(editor){
23965         if (!editor.toolbars || !editor.toolbars.length) {
23966             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23967         }
23968         
23969         for (var i =0 ; i < editor.toolbars.length;i++) {
23970             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23971             editor.toolbars[i].init(editor);
23972         }
23973          
23974         
23975     },
23976
23977     /**
23978      * Protected method that will not generally be called directly. It
23979      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23980      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23981      */
23982     getDocMarkup : function(){
23983         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
23984     },
23985
23986     // private
23987     onRender : function(ct, position){
23988         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
23989         this.el.dom.style.border = '0 none';
23990         this.el.dom.setAttribute('tabIndex', -1);
23991         this.el.addClass('x-hidden');
23992         if(Roo.isIE){ // fix IE 1px bogus margin
23993             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
23994         }
23995         this.wrap = this.el.wrap({
23996             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23997         });
23998
23999         this.frameId = Roo.id();
24000         this.createToolbar(this);
24001         
24002         
24003         
24004         
24005       
24006         
24007         var iframe = this.wrap.createChild({
24008             tag: 'iframe',
24009             id: this.frameId,
24010             name: this.frameId,
24011             frameBorder : 'no',
24012             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24013         });
24014         
24015        // console.log(iframe);
24016         //this.wrap.dom.appendChild(iframe);
24017
24018         this.iframe = iframe.dom;
24019
24020          this.assignDocWin();
24021         
24022         this.doc.designMode = 'on';
24023        
24024         this.doc.open();
24025         this.doc.write(this.getDocMarkup());
24026         this.doc.close();
24027
24028         
24029         var task = { // must defer to wait for browser to be ready
24030             run : function(){
24031                 //console.log("run task?" + this.doc.readyState);
24032                 this.assignDocWin();
24033                 if(this.doc.body || this.doc.readyState == 'complete'){
24034                     try {
24035                         this.doc.designMode="on";
24036                     } catch (e) {
24037                         return;
24038                     }
24039                     Roo.TaskMgr.stop(task);
24040                     this.initEditor.defer(10, this);
24041                 }
24042             },
24043             interval : 10,
24044             duration:10000,
24045             scope: this
24046         };
24047         Roo.TaskMgr.start(task);
24048
24049         if(!this.width){
24050             this.setSize(this.el.getSize());
24051         }
24052     },
24053
24054     // private
24055     onResize : function(w, h){
24056         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24057         if(this.el && this.iframe){
24058             if(typeof w == 'number'){
24059                 var aw = w - this.wrap.getFrameWidth('lr');
24060                 this.el.setWidth(this.adjustWidth('textarea', aw));
24061                 this.iframe.style.width = aw + 'px';
24062             }
24063             if(typeof h == 'number'){
24064                 var tbh = 0;
24065                 for (var i =0; i < this.toolbars.length;i++) {
24066                     // fixme - ask toolbars for heights?
24067                     tbh += this.toolbars[i].tb.el.getHeight();
24068                 }
24069                 
24070                 
24071                 
24072                 
24073                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24074                 this.el.setHeight(this.adjustWidth('textarea', ah));
24075                 this.iframe.style.height = ah + 'px';
24076                 if(this.doc){
24077                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24078                 }
24079             }
24080         }
24081     },
24082
24083     /**
24084      * Toggles the editor between standard and source edit mode.
24085      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24086      */
24087     toggleSourceEdit : function(sourceEditMode){
24088         
24089         this.sourceEditMode = sourceEditMode === true;
24090         
24091         if(this.sourceEditMode){
24092           
24093             this.syncValue();
24094             this.iframe.className = 'x-hidden';
24095             this.el.removeClass('x-hidden');
24096             this.el.dom.removeAttribute('tabIndex');
24097             this.el.focus();
24098         }else{
24099              
24100             this.pushValue();
24101             this.iframe.className = '';
24102             this.el.addClass('x-hidden');
24103             this.el.dom.setAttribute('tabIndex', -1);
24104             this.deferFocus();
24105         }
24106         this.setSize(this.wrap.getSize());
24107         this.fireEvent('editmodechange', this, this.sourceEditMode);
24108     },
24109
24110     // private used internally
24111     createLink : function(){
24112         var url = prompt(this.createLinkText, this.defaultLinkValue);
24113         if(url && url != 'http:/'+'/'){
24114             this.relayCmd('createlink', url);
24115         }
24116     },
24117
24118     // private (for BoxComponent)
24119     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24120
24121     // private (for BoxComponent)
24122     getResizeEl : function(){
24123         return this.wrap;
24124     },
24125
24126     // private (for BoxComponent)
24127     getPositionEl : function(){
24128         return this.wrap;
24129     },
24130
24131     // private
24132     initEvents : function(){
24133         this.originalValue = this.getValue();
24134     },
24135
24136     /**
24137      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24138      * @method
24139      */
24140     markInvalid : Roo.emptyFn,
24141     /**
24142      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24143      * @method
24144      */
24145     clearInvalid : Roo.emptyFn,
24146
24147     setValue : function(v){
24148         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24149         this.pushValue();
24150     },
24151
24152     /**
24153      * Protected method that will not generally be called directly. If you need/want
24154      * custom HTML cleanup, this is the method you should override.
24155      * @param {String} html The HTML to be cleaned
24156      * return {String} The cleaned HTML
24157      */
24158     cleanHtml : function(html){
24159         html = String(html);
24160         if(html.length > 5){
24161             if(Roo.isSafari){ // strip safari nonsense
24162                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24163             }
24164         }
24165         if(html == '&nbsp;'){
24166             html = '';
24167         }
24168         return html;
24169     },
24170
24171     /**
24172      * Protected method that will not generally be called directly. Syncs the contents
24173      * of the editor iframe with the textarea.
24174      */
24175     syncValue : function(){
24176         if(this.initialized){
24177             var bd = (this.doc.body || this.doc.documentElement);
24178             var html = bd.innerHTML;
24179             if(Roo.isSafari){
24180                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24181                 var m = bs.match(/text-align:(.*?);/i);
24182                 if(m && m[1]){
24183                     html = '<div style="'+m[0]+'">' + html + '</div>';
24184                 }
24185             }
24186             html = this.cleanHtml(html);
24187             if(this.fireEvent('beforesync', this, html) !== false){
24188                 this.el.dom.value = html;
24189                 this.fireEvent('sync', this, html);
24190             }
24191         }
24192     },
24193
24194     /**
24195      * Protected method that will not generally be called directly. Pushes the value of the textarea
24196      * into the iframe editor.
24197      */
24198     pushValue : function(){
24199         if(this.initialized){
24200             var v = this.el.dom.value;
24201             if(v.length < 1){
24202                 v = '&#160;';
24203             }
24204             if(this.fireEvent('beforepush', this, v) !== false){
24205                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24206                 this.fireEvent('push', this, v);
24207             }
24208         }
24209     },
24210
24211     // private
24212     deferFocus : function(){
24213         this.focus.defer(10, this);
24214     },
24215
24216     // doc'ed in Field
24217     focus : function(){
24218         if(this.win && !this.sourceEditMode){
24219             this.win.focus();
24220         }else{
24221             this.el.focus();
24222         }
24223     },
24224     
24225     assignDocWin: function()
24226     {
24227         var iframe = this.iframe;
24228         
24229          if(Roo.isIE){
24230             this.doc = iframe.contentWindow.document;
24231             this.win = iframe.contentWindow;
24232         } else {
24233             if (!Roo.get(this.frameId)) {
24234                 return;
24235             }
24236             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24237             this.win = Roo.get(this.frameId).dom.contentWindow;
24238         }
24239     },
24240     
24241     // private
24242     initEditor : function(){
24243         //console.log("INIT EDITOR");
24244         this.assignDocWin();
24245         
24246         
24247         
24248         this.doc.designMode="on";
24249         this.doc.open();
24250         this.doc.write(this.getDocMarkup());
24251         this.doc.close();
24252         
24253         var dbody = (this.doc.body || this.doc.documentElement);
24254         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24255         // this copies styles from the containing element into thsi one..
24256         // not sure why we need all of this..
24257         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24258         ss['background-attachment'] = 'fixed'; // w3c
24259         dbody.bgProperties = 'fixed'; // ie
24260         Roo.DomHelper.applyStyles(dbody, ss);
24261         Roo.EventManager.on(this.doc, {
24262             'mousedown': this.onEditorEvent,
24263             'dblclick': this.onEditorEvent,
24264             'click': this.onEditorEvent,
24265             'keyup': this.onEditorEvent,
24266             buffer:100,
24267             scope: this
24268         });
24269         if(Roo.isGecko){
24270             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24271         }
24272         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24273             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24274         }
24275         this.initialized = true;
24276
24277         this.fireEvent('initialize', this);
24278         this.pushValue();
24279     },
24280
24281     // private
24282     onDestroy : function(){
24283         
24284         
24285         
24286         if(this.rendered){
24287             
24288             for (var i =0; i < this.toolbars.length;i++) {
24289                 // fixme - ask toolbars for heights?
24290                 this.toolbars[i].onDestroy();
24291             }
24292             
24293             this.wrap.dom.innerHTML = '';
24294             this.wrap.remove();
24295         }
24296     },
24297
24298     // private
24299     onFirstFocus : function(){
24300         
24301         this.assignDocWin();
24302         
24303         
24304         this.activated = true;
24305         for (var i =0; i < this.toolbars.length;i++) {
24306             this.toolbars[i].onFirstFocus();
24307         }
24308        
24309         if(Roo.isGecko){ // prevent silly gecko errors
24310             this.win.focus();
24311             var s = this.win.getSelection();
24312             if(!s.focusNode || s.focusNode.nodeType != 3){
24313                 var r = s.getRangeAt(0);
24314                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24315                 r.collapse(true);
24316                 this.deferFocus();
24317             }
24318             try{
24319                 this.execCmd('useCSS', true);
24320                 this.execCmd('styleWithCSS', false);
24321             }catch(e){}
24322         }
24323         this.fireEvent('activate', this);
24324     },
24325
24326     // private
24327     adjustFont: function(btn){
24328         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24329         //if(Roo.isSafari){ // safari
24330         //    adjust *= 2;
24331        // }
24332         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24333         if(Roo.isSafari){ // safari
24334             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24335             v =  (v < 10) ? 10 : v;
24336             v =  (v > 48) ? 48 : v;
24337             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24338             
24339         }
24340         
24341         
24342         v = Math.max(1, v+adjust);
24343         
24344         this.execCmd('FontSize', v  );
24345     },
24346
24347     onEditorEvent : function(e){
24348         this.fireEvent('editorevent', this, e);
24349       //  this.updateToolbar();
24350         this.syncValue();
24351     },
24352
24353     insertTag : function(tg)
24354     {
24355         // could be a bit smarter... -> wrap the current selected tRoo..
24356         
24357         this.execCmd("formatblock",   tg);
24358         
24359     },
24360     
24361     insertText : function(txt)
24362     {
24363         
24364         
24365         range = this.createRange();
24366         range.deleteContents();
24367                //alert(Sender.getAttribute('label'));
24368                
24369         range.insertNode(this.doc.createTextNode(txt));
24370     } ,
24371     
24372     // private
24373     relayBtnCmd : function(btn){
24374         this.relayCmd(btn.cmd);
24375     },
24376
24377     /**
24378      * Executes a Midas editor command on the editor document and performs necessary focus and
24379      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24380      * @param {String} cmd The Midas command
24381      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24382      */
24383     relayCmd : function(cmd, value){
24384         this.win.focus();
24385         this.execCmd(cmd, value);
24386         this.fireEvent('editorevent', this);
24387         //this.updateToolbar();
24388         this.deferFocus();
24389     },
24390
24391     /**
24392      * Executes a Midas editor command directly on the editor document.
24393      * For visual commands, you should use {@link #relayCmd} instead.
24394      * <b>This should only be called after the editor is initialized.</b>
24395      * @param {String} cmd The Midas command
24396      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24397      */
24398     execCmd : function(cmd, value){
24399         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24400         this.syncValue();
24401     },
24402
24403    
24404     /**
24405      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24406      * to insert tRoo.
24407      * @param {String} text
24408      */
24409     insertAtCursor : function(text){
24410         if(!this.activated){
24411             return;
24412         }
24413         if(Roo.isIE){
24414             this.win.focus();
24415             var r = this.doc.selection.createRange();
24416             if(r){
24417                 r.collapse(true);
24418                 r.pasteHTML(text);
24419                 this.syncValue();
24420                 this.deferFocus();
24421             }
24422         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24423             this.win.focus();
24424             this.execCmd('InsertHTML', text);
24425             this.deferFocus();
24426         }
24427     },
24428  // private
24429     mozKeyPress : function(e){
24430         if(e.ctrlKey){
24431             var c = e.getCharCode(), cmd;
24432           
24433             if(c > 0){
24434                 c = String.fromCharCode(c).toLowerCase();
24435                 switch(c){
24436                     case 'b':
24437                         cmd = 'bold';
24438                     break;
24439                     case 'i':
24440                         cmd = 'italic';
24441                     break;
24442                     case 'u':
24443                         cmd = 'underline';
24444                     case 'v':
24445                         this.cleanUpPaste.defer(100, this);
24446                         return;
24447                     break;
24448                 }
24449                 if(cmd){
24450                     this.win.focus();
24451                     this.execCmd(cmd);
24452                     this.deferFocus();
24453                     e.preventDefault();
24454                 }
24455                 
24456             }
24457         }
24458     },
24459
24460     // private
24461     fixKeys : function(){ // load time branching for fastest keydown performance
24462         if(Roo.isIE){
24463             return function(e){
24464                 var k = e.getKey(), r;
24465                 if(k == e.TAB){
24466                     e.stopEvent();
24467                     r = this.doc.selection.createRange();
24468                     if(r){
24469                         r.collapse(true);
24470                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24471                         this.deferFocus();
24472                     }
24473                     return;
24474                 }
24475                 
24476                 if(k == e.ENTER){
24477                     r = this.doc.selection.createRange();
24478                     if(r){
24479                         var target = r.parentElement();
24480                         if(!target || target.tagName.toLowerCase() != 'li'){
24481                             e.stopEvent();
24482                             r.pasteHTML('<br />');
24483                             r.collapse(false);
24484                             r.select();
24485                         }
24486                     }
24487                 }
24488                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24489                     this.cleanUpPaste.defer(100, this);
24490                     return;
24491                 }
24492                 
24493                 
24494             };
24495         }else if(Roo.isOpera){
24496             return function(e){
24497                 var k = e.getKey();
24498                 if(k == e.TAB){
24499                     e.stopEvent();
24500                     this.win.focus();
24501                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24502                     this.deferFocus();
24503                 }
24504                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24505                     this.cleanUpPaste.defer(100, this);
24506                     return;
24507                 }
24508                 
24509             };
24510         }else if(Roo.isSafari){
24511             return function(e){
24512                 var k = e.getKey();
24513                 
24514                 if(k == e.TAB){
24515                     e.stopEvent();
24516                     this.execCmd('InsertText','\t');
24517                     this.deferFocus();
24518                     return;
24519                 }
24520                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24521                     this.cleanUpPaste.defer(100, this);
24522                     return;
24523                 }
24524                 
24525              };
24526         }
24527     }(),
24528     
24529     getAllAncestors: function()
24530     {
24531         var p = this.getSelectedNode();
24532         var a = [];
24533         if (!p) {
24534             a.push(p); // push blank onto stack..
24535             p = this.getParentElement();
24536         }
24537         
24538         
24539         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24540             a.push(p);
24541             p = p.parentNode;
24542         }
24543         a.push(this.doc.body);
24544         return a;
24545     },
24546     lastSel : false,
24547     lastSelNode : false,
24548     
24549     
24550     getSelection : function() 
24551     {
24552         this.assignDocWin();
24553         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24554     },
24555     
24556     getSelectedNode: function() 
24557     {
24558         // this may only work on Gecko!!!
24559         
24560         // should we cache this!!!!
24561         
24562         
24563         
24564          
24565         var range = this.createRange(this.getSelection());
24566         
24567         if (Roo.isIE) {
24568             var parent = range.parentElement();
24569             while (true) {
24570                 var testRange = range.duplicate();
24571                 testRange.moveToElementText(parent);
24572                 if (testRange.inRange(range)) {
24573                     break;
24574                 }
24575                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24576                     break;
24577                 }
24578                 parent = parent.parentElement;
24579             }
24580             return parent;
24581         }
24582         
24583         
24584         var ar = range.endContainer.childNodes;
24585         if (!ar.length) {
24586             ar = range.commonAncestorContainer.childNodes;
24587             //alert(ar.length);
24588         }
24589         var nodes = [];
24590         var other_nodes = [];
24591         var has_other_nodes = false;
24592         for (var i=0;i<ar.length;i++) {
24593             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24594                 continue;
24595             }
24596             // fullly contained node.
24597             
24598             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24599                 nodes.push(ar[i]);
24600                 continue;
24601             }
24602             
24603             // probably selected..
24604             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24605                 other_nodes.push(ar[i]);
24606                 continue;
24607             }
24608             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24609                 continue;
24610             }
24611             
24612             
24613             has_other_nodes = true;
24614         }
24615         if (!nodes.length && other_nodes.length) {
24616             nodes= other_nodes;
24617         }
24618         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24619             return false;
24620         }
24621         
24622         return nodes[0];
24623     },
24624     createRange: function(sel)
24625     {
24626         // this has strange effects when using with 
24627         // top toolbar - not sure if it's a great idea.
24628         //this.editor.contentWindow.focus();
24629         if (typeof sel != "undefined") {
24630             try {
24631                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24632             } catch(e) {
24633                 return this.doc.createRange();
24634             }
24635         } else {
24636             return this.doc.createRange();
24637         }
24638     },
24639     getParentElement: function()
24640     {
24641         
24642         this.assignDocWin();
24643         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24644         
24645         var range = this.createRange(sel);
24646          
24647         try {
24648             var p = range.commonAncestorContainer;
24649             while (p.nodeType == 3) { // text node
24650                 p = p.parentNode;
24651             }
24652             return p;
24653         } catch (e) {
24654             return null;
24655         }
24656     
24657     },
24658     
24659     
24660     
24661     // BC Hacks - cause I cant work out what i was trying to do..
24662     rangeIntersectsNode : function(range, node)
24663     {
24664         var nodeRange = node.ownerDocument.createRange();
24665         try {
24666             nodeRange.selectNode(node);
24667         }
24668         catch (e) {
24669             nodeRange.selectNodeContents(node);
24670         }
24671
24672         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24673                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24674     },
24675     rangeCompareNode : function(range, node) {
24676         var nodeRange = node.ownerDocument.createRange();
24677         try {
24678             nodeRange.selectNode(node);
24679         } catch (e) {
24680             nodeRange.selectNodeContents(node);
24681         }
24682         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24683         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24684
24685         if (nodeIsBefore && !nodeIsAfter)
24686             return 0;
24687         if (!nodeIsBefore && nodeIsAfter)
24688             return 1;
24689         if (nodeIsBefore && nodeIsAfter)
24690             return 2;
24691
24692         return 3;
24693     },
24694
24695     // private? - in a new class?
24696     cleanUpPaste :  function()
24697     {
24698         // cleans up the whole document..
24699       //  console.log('cleanuppaste');
24700         this.cleanUpChildren(this.doc.body)
24701         
24702         
24703     },
24704     cleanUpChildren : function (n)
24705     {
24706         if (!n.childNodes.length) {
24707             return;
24708         }
24709         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24710            this.cleanUpChild(n.childNodes[i]);
24711         }
24712     },
24713     
24714     
24715         
24716     
24717     cleanUpChild : function (node)
24718     {
24719         //console.log(node);
24720         if (node.nodeName == "#text") {
24721             // clean up silly Windows -- stuff?
24722             return; 
24723         }
24724         if (node.nodeName == "#comment") {
24725             node.parentNode.removeChild(node);
24726             // clean up silly Windows -- stuff?
24727             return; 
24728         }
24729         
24730         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24731             // remove node.
24732             node.parentNode.removeChild(node);
24733             return;
24734             
24735         }
24736         if (!node.attributes || !node.attributes.length) {
24737             this.cleanUpChildren(node);
24738             return;
24739         }
24740         
24741         function cleanAttr(n,v)
24742         {
24743             
24744             if (v.match(/^\./) || v.match(/^\//)) {
24745                 return;
24746             }
24747             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24748                 return;
24749             }
24750             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24751             node.removeAttribute(n);
24752             
24753         }
24754         
24755         function cleanStyle(n,v)
24756         {
24757             if (v.match(/expression/)) { //XSS?? should we even bother..
24758                 node.removeAttribute(n);
24759                 return;
24760             }
24761             
24762             
24763             var parts = v.split(/;/);
24764             Roo.each(parts, function(p) {
24765                 p = p.replace(/\s+/g,'');
24766                 if (!p.length) {
24767                     return;
24768                 }
24769                 var l = p.split(':').shift().replace(/\s+/g,'');
24770                 
24771                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24772                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24773                     node.removeAttribute(n);
24774                     return false;
24775                 }
24776             });
24777             
24778             
24779         }
24780         
24781         
24782         for (var i = node.attributes.length-1; i > -1 ; i--) {
24783             var a = node.attributes[i];
24784             //console.log(a);
24785             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24786                 node.removeAttribute(a.name);
24787                 return;
24788             }
24789             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24790                 cleanAttr(a.name,a.value); // fixme..
24791                 return;
24792             }
24793             if (a.name == 'style') {
24794                 cleanStyle(a.name,a.value);
24795             }
24796             /// clean up MS crap..
24797             if (a.name == 'class') {
24798                 if (a.value.match(/^Mso/)) {
24799                     node.className = '';
24800                 }
24801             }
24802             
24803             // style cleanup!?
24804             // class cleanup?
24805             
24806         }
24807         
24808         
24809         this.cleanUpChildren(node);
24810         
24811         
24812     }
24813     
24814     
24815     // hide stuff that is not compatible
24816     /**
24817      * @event blur
24818      * @hide
24819      */
24820     /**
24821      * @event change
24822      * @hide
24823      */
24824     /**
24825      * @event focus
24826      * @hide
24827      */
24828     /**
24829      * @event specialkey
24830      * @hide
24831      */
24832     /**
24833      * @cfg {String} fieldClass @hide
24834      */
24835     /**
24836      * @cfg {String} focusClass @hide
24837      */
24838     /**
24839      * @cfg {String} autoCreate @hide
24840      */
24841     /**
24842      * @cfg {String} inputType @hide
24843      */
24844     /**
24845      * @cfg {String} invalidClass @hide
24846      */
24847     /**
24848      * @cfg {String} invalidText @hide
24849      */
24850     /**
24851      * @cfg {String} msgFx @hide
24852      */
24853     /**
24854      * @cfg {String} validateOnBlur @hide
24855      */
24856 });
24857
24858 Roo.form.HtmlEditor.white = [
24859         'area', 'br', 'img', 'input', 'hr', 'wbr',
24860         
24861        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24862        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24863        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24864        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24865        'table',   'ul',         'xmp', 
24866        
24867        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24868       'thead',   'tr', 
24869      
24870       'dir', 'menu', 'ol', 'ul', 'dl',
24871        
24872       'embed',  'object'
24873 ];
24874
24875
24876 Roo.form.HtmlEditor.black = [
24877     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24878         'applet', // 
24879         'base',   'basefont', 'bgsound', 'blink',  'body', 
24880         'frame',  'frameset', 'head',    'html',   'ilayer', 
24881         'iframe', 'layer',  'link',     'meta',    'object',   
24882         'script', 'style' ,'title',  'xml' // clean later..
24883 ];
24884 Roo.form.HtmlEditor.clean = [
24885     'script', 'style', 'title', 'xml'
24886 ];
24887
24888 // attributes..
24889
24890 Roo.form.HtmlEditor.ablack = [
24891     'on'
24892 ];
24893     
24894 Roo.form.HtmlEditor.aclean = [ 
24895     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24896 ];
24897
24898 // protocols..
24899 Roo.form.HtmlEditor.pwhite= [
24900         'http',  'https',  'mailto'
24901 ];
24902
24903 Roo.form.HtmlEditor.cwhite= [
24904         'text-align',
24905         'font-size'
24906 ];
24907
24908 // <script type="text/javascript">
24909 /*
24910  * Based on
24911  * Ext JS Library 1.1.1
24912  * Copyright(c) 2006-2007, Ext JS, LLC.
24913  *  
24914  
24915  */
24916
24917 /**
24918  * @class Roo.form.HtmlEditorToolbar1
24919  * Basic Toolbar
24920  * 
24921  * Usage:
24922  *
24923  new Roo.form.HtmlEditor({
24924     ....
24925     toolbars : [
24926         new Roo.form.HtmlEditorToolbar1({
24927             disable : { fonts: 1 , format: 1, ..., ... , ...],
24928             btns : [ .... ]
24929         })
24930     }
24931      
24932  * 
24933  * @cfg {Object} disable List of elements to disable..
24934  * @cfg {Array} btns List of additional buttons.
24935  * 
24936  * 
24937  * NEEDS Extra CSS? 
24938  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24939  */
24940  
24941 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24942 {
24943     
24944     Roo.apply(this, config);
24945     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24946     // dont call parent... till later.
24947 }
24948
24949 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24950     
24951     tb: false,
24952     
24953     rendered: false,
24954     
24955     editor : false,
24956     /**
24957      * @cfg {Object} disable  List of toolbar elements to disable
24958          
24959      */
24960     disable : false,
24961       /**
24962      * @cfg {Array} fontFamilies An array of available font families
24963      */
24964     fontFamilies : [
24965         'Arial',
24966         'Courier New',
24967         'Tahoma',
24968         'Times New Roman',
24969         'Verdana'
24970     ],
24971     
24972     specialChars : [
24973            "&#169;",
24974           "&#174;",     
24975           "&#8482;",    
24976           "&#163;" ,    
24977          // "&#8212;",    
24978           "&#8230;",    
24979           "&#247;" ,    
24980         //  "&#225;" ,     ?? a acute?
24981            "&#8364;"    , //Euro
24982        //   "&#8220;"    ,
24983         //  "&#8221;"    ,
24984         //  "&#8226;"    ,
24985           "&#176;"  //   , // degrees
24986
24987          // "&#233;"     , // e ecute
24988          // "&#250;"     , // u ecute?
24989     ],
24990     inputElements : [ 
24991             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
24992             "input:submit", "input:button", "select", "textarea", "label" ],
24993     formats : [
24994         ["p"] ,  
24995         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
24996         ["pre"],[ "code"], 
24997         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
24998     ],
24999      /**
25000      * @cfg {String} defaultFont default font to use.
25001      */
25002     defaultFont: 'tahoma',
25003    
25004     fontSelect : false,
25005     
25006     
25007     formatCombo : false,
25008     
25009     init : function(editor)
25010     {
25011         this.editor = editor;
25012         
25013         
25014         var fid = editor.frameId;
25015         var etb = this;
25016         function btn(id, toggle, handler){
25017             var xid = fid + '-'+ id ;
25018             return {
25019                 id : xid,
25020                 cmd : id,
25021                 cls : 'x-btn-icon x-edit-'+id,
25022                 enableToggle:toggle !== false,
25023                 scope: editor, // was editor...
25024                 handler:handler||editor.relayBtnCmd,
25025                 clickEvent:'mousedown',
25026                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25027                 tabIndex:-1
25028             };
25029         }
25030         
25031         
25032         
25033         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25034         this.tb = tb;
25035          // stop form submits
25036         tb.el.on('click', function(e){
25037             e.preventDefault(); // what does this do?
25038         });
25039
25040         if(!this.disable.font && !Roo.isSafari){
25041             /* why no safari for fonts
25042             editor.fontSelect = tb.el.createChild({
25043                 tag:'select',
25044                 tabIndex: -1,
25045                 cls:'x-font-select',
25046                 html: editor.createFontOptions()
25047             });
25048             editor.fontSelect.on('change', function(){
25049                 var font = editor.fontSelect.dom.value;
25050                 editor.relayCmd('fontname', font);
25051                 editor.deferFocus();
25052             }, editor);
25053             tb.add(
25054                 editor.fontSelect.dom,
25055                 '-'
25056             );
25057             */
25058         };
25059         if(!this.disable.formats){
25060             this.formatCombo = new Roo.form.ComboBox({
25061                 store: new Roo.data.SimpleStore({
25062                     id : 'tag',
25063                     fields: ['tag'],
25064                     data : this.formats // from states.js
25065                 }),
25066                 blockFocus : true,
25067                 //autoCreate : {tag: "div",  size: "20"},
25068                 displayField:'tag',
25069                 typeAhead: false,
25070                 mode: 'local',
25071                 editable : false,
25072                 triggerAction: 'all',
25073                 emptyText:'Add tag',
25074                 selectOnFocus:true,
25075                 width:135,
25076                 listeners : {
25077                     'select': function(c, r, i) {
25078                         editor.insertTag(r.get('tag'));
25079                         editor.focus();
25080                     }
25081                 }
25082
25083             });
25084             tb.addField(this.formatCombo);
25085             
25086         }
25087         
25088         if(!this.disable.format){
25089             tb.add(
25090                 btn('bold'),
25091                 btn('italic'),
25092                 btn('underline')
25093             );
25094         };
25095         if(!this.disable.fontSize){
25096             tb.add(
25097                 '-',
25098                 
25099                 
25100                 btn('increasefontsize', false, editor.adjustFont),
25101                 btn('decreasefontsize', false, editor.adjustFont)
25102             );
25103         };
25104         
25105         
25106         if(this.disable.colors){
25107             tb.add(
25108                 '-', {
25109                     id:editor.frameId +'-forecolor',
25110                     cls:'x-btn-icon x-edit-forecolor',
25111                     clickEvent:'mousedown',
25112                     tooltip: this.buttonTips['forecolor'] || undefined,
25113                     tabIndex:-1,
25114                     menu : new Roo.menu.ColorMenu({
25115                         allowReselect: true,
25116                         focus: Roo.emptyFn,
25117                         value:'000000',
25118                         plain:true,
25119                         selectHandler: function(cp, color){
25120                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25121                             editor.deferFocus();
25122                         },
25123                         scope: editor,
25124                         clickEvent:'mousedown'
25125                     })
25126                 }, {
25127                     id:editor.frameId +'backcolor',
25128                     cls:'x-btn-icon x-edit-backcolor',
25129                     clickEvent:'mousedown',
25130                     tooltip: this.buttonTips['backcolor'] || undefined,
25131                     tabIndex:-1,
25132                     menu : new Roo.menu.ColorMenu({
25133                         focus: Roo.emptyFn,
25134                         value:'FFFFFF',
25135                         plain:true,
25136                         allowReselect: true,
25137                         selectHandler: function(cp, color){
25138                             if(Roo.isGecko){
25139                                 editor.execCmd('useCSS', false);
25140                                 editor.execCmd('hilitecolor', color);
25141                                 editor.execCmd('useCSS', true);
25142                                 editor.deferFocus();
25143                             }else{
25144                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25145                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25146                                 editor.deferFocus();
25147                             }
25148                         },
25149                         scope:editor,
25150                         clickEvent:'mousedown'
25151                     })
25152                 }
25153             );
25154         };
25155         // now add all the items...
25156         
25157
25158         if(!this.disable.alignments){
25159             tb.add(
25160                 '-',
25161                 btn('justifyleft'),
25162                 btn('justifycenter'),
25163                 btn('justifyright')
25164             );
25165         };
25166
25167         //if(!Roo.isSafari){
25168             if(!this.disable.links){
25169                 tb.add(
25170                     '-',
25171                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25172                 );
25173             };
25174
25175             if(!this.disable.lists){
25176                 tb.add(
25177                     '-',
25178                     btn('insertorderedlist'),
25179                     btn('insertunorderedlist')
25180                 );
25181             }
25182             if(!this.disable.sourceEdit){
25183                 tb.add(
25184                     '-',
25185                     btn('sourceedit', true, function(btn){
25186                         this.toggleSourceEdit(btn.pressed);
25187                     })
25188                 );
25189             }
25190         //}
25191         
25192         var smenu = { };
25193         // special menu.. - needs to be tidied up..
25194         if (!this.disable.special) {
25195             smenu = {
25196                 text: "&#169;",
25197                 cls: 'x-edit-none',
25198                 menu : {
25199                     items : []
25200                    }
25201             };
25202             for (var i =0; i < this.specialChars.length; i++) {
25203                 smenu.menu.items.push({
25204                     
25205                     html: this.specialChars[i],
25206                     handler: function(a,b) {
25207                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25208                         
25209                     },
25210                     tabIndex:-1
25211                 });
25212             }
25213             
25214             
25215             tb.add(smenu);
25216             
25217             
25218         }
25219         if (this.btns) {
25220             for(var i =0; i< this.btns.length;i++) {
25221                 var b = this.btns[i];
25222                 b.cls =  'x-edit-none';
25223                 b.scope = editor;
25224                 tb.add(b);
25225             }
25226         
25227         }
25228         
25229         
25230         
25231         // disable everything...
25232         
25233         this.tb.items.each(function(item){
25234            if(item.id != editor.frameId+ '-sourceedit'){
25235                 item.disable();
25236             }
25237         });
25238         this.rendered = true;
25239         
25240         // the all the btns;
25241         editor.on('editorevent', this.updateToolbar, this);
25242         // other toolbars need to implement this..
25243         //editor.on('editmodechange', this.updateToolbar, this);
25244     },
25245     
25246     
25247     
25248     /**
25249      * Protected method that will not generally be called directly. It triggers
25250      * a toolbar update by reading the markup state of the current selection in the editor.
25251      */
25252     updateToolbar: function(){
25253
25254         if(!this.editor.activated){
25255             this.editor.onFirstFocus();
25256             return;
25257         }
25258
25259         var btns = this.tb.items.map, 
25260             doc = this.editor.doc,
25261             frameId = this.editor.frameId;
25262
25263         if(!this.disable.font && !Roo.isSafari){
25264             /*
25265             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25266             if(name != this.fontSelect.dom.value){
25267                 this.fontSelect.dom.value = name;
25268             }
25269             */
25270         }
25271         if(!this.disable.format){
25272             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25273             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25274             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25275         }
25276         if(!this.disable.alignments){
25277             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25278             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25279             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25280         }
25281         if(!Roo.isSafari && !this.disable.lists){
25282             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25283             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25284         }
25285         
25286         var ans = this.editor.getAllAncestors();
25287         if (this.formatCombo) {
25288             
25289             
25290             var store = this.formatCombo.store;
25291             this.formatCombo.setValue("");
25292             for (var i =0; i < ans.length;i++) {
25293                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25294                     // select it..
25295                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25296                     break;
25297                 }
25298             }
25299         }
25300         
25301         
25302         
25303         // hides menus... - so this cant be on a menu...
25304         Roo.menu.MenuMgr.hideAll();
25305
25306         //this.editorsyncValue();
25307     },
25308    
25309     
25310     createFontOptions : function(){
25311         var buf = [], fs = this.fontFamilies, ff, lc;
25312         for(var i = 0, len = fs.length; i< len; i++){
25313             ff = fs[i];
25314             lc = ff.toLowerCase();
25315             buf.push(
25316                 '<option value="',lc,'" style="font-family:',ff,';"',
25317                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25318                     ff,
25319                 '</option>'
25320             );
25321         }
25322         return buf.join('');
25323     },
25324     
25325     toggleSourceEdit : function(sourceEditMode){
25326         if(sourceEditMode === undefined){
25327             sourceEditMode = !this.sourceEditMode;
25328         }
25329         this.sourceEditMode = sourceEditMode === true;
25330         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25331         // just toggle the button?
25332         if(btn.pressed !== this.editor.sourceEditMode){
25333             btn.toggle(this.editor.sourceEditMode);
25334             return;
25335         }
25336         
25337         if(this.sourceEditMode){
25338             this.tb.items.each(function(item){
25339                 if(item.cmd != 'sourceedit'){
25340                     item.disable();
25341                 }
25342             });
25343           
25344         }else{
25345             if(this.initialized){
25346                 this.tb.items.each(function(item){
25347                     item.enable();
25348                 });
25349             }
25350             
25351         }
25352         // tell the editor that it's been pressed..
25353         this.editor.toggleSourceEdit(sourceEditMode);
25354        
25355     },
25356      /**
25357      * Object collection of toolbar tooltips for the buttons in the editor. The key
25358      * is the command id associated with that button and the value is a valid QuickTips object.
25359      * For example:
25360 <pre><code>
25361 {
25362     bold : {
25363         title: 'Bold (Ctrl+B)',
25364         text: 'Make the selected text bold.',
25365         cls: 'x-html-editor-tip'
25366     },
25367     italic : {
25368         title: 'Italic (Ctrl+I)',
25369         text: 'Make the selected text italic.',
25370         cls: 'x-html-editor-tip'
25371     },
25372     ...
25373 </code></pre>
25374     * @type Object
25375      */
25376     buttonTips : {
25377         bold : {
25378             title: 'Bold (Ctrl+B)',
25379             text: 'Make the selected text bold.',
25380             cls: 'x-html-editor-tip'
25381         },
25382         italic : {
25383             title: 'Italic (Ctrl+I)',
25384             text: 'Make the selected text italic.',
25385             cls: 'x-html-editor-tip'
25386         },
25387         underline : {
25388             title: 'Underline (Ctrl+U)',
25389             text: 'Underline the selected text.',
25390             cls: 'x-html-editor-tip'
25391         },
25392         increasefontsize : {
25393             title: 'Grow Text',
25394             text: 'Increase the font size.',
25395             cls: 'x-html-editor-tip'
25396         },
25397         decreasefontsize : {
25398             title: 'Shrink Text',
25399             text: 'Decrease the font size.',
25400             cls: 'x-html-editor-tip'
25401         },
25402         backcolor : {
25403             title: 'Text Highlight Color',
25404             text: 'Change the background color of the selected text.',
25405             cls: 'x-html-editor-tip'
25406         },
25407         forecolor : {
25408             title: 'Font Color',
25409             text: 'Change the color of the selected text.',
25410             cls: 'x-html-editor-tip'
25411         },
25412         justifyleft : {
25413             title: 'Align Text Left',
25414             text: 'Align text to the left.',
25415             cls: 'x-html-editor-tip'
25416         },
25417         justifycenter : {
25418             title: 'Center Text',
25419             text: 'Center text in the editor.',
25420             cls: 'x-html-editor-tip'
25421         },
25422         justifyright : {
25423             title: 'Align Text Right',
25424             text: 'Align text to the right.',
25425             cls: 'x-html-editor-tip'
25426         },
25427         insertunorderedlist : {
25428             title: 'Bullet List',
25429             text: 'Start a bulleted list.',
25430             cls: 'x-html-editor-tip'
25431         },
25432         insertorderedlist : {
25433             title: 'Numbered List',
25434             text: 'Start a numbered list.',
25435             cls: 'x-html-editor-tip'
25436         },
25437         createlink : {
25438             title: 'Hyperlink',
25439             text: 'Make the selected text a hyperlink.',
25440             cls: 'x-html-editor-tip'
25441         },
25442         sourceedit : {
25443             title: 'Source Edit',
25444             text: 'Switch to source editing mode.',
25445             cls: 'x-html-editor-tip'
25446         }
25447     },
25448     // private
25449     onDestroy : function(){
25450         if(this.rendered){
25451             
25452             this.tb.items.each(function(item){
25453                 if(item.menu){
25454                     item.menu.removeAll();
25455                     if(item.menu.el){
25456                         item.menu.el.destroy();
25457                     }
25458                 }
25459                 item.destroy();
25460             });
25461              
25462         }
25463     },
25464     onFirstFocus: function() {
25465         this.tb.items.each(function(item){
25466            item.enable();
25467         });
25468     }
25469 });
25470
25471
25472
25473