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     
76 };
77
78 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
79
80     /**
81      * The id of the element associated with this object.  This is what we
82      * refer to as the "linked element" because the size and position of
83      * this element is used to determine when the drag and drop objects have
84      * interacted.
85      * @property id
86      * @type String
87      */
88     id: null,
89
90     /**
91      * Configuration attributes passed into the constructor
92      * @property config
93      * @type object
94      */
95     config: null,
96
97     /**
98      * The id of the element that will be dragged.  By default this is same
99      * as the linked element , but could be changed to another element. Ex:
100      * Roo.dd.DDProxy
101      * @property dragElId
102      * @type String
103      * @private
104      */
105     dragElId: null,
106
107     /**
108      * the id of the element that initiates the drag operation.  By default
109      * this is the linked element, but could be changed to be a child of this
110      * element.  This lets us do things like only starting the drag when the
111      * header element within the linked html element is clicked.
112      * @property handleElId
113      * @type String
114      * @private
115      */
116     handleElId: null,
117
118     /**
119      * An associative array of HTML tags that will be ignored if clicked.
120      * @property invalidHandleTypes
121      * @type {string: string}
122      */
123     invalidHandleTypes: null,
124
125     /**
126      * An associative array of ids for elements that will be ignored if clicked
127      * @property invalidHandleIds
128      * @type {string: string}
129      */
130     invalidHandleIds: null,
131
132     /**
133      * An indexted array of css class names for elements that will be ignored
134      * if clicked.
135      * @property invalidHandleClasses
136      * @type string[]
137      */
138     invalidHandleClasses: null,
139
140     /**
141      * The linked element's absolute X position at the time the drag was
142      * started
143      * @property startPageX
144      * @type int
145      * @private
146      */
147     startPageX: 0,
148
149     /**
150      * The linked element's absolute X position at the time the drag was
151      * started
152      * @property startPageY
153      * @type int
154      * @private
155      */
156     startPageY: 0,
157
158     /**
159      * The group defines a logical collection of DragDrop objects that are
160      * related.  Instances only get events when interacting with other
161      * DragDrop object in the same group.  This lets us define multiple
162      * groups using a single DragDrop subclass if we want.
163      * @property groups
164      * @type {string: string}
165      */
166     groups: null,
167
168     /**
169      * Individual drag/drop instances can be locked.  This will prevent
170      * onmousedown start drag.
171      * @property locked
172      * @type boolean
173      * @private
174      */
175     locked: false,
176
177     /**
178      * Lock this instance
179      * @method lock
180      */
181     lock: function() { this.locked = true; },
182
183     /**
184      * Unlock this instace
185      * @method unlock
186      */
187     unlock: function() { this.locked = false; },
188
189     /**
190      * By default, all insances can be a drop target.  This can be disabled by
191      * setting isTarget to false.
192      * @method isTarget
193      * @type boolean
194      */
195     isTarget: true,
196
197     /**
198      * The padding configured for this drag and drop object for calculating
199      * the drop zone intersection with this object.
200      * @method padding
201      * @type int[]
202      */
203     padding: null,
204
205     /**
206      * Cached reference to the linked element
207      * @property _domRef
208      * @private
209      */
210     _domRef: null,
211
212     /**
213      * Internal typeof flag
214      * @property __ygDragDrop
215      * @private
216      */
217     __ygDragDrop: true,
218
219     /**
220      * Set to true when horizontal contraints are applied
221      * @property constrainX
222      * @type boolean
223      * @private
224      */
225     constrainX: false,
226
227     /**
228      * Set to true when vertical contraints are applied
229      * @property constrainY
230      * @type boolean
231      * @private
232      */
233     constrainY: false,
234
235     /**
236      * The left constraint
237      * @property minX
238      * @type int
239      * @private
240      */
241     minX: 0,
242
243     /**
244      * The right constraint
245      * @property maxX
246      * @type int
247      * @private
248      */
249     maxX: 0,
250
251     /**
252      * The up constraint
253      * @property minY
254      * @type int
255      * @type int
256      * @private
257      */
258     minY: 0,
259
260     /**
261      * The down constraint
262      * @property maxY
263      * @type int
264      * @private
265      */
266     maxY: 0,
267
268     /**
269      * Maintain offsets when we resetconstraints.  Set to true when you want
270      * the position of the element relative to its parent to stay the same
271      * when the page changes
272      *
273      * @property maintainOffset
274      * @type boolean
275      */
276     maintainOffset: false,
277
278     /**
279      * Array of pixel locations the element will snap to if we specified a
280      * horizontal graduation/interval.  This array is generated automatically
281      * when you define a tick interval.
282      * @property xTicks
283      * @type int[]
284      */
285     xTicks: null,
286
287     /**
288      * Array of pixel locations the element will snap to if we specified a
289      * vertical graduation/interval.  This array is generated automatically
290      * when you define a tick interval.
291      * @property yTicks
292      * @type int[]
293      */
294     yTicks: null,
295
296     /**
297      * By default the drag and drop instance will only respond to the primary
298      * button click (left button for a right-handed mouse).  Set to true to
299      * allow drag and drop to start with any mouse click that is propogated
300      * by the browser
301      * @property primaryButtonOnly
302      * @type boolean
303      */
304     primaryButtonOnly: true,
305
306     /**
307      * The availabe property is false until the linked dom element is accessible.
308      * @property available
309      * @type boolean
310      */
311     available: false,
312
313     /**
314      * By default, drags can only be initiated if the mousedown occurs in the
315      * region the linked element is.  This is done in part to work around a
316      * bug in some browsers that mis-report the mousedown if the previous
317      * mouseup happened outside of the window.  This property is set to true
318      * if outer handles are defined.
319      *
320      * @property hasOuterHandles
321      * @type boolean
322      * @default false
323      */
324     hasOuterHandles: false,
325
326     /**
327      * Code that executes immediately before the startDrag event
328      * @method b4StartDrag
329      * @private
330      */
331     b4StartDrag: function(x, y) { },
332
333     /**
334      * Abstract method called after a drag/drop object is clicked
335      * and the drag or mousedown time thresholds have beeen met.
336      * @method startDrag
337      * @param {int} X click location
338      * @param {int} Y click location
339      */
340     startDrag: function(x, y) { /* override this */ },
341
342     /**
343      * Code that executes immediately before the onDrag event
344      * @method b4Drag
345      * @private
346      */
347     b4Drag: function(e) { },
348
349     /**
350      * Abstract method called during the onMouseMove event while dragging an
351      * object.
352      * @method onDrag
353      * @param {Event} e the mousemove event
354      */
355     onDrag: function(e) { /* override this */ },
356
357     /**
358      * Abstract method called when this element fist begins hovering over
359      * another DragDrop obj
360      * @method onDragEnter
361      * @param {Event} e the mousemove event
362      * @param {String|DragDrop[]} id In POINT mode, the element
363      * id this is hovering over.  In INTERSECT mode, an array of one or more
364      * dragdrop items being hovered over.
365      */
366     onDragEnter: function(e, id) { /* override this */ },
367
368     /**
369      * Code that executes immediately before the onDragOver event
370      * @method b4DragOver
371      * @private
372      */
373     b4DragOver: function(e) { },
374
375     /**
376      * Abstract method called when this element is hovering over another
377      * DragDrop obj
378      * @method onDragOver
379      * @param {Event} e the mousemove event
380      * @param {String|DragDrop[]} id In POINT mode, the element
381      * id this is hovering over.  In INTERSECT mode, an array of dd items
382      * being hovered over.
383      */
384     onDragOver: function(e, id) { /* override this */ },
385
386     /**
387      * Code that executes immediately before the onDragOut event
388      * @method b4DragOut
389      * @private
390      */
391     b4DragOut: function(e) { },
392
393     /**
394      * Abstract method called when we are no longer hovering over an element
395      * @method onDragOut
396      * @param {Event} e the mousemove event
397      * @param {String|DragDrop[]} id In POINT mode, the element
398      * id this was hovering over.  In INTERSECT mode, an array of dd items
399      * that the mouse is no longer over.
400      */
401     onDragOut: function(e, id) { /* override this */ },
402
403     /**
404      * Code that executes immediately before the onDragDrop event
405      * @method b4DragDrop
406      * @private
407      */
408     b4DragDrop: function(e) { },
409
410     /**
411      * Abstract method called when this item is dropped on another DragDrop
412      * obj
413      * @method onDragDrop
414      * @param {Event} e the mouseup event
415      * @param {String|DragDrop[]} id In POINT mode, the element
416      * id this was dropped on.  In INTERSECT mode, an array of dd items this
417      * was dropped on.
418      */
419     onDragDrop: function(e, id) { /* override this */ },
420
421     /**
422      * Abstract method called when this item is dropped on an area with no
423      * drop target
424      * @method onInvalidDrop
425      * @param {Event} e the mouseup event
426      */
427     onInvalidDrop: function(e) { /* override this */ },
428
429     /**
430      * Code that executes immediately before the endDrag event
431      * @method b4EndDrag
432      * @private
433      */
434     b4EndDrag: function(e) { },
435
436     /**
437      * Fired when we are done dragging the object
438      * @method endDrag
439      * @param {Event} e the mouseup event
440      */
441     endDrag: function(e) { /* override this */ },
442
443     /**
444      * Code executed immediately before the onMouseDown event
445      * @method b4MouseDown
446      * @param {Event} e the mousedown event
447      * @private
448      */
449     b4MouseDown: function(e) {  },
450
451     /**
452      * Event handler that fires when a drag/drop obj gets a mousedown
453      * @method onMouseDown
454      * @param {Event} e the mousedown event
455      */
456     onMouseDown: function(e) { /* override this */ },
457
458     /**
459      * Event handler that fires when a drag/drop obj gets a mouseup
460      * @method onMouseUp
461      * @param {Event} e the mouseup event
462      */
463     onMouseUp: function(e) { /* override this */ },
464
465     /**
466      * Override the onAvailable method to do what is needed after the initial
467      * position was determined.
468      * @method onAvailable
469      */
470     onAvailable: function () {
471     },
472
473     /*
474      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
475      * @type Object
476      */
477     defaultPadding : {left:0, right:0, top:0, bottom:0},
478
479     /*
480      * Initializes the drag drop object's constraints to restrict movement to a certain element.
481  *
482  * Usage:
483  <pre><code>
484  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
485                 { dragElId: "existingProxyDiv" });
486  dd.startDrag = function(){
487      this.constrainTo("parent-id");
488  };
489  </code></pre>
490  * Or you can initalize it using the {@link Roo.Element} object:
491  <pre><code>
492  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
493      startDrag : function(){
494          this.constrainTo("parent-id");
495      }
496  });
497  </code></pre>
498      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
499      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
500      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
501      * an object containing the sides to pad. For example: {right:10, bottom:10}
502      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
503      */
504     constrainTo : function(constrainTo, pad, inContent){
505         if(typeof pad == "number"){
506             pad = {left: pad, right:pad, top:pad, bottom:pad};
507         }
508         pad = pad || this.defaultPadding;
509         var b = Roo.get(this.getEl()).getBox();
510         var ce = Roo.get(constrainTo);
511         var s = ce.getScroll();
512         var c, cd = ce.dom;
513         if(cd == document.body){
514             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
515         }else{
516             xy = ce.getXY();
517             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
518         }
519
520
521         var topSpace = b.y - c.y;
522         var leftSpace = b.x - c.x;
523
524         this.resetConstraints();
525         this.setXConstraint(leftSpace - (pad.left||0), // left
526                 c.width - leftSpace - b.width - (pad.right||0) //right
527         );
528         this.setYConstraint(topSpace - (pad.top||0), //top
529                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
530         );
531     },
532
533     /**
534      * Returns a reference to the linked element
535      * @method getEl
536      * @return {HTMLElement} the html element
537      */
538     getEl: function() {
539         if (!this._domRef) {
540             this._domRef = Roo.getDom(this.id);
541         }
542
543         return this._domRef;
544     },
545
546     /**
547      * Returns a reference to the actual element to drag.  By default this is
548      * the same as the html element, but it can be assigned to another
549      * element. An example of this can be found in Roo.dd.DDProxy
550      * @method getDragEl
551      * @return {HTMLElement} the html element
552      */
553     getDragEl: function() {
554         return Roo.getDom(this.dragElId);
555     },
556
557     /**
558      * Sets up the DragDrop object.  Must be called in the constructor of any
559      * Roo.dd.DragDrop subclass
560      * @method init
561      * @param id the id of the linked element
562      * @param {String} sGroup the group of related items
563      * @param {object} config configuration attributes
564      */
565     init: function(id, sGroup, config) {
566         this.initTarget(id, sGroup, config);
567         Event.on(this.id, "mousedown", this.handleMouseDown, this);
568         // Event.on(this.id, "selectstart", Event.preventDefault);
569     },
570
571     /**
572      * Initializes Targeting functionality only... the object does not
573      * get a mousedown handler.
574      * @method initTarget
575      * @param id the id of the linked element
576      * @param {String} sGroup the group of related items
577      * @param {object} config configuration attributes
578      */
579     initTarget: function(id, sGroup, config) {
580
581         // configuration attributes
582         this.config = config || {};
583
584         // create a local reference to the drag and drop manager
585         this.DDM = Roo.dd.DDM;
586         // initialize the groups array
587         this.groups = {};
588
589         // assume that we have an element reference instead of an id if the
590         // parameter is not a string
591         if (typeof id !== "string") {
592             id = Roo.id(id);
593         }
594
595         // set the id
596         this.id = id;
597
598         // add to an interaction group
599         this.addToGroup((sGroup) ? sGroup : "default");
600
601         // We don't want to register this as the handle with the manager
602         // so we just set the id rather than calling the setter.
603         this.handleElId = id;
604
605         // the linked element is the element that gets dragged by default
606         this.setDragElId(id);
607
608         // by default, clicked anchors will not start drag operations.
609         this.invalidHandleTypes = { A: "A" };
610         this.invalidHandleIds = {};
611         this.invalidHandleClasses = [];
612
613         this.applyConfig();
614
615         this.handleOnAvailable();
616     },
617
618     /**
619      * Applies the configuration parameters that were passed into the constructor.
620      * This is supposed to happen at each level through the inheritance chain.  So
621      * a DDProxy implentation will execute apply config on DDProxy, DD, and
622      * DragDrop in order to get all of the parameters that are available in
623      * each object.
624      * @method applyConfig
625      */
626     applyConfig: function() {
627
628         // configurable properties:
629         //    padding, isTarget, maintainOffset, primaryButtonOnly
630         this.padding           = this.config.padding || [0, 0, 0, 0];
631         this.isTarget          = (this.config.isTarget !== false);
632         this.maintainOffset    = (this.config.maintainOffset);
633         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
634
635     },
636
637     /**
638      * Executed when the linked element is available
639      * @method handleOnAvailable
640      * @private
641      */
642     handleOnAvailable: function() {
643         this.available = true;
644         this.resetConstraints();
645         this.onAvailable();
646     },
647
648      /**
649      * Configures the padding for the target zone in px.  Effectively expands
650      * (or reduces) the virtual object size for targeting calculations.
651      * Supports css-style shorthand; if only one parameter is passed, all sides
652      * will have that padding, and if only two are passed, the top and bottom
653      * will have the first param, the left and right the second.
654      * @method setPadding
655      * @param {int} iTop    Top pad
656      * @param {int} iRight  Right pad
657      * @param {int} iBot    Bot pad
658      * @param {int} iLeft   Left pad
659      */
660     setPadding: function(iTop, iRight, iBot, iLeft) {
661         // this.padding = [iLeft, iRight, iTop, iBot];
662         if (!iRight && 0 !== iRight) {
663             this.padding = [iTop, iTop, iTop, iTop];
664         } else if (!iBot && 0 !== iBot) {
665             this.padding = [iTop, iRight, iTop, iRight];
666         } else {
667             this.padding = [iTop, iRight, iBot, iLeft];
668         }
669     },
670
671     /**
672      * Stores the initial placement of the linked element.
673      * @method setInitialPosition
674      * @param {int} diffX   the X offset, default 0
675      * @param {int} diffY   the Y offset, default 0
676      */
677     setInitPosition: function(diffX, diffY) {
678         var el = this.getEl();
679
680         if (!this.DDM.verifyEl(el)) {
681             return;
682         }
683
684         var dx = diffX || 0;
685         var dy = diffY || 0;
686
687         var p = Dom.getXY( el );
688
689         this.initPageX = p[0] - dx;
690         this.initPageY = p[1] - dy;
691
692         this.lastPageX = p[0];
693         this.lastPageY = p[1];
694
695
696         this.setStartPosition(p);
697     },
698
699     /**
700      * Sets the start position of the element.  This is set when the obj
701      * is initialized, the reset when a drag is started.
702      * @method setStartPosition
703      * @param pos current position (from previous lookup)
704      * @private
705      */
706     setStartPosition: function(pos) {
707         var p = pos || Dom.getXY( this.getEl() );
708         this.deltaSetXY = null;
709
710         this.startPageX = p[0];
711         this.startPageY = p[1];
712     },
713
714     /**
715      * Add this instance to a group of related drag/drop objects.  All
716      * instances belong to at least one group, and can belong to as many
717      * groups as needed.
718      * @method addToGroup
719      * @param sGroup {string} the name of the group
720      */
721     addToGroup: function(sGroup) {
722         this.groups[sGroup] = true;
723         this.DDM.regDragDrop(this, sGroup);
724     },
725
726     /**
727      * Remove's this instance from the supplied interaction group
728      * @method removeFromGroup
729      * @param {string}  sGroup  The group to drop
730      */
731     removeFromGroup: function(sGroup) {
732         if (this.groups[sGroup]) {
733             delete this.groups[sGroup];
734         }
735
736         this.DDM.removeDDFromGroup(this, sGroup);
737     },
738
739     /**
740      * Allows you to specify that an element other than the linked element
741      * will be moved with the cursor during a drag
742      * @method setDragElId
743      * @param id {string} the id of the element that will be used to initiate the drag
744      */
745     setDragElId: function(id) {
746         this.dragElId = id;
747     },
748
749     /**
750      * Allows you to specify a child of the linked element that should be
751      * used to initiate the drag operation.  An example of this would be if
752      * you have a content div with text and links.  Clicking anywhere in the
753      * content area would normally start the drag operation.  Use this method
754      * to specify that an element inside of the content div is the element
755      * that starts the drag operation.
756      * @method setHandleElId
757      * @param id {string} the id of the element that will be used to
758      * initiate the drag.
759      */
760     setHandleElId: function(id) {
761         if (typeof id !== "string") {
762             id = Roo.id(id);
763         }
764         this.handleElId = id;
765         this.DDM.regHandle(this.id, id);
766     },
767
768     /**
769      * Allows you to set an element outside of the linked element as a drag
770      * handle
771      * @method setOuterHandleElId
772      * @param id the id of the element that will be used to initiate the drag
773      */
774     setOuterHandleElId: function(id) {
775         if (typeof id !== "string") {
776             id = Roo.id(id);
777         }
778         Event.on(id, "mousedown",
779                 this.handleMouseDown, this);
780         this.setHandleElId(id);
781
782         this.hasOuterHandles = true;
783     },
784
785     /**
786      * Remove all drag and drop hooks for this element
787      * @method unreg
788      */
789     unreg: function() {
790         Event.un(this.id, "mousedown",
791                 this.handleMouseDown);
792         this._domRef = null;
793         this.DDM._remove(this);
794     },
795
796     destroy : function(){
797         this.unreg();
798     },
799
800     /**
801      * Returns true if this instance is locked, or the drag drop mgr is locked
802      * (meaning that all drag/drop is disabled on the page.)
803      * @method isLocked
804      * @return {boolean} true if this obj or all drag/drop is locked, else
805      * false
806      */
807     isLocked: function() {
808         return (this.DDM.isLocked() || this.locked);
809     },
810
811     /**
812      * Fired when this object is clicked
813      * @method handleMouseDown
814      * @param {Event} e
815      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
816      * @private
817      */
818     handleMouseDown: function(e, oDD){
819         if (this.primaryButtonOnly && e.button != 0) {
820             return;
821         }
822
823         if (this.isLocked()) {
824             return;
825         }
826
827         this.DDM.refreshCache(this.groups);
828
829         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
830         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
831         } else {
832             if (this.clickValidator(e)) {
833
834                 // set the initial element position
835                 this.setStartPosition();
836
837
838                 this.b4MouseDown(e);
839                 this.onMouseDown(e);
840
841                 this.DDM.handleMouseDown(e, this);
842
843                 this.DDM.stopEvent(e);
844             } else {
845
846
847             }
848         }
849     },
850
851     clickValidator: function(e) {
852         var target = e.getTarget();
853         return ( this.isValidHandleChild(target) &&
854                     (this.id == this.handleElId ||
855                         this.DDM.handleWasClicked(target, this.id)) );
856     },
857
858     /**
859      * Allows you to specify a tag name that should not start a drag operation
860      * when clicked.  This is designed to facilitate embedding links within a
861      * drag handle that do something other than start the drag.
862      * @method addInvalidHandleType
863      * @param {string} tagName the type of element to exclude
864      */
865     addInvalidHandleType: function(tagName) {
866         var type = tagName.toUpperCase();
867         this.invalidHandleTypes[type] = type;
868     },
869
870     /**
871      * Lets you to specify an element id for a child of a drag handle
872      * that should not initiate a drag
873      * @method addInvalidHandleId
874      * @param {string} id the element id of the element you wish to ignore
875      */
876     addInvalidHandleId: function(id) {
877         if (typeof id !== "string") {
878             id = Roo.id(id);
879         }
880         this.invalidHandleIds[id] = id;
881     },
882
883     /**
884      * Lets you specify a css class of elements that will not initiate a drag
885      * @method addInvalidHandleClass
886      * @param {string} cssClass the class of the elements you wish to ignore
887      */
888     addInvalidHandleClass: function(cssClass) {
889         this.invalidHandleClasses.push(cssClass);
890     },
891
892     /**
893      * Unsets an excluded tag name set by addInvalidHandleType
894      * @method removeInvalidHandleType
895      * @param {string} tagName the type of element to unexclude
896      */
897     removeInvalidHandleType: function(tagName) {
898         var type = tagName.toUpperCase();
899         // this.invalidHandleTypes[type] = null;
900         delete this.invalidHandleTypes[type];
901     },
902
903     /**
904      * Unsets an invalid handle id
905      * @method removeInvalidHandleId
906      * @param {string} id the id of the element to re-enable
907      */
908     removeInvalidHandleId: function(id) {
909         if (typeof id !== "string") {
910             id = Roo.id(id);
911         }
912         delete this.invalidHandleIds[id];
913     },
914
915     /**
916      * Unsets an invalid css class
917      * @method removeInvalidHandleClass
918      * @param {string} cssClass the class of the element(s) you wish to
919      * re-enable
920      */
921     removeInvalidHandleClass: function(cssClass) {
922         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
923             if (this.invalidHandleClasses[i] == cssClass) {
924                 delete this.invalidHandleClasses[i];
925             }
926         }
927     },
928
929     /**
930      * Checks the tag exclusion list to see if this click should be ignored
931      * @method isValidHandleChild
932      * @param {HTMLElement} node the HTMLElement to evaluate
933      * @return {boolean} true if this is a valid tag type, false if not
934      */
935     isValidHandleChild: function(node) {
936
937         var valid = true;
938         // var n = (node.nodeName == "#text") ? node.parentNode : node;
939         var nodeName;
940         try {
941             nodeName = node.nodeName.toUpperCase();
942         } catch(e) {
943             nodeName = node.nodeName;
944         }
945         valid = valid && !this.invalidHandleTypes[nodeName];
946         valid = valid && !this.invalidHandleIds[node.id];
947
948         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
949             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
950         }
951
952
953         return valid;
954
955     },
956
957     /**
958      * Create the array of horizontal tick marks if an interval was specified
959      * in setXConstraint().
960      * @method setXTicks
961      * @private
962      */
963     setXTicks: function(iStartX, iTickSize) {
964         this.xTicks = [];
965         this.xTickSize = iTickSize;
966
967         var tickMap = {};
968
969         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
970             if (!tickMap[i]) {
971                 this.xTicks[this.xTicks.length] = i;
972                 tickMap[i] = true;
973             }
974         }
975
976         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
977             if (!tickMap[i]) {
978                 this.xTicks[this.xTicks.length] = i;
979                 tickMap[i] = true;
980             }
981         }
982
983         this.xTicks.sort(this.DDM.numericSort) ;
984     },
985
986     /**
987      * Create the array of vertical tick marks if an interval was specified in
988      * setYConstraint().
989      * @method setYTicks
990      * @private
991      */
992     setYTicks: function(iStartY, iTickSize) {
993         this.yTicks = [];
994         this.yTickSize = iTickSize;
995
996         var tickMap = {};
997
998         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
999             if (!tickMap[i]) {
1000                 this.yTicks[this.yTicks.length] = i;
1001                 tickMap[i] = true;
1002             }
1003         }
1004
1005         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1006             if (!tickMap[i]) {
1007                 this.yTicks[this.yTicks.length] = i;
1008                 tickMap[i] = true;
1009             }
1010         }
1011
1012         this.yTicks.sort(this.DDM.numericSort) ;
1013     },
1014
1015     /**
1016      * By default, the element can be dragged any place on the screen.  Use
1017      * this method to limit the horizontal travel of the element.  Pass in
1018      * 0,0 for the parameters if you want to lock the drag to the y axis.
1019      * @method setXConstraint
1020      * @param {int} iLeft the number of pixels the element can move to the left
1021      * @param {int} iRight the number of pixels the element can move to the
1022      * right
1023      * @param {int} iTickSize optional parameter for specifying that the
1024      * element
1025      * should move iTickSize pixels at a time.
1026      */
1027     setXConstraint: function(iLeft, iRight, iTickSize) {
1028         this.leftConstraint = iLeft;
1029         this.rightConstraint = iRight;
1030
1031         this.minX = this.initPageX - iLeft;
1032         this.maxX = this.initPageX + iRight;
1033         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1034
1035         this.constrainX = true;
1036     },
1037
1038     /**
1039      * Clears any constraints applied to this instance.  Also clears ticks
1040      * since they can't exist independent of a constraint at this time.
1041      * @method clearConstraints
1042      */
1043     clearConstraints: function() {
1044         this.constrainX = false;
1045         this.constrainY = false;
1046         this.clearTicks();
1047     },
1048
1049     /**
1050      * Clears any tick interval defined for this instance
1051      * @method clearTicks
1052      */
1053     clearTicks: function() {
1054         this.xTicks = null;
1055         this.yTicks = null;
1056         this.xTickSize = 0;
1057         this.yTickSize = 0;
1058     },
1059
1060     /**
1061      * By default, the element can be dragged any place on the screen.  Set
1062      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1063      * parameters if you want to lock the drag to the x axis.
1064      * @method setYConstraint
1065      * @param {int} iUp the number of pixels the element can move up
1066      * @param {int} iDown the number of pixels the element can move down
1067      * @param {int} iTickSize optional parameter for specifying that the
1068      * element should move iTickSize pixels at a time.
1069      */
1070     setYConstraint: function(iUp, iDown, iTickSize) {
1071         this.topConstraint = iUp;
1072         this.bottomConstraint = iDown;
1073
1074         this.minY = this.initPageY - iUp;
1075         this.maxY = this.initPageY + iDown;
1076         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1077
1078         this.constrainY = true;
1079
1080     },
1081
1082     /**
1083      * resetConstraints must be called if you manually reposition a dd element.
1084      * @method resetConstraints
1085      * @param {boolean} maintainOffset
1086      */
1087     resetConstraints: function() {
1088
1089
1090         // Maintain offsets if necessary
1091         if (this.initPageX || this.initPageX === 0) {
1092             // figure out how much this thing has moved
1093             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1094             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1095
1096             this.setInitPosition(dx, dy);
1097
1098         // This is the first time we have detected the element's position
1099         } else {
1100             this.setInitPosition();
1101         }
1102
1103         if (this.constrainX) {
1104             this.setXConstraint( this.leftConstraint,
1105                                  this.rightConstraint,
1106                                  this.xTickSize        );
1107         }
1108
1109         if (this.constrainY) {
1110             this.setYConstraint( this.topConstraint,
1111                                  this.bottomConstraint,
1112                                  this.yTickSize         );
1113         }
1114     },
1115
1116     /**
1117      * Normally the drag element is moved pixel by pixel, but we can specify
1118      * that it move a number of pixels at a time.  This method resolves the
1119      * location when we have it set up like this.
1120      * @method getTick
1121      * @param {int} val where we want to place the object
1122      * @param {int[]} tickArray sorted array of valid points
1123      * @return {int} the closest tick
1124      * @private
1125      */
1126     getTick: function(val, tickArray) {
1127
1128         if (!tickArray) {
1129             // If tick interval is not defined, it is effectively 1 pixel,
1130             // so we return the value passed to us.
1131             return val;
1132         } else if (tickArray[0] >= val) {
1133             // The value is lower than the first tick, so we return the first
1134             // tick.
1135             return tickArray[0];
1136         } else {
1137             for (var i=0, len=tickArray.length; i<len; ++i) {
1138                 var next = i + 1;
1139                 if (tickArray[next] && tickArray[next] >= val) {
1140                     var diff1 = val - tickArray[i];
1141                     var diff2 = tickArray[next] - val;
1142                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1143                 }
1144             }
1145
1146             // The value is larger than the last tick, so we return the last
1147             // tick.
1148             return tickArray[tickArray.length - 1];
1149         }
1150     },
1151
1152     /**
1153      * toString method
1154      * @method toString
1155      * @return {string} string representation of the dd obj
1156      */
1157     toString: function() {
1158         return ("DragDrop " + this.id);
1159     }
1160
1161 });
1162
1163 })();
1164 /*
1165  * Based on:
1166  * Ext JS Library 1.1.1
1167  * Copyright(c) 2006-2007, Ext JS, LLC.
1168  *
1169  * Originally Released Under LGPL - original licence link has changed is not relivant.
1170  *
1171  * Fork - LGPL
1172  * <script type="text/javascript">
1173  */
1174
1175
1176 /**
1177  * The drag and drop utility provides a framework for building drag and drop
1178  * applications.  In addition to enabling drag and drop for specific elements,
1179  * the drag and drop elements are tracked by the manager class, and the
1180  * interactions between the various elements are tracked during the drag and
1181  * the implementing code is notified about these important moments.
1182  */
1183
1184 // Only load the library once.  Rewriting the manager class would orphan
1185 // existing drag and drop instances.
1186 if (!Roo.dd.DragDropMgr) {
1187
1188 /**
1189  * @class Roo.dd.DragDropMgr
1190  * DragDropMgr is a singleton that tracks the element interaction for
1191  * all DragDrop items in the window.  Generally, you will not call
1192  * this class directly, but it does have helper methods that could
1193  * be useful in your DragDrop implementations.
1194  * @singleton
1195  */
1196 Roo.dd.DragDropMgr = function() {
1197
1198     var Event = Roo.EventManager;
1199
1200     return {
1201
1202         /**
1203          * Two dimensional Array of registered DragDrop objects.  The first
1204          * dimension is the DragDrop item group, the second the DragDrop
1205          * object.
1206          * @property ids
1207          * @type {string: string}
1208          * @private
1209          * @static
1210          */
1211         ids: {},
1212
1213         /**
1214          * Array of element ids defined as drag handles.  Used to determine
1215          * if the element that generated the mousedown event is actually the
1216          * handle and not the html element itself.
1217          * @property handleIds
1218          * @type {string: string}
1219          * @private
1220          * @static
1221          */
1222         handleIds: {},
1223
1224         /**
1225          * the DragDrop object that is currently being dragged
1226          * @property dragCurrent
1227          * @type DragDrop
1228          * @private
1229          * @static
1230          **/
1231         dragCurrent: null,
1232
1233         /**
1234          * the DragDrop object(s) that are being hovered over
1235          * @property dragOvers
1236          * @type Array
1237          * @private
1238          * @static
1239          */
1240         dragOvers: {},
1241
1242         /**
1243          * the X distance between the cursor and the object being dragged
1244          * @property deltaX
1245          * @type int
1246          * @private
1247          * @static
1248          */
1249         deltaX: 0,
1250
1251         /**
1252          * the Y distance between the cursor and the object being dragged
1253          * @property deltaY
1254          * @type int
1255          * @private
1256          * @static
1257          */
1258         deltaY: 0,
1259
1260         /**
1261          * Flag to determine if we should prevent the default behavior of the
1262          * events we define. By default this is true, but this can be set to
1263          * false if you need the default behavior (not recommended)
1264          * @property preventDefault
1265          * @type boolean
1266          * @static
1267          */
1268         preventDefault: true,
1269
1270         /**
1271          * Flag to determine if we should stop the propagation of the events
1272          * we generate. This is true by default but you may want to set it to
1273          * false if the html element contains other features that require the
1274          * mouse click.
1275          * @property stopPropagation
1276          * @type boolean
1277          * @static
1278          */
1279         stopPropagation: true,
1280
1281         /**
1282          * Internal flag that is set to true when drag and drop has been
1283          * intialized
1284          * @property initialized
1285          * @private
1286          * @static
1287          */
1288         initalized: false,
1289
1290         /**
1291          * All drag and drop can be disabled.
1292          * @property locked
1293          * @private
1294          * @static
1295          */
1296         locked: false,
1297
1298         /**
1299          * Called the first time an element is registered.
1300          * @method init
1301          * @private
1302          * @static
1303          */
1304         init: function() {
1305             this.initialized = true;
1306         },
1307
1308         /**
1309          * In point mode, drag and drop interaction is defined by the
1310          * location of the cursor during the drag/drop
1311          * @property POINT
1312          * @type int
1313          * @static
1314          */
1315         POINT: 0,
1316
1317         /**
1318          * In intersect mode, drag and drop interactio nis defined by the
1319          * overlap of two or more drag and drop objects.
1320          * @property INTERSECT
1321          * @type int
1322          * @static
1323          */
1324         INTERSECT: 1,
1325
1326         /**
1327          * The current drag and drop mode.  Default: POINT
1328          * @property mode
1329          * @type int
1330          * @static
1331          */
1332         mode: 0,
1333
1334         /**
1335          * Runs method on all drag and drop objects
1336          * @method _execOnAll
1337          * @private
1338          * @static
1339          */
1340         _execOnAll: function(sMethod, args) {
1341             for (var i in this.ids) {
1342                 for (var j in this.ids[i]) {
1343                     var oDD = this.ids[i][j];
1344                     if (! this.isTypeOfDD(oDD)) {
1345                         continue;
1346                     }
1347                     oDD[sMethod].apply(oDD, args);
1348                 }
1349             }
1350         },
1351
1352         /**
1353          * Drag and drop initialization.  Sets up the global event handlers
1354          * @method _onLoad
1355          * @private
1356          * @static
1357          */
1358         _onLoad: function() {
1359
1360             this.init();
1361
1362
1363             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1364             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1365             Event.on(window,   "unload",    this._onUnload, this, true);
1366             Event.on(window,   "resize",    this._onResize, this, true);
1367             // Event.on(window,   "mouseout",    this._test);
1368
1369         },
1370
1371         /**
1372          * Reset constraints on all drag and drop objs
1373          * @method _onResize
1374          * @private
1375          * @static
1376          */
1377         _onResize: function(e) {
1378             this._execOnAll("resetConstraints", []);
1379         },
1380
1381         /**
1382          * Lock all drag and drop functionality
1383          * @method lock
1384          * @static
1385          */
1386         lock: function() { this.locked = true; },
1387
1388         /**
1389          * Unlock all drag and drop functionality
1390          * @method unlock
1391          * @static
1392          */
1393         unlock: function() { this.locked = false; },
1394
1395         /**
1396          * Is drag and drop locked?
1397          * @method isLocked
1398          * @return {boolean} True if drag and drop is locked, false otherwise.
1399          * @static
1400          */
1401         isLocked: function() { return this.locked; },
1402
1403         /**
1404          * Location cache that is set for all drag drop objects when a drag is
1405          * initiated, cleared when the drag is finished.
1406          * @property locationCache
1407          * @private
1408          * @static
1409          */
1410         locationCache: {},
1411
1412         /**
1413          * Set useCache to false if you want to force object the lookup of each
1414          * drag and drop linked element constantly during a drag.
1415          * @property useCache
1416          * @type boolean
1417          * @static
1418          */
1419         useCache: true,
1420
1421         /**
1422          * The number of pixels that the mouse needs to move after the
1423          * mousedown before the drag is initiated.  Default=3;
1424          * @property clickPixelThresh
1425          * @type int
1426          * @static
1427          */
1428         clickPixelThresh: 3,
1429
1430         /**
1431          * The number of milliseconds after the mousedown event to initiate the
1432          * drag if we don't get a mouseup event. Default=1000
1433          * @property clickTimeThresh
1434          * @type int
1435          * @static
1436          */
1437         clickTimeThresh: 350,
1438
1439         /**
1440          * Flag that indicates that either the drag pixel threshold or the
1441          * mousdown time threshold has been met
1442          * @property dragThreshMet
1443          * @type boolean
1444          * @private
1445          * @static
1446          */
1447         dragThreshMet: false,
1448
1449         /**
1450          * Timeout used for the click time threshold
1451          * @property clickTimeout
1452          * @type Object
1453          * @private
1454          * @static
1455          */
1456         clickTimeout: null,
1457
1458         /**
1459          * The X position of the mousedown event stored for later use when a
1460          * drag threshold is met.
1461          * @property startX
1462          * @type int
1463          * @private
1464          * @static
1465          */
1466         startX: 0,
1467
1468         /**
1469          * The Y position of the mousedown event stored for later use when a
1470          * drag threshold is met.
1471          * @property startY
1472          * @type int
1473          * @private
1474          * @static
1475          */
1476         startY: 0,
1477
1478         /**
1479          * Each DragDrop instance must be registered with the DragDropMgr.
1480          * This is executed in DragDrop.init()
1481          * @method regDragDrop
1482          * @param {DragDrop} oDD the DragDrop object to register
1483          * @param {String} sGroup the name of the group this element belongs to
1484          * @static
1485          */
1486         regDragDrop: function(oDD, sGroup) {
1487             if (!this.initialized) { this.init(); }
1488
1489             if (!this.ids[sGroup]) {
1490                 this.ids[sGroup] = {};
1491             }
1492             this.ids[sGroup][oDD.id] = oDD;
1493         },
1494
1495         /**
1496          * Removes the supplied dd instance from the supplied group. Executed
1497          * by DragDrop.removeFromGroup, so don't call this function directly.
1498          * @method removeDDFromGroup
1499          * @private
1500          * @static
1501          */
1502         removeDDFromGroup: function(oDD, sGroup) {
1503             if (!this.ids[sGroup]) {
1504                 this.ids[sGroup] = {};
1505             }
1506
1507             var obj = this.ids[sGroup];
1508             if (obj && obj[oDD.id]) {
1509                 delete obj[oDD.id];
1510             }
1511         },
1512
1513         /**
1514          * Unregisters a drag and drop item.  This is executed in
1515          * DragDrop.unreg, use that method instead of calling this directly.
1516          * @method _remove
1517          * @private
1518          * @static
1519          */
1520         _remove: function(oDD) {
1521             for (var g in oDD.groups) {
1522                 if (g && this.ids[g][oDD.id]) {
1523                     delete this.ids[g][oDD.id];
1524                 }
1525             }
1526             delete this.handleIds[oDD.id];
1527         },
1528
1529         /**
1530          * Each DragDrop handle element must be registered.  This is done
1531          * automatically when executing DragDrop.setHandleElId()
1532          * @method regHandle
1533          * @param {String} sDDId the DragDrop id this element is a handle for
1534          * @param {String} sHandleId the id of the element that is the drag
1535          * handle
1536          * @static
1537          */
1538         regHandle: function(sDDId, sHandleId) {
1539             if (!this.handleIds[sDDId]) {
1540                 this.handleIds[sDDId] = {};
1541             }
1542             this.handleIds[sDDId][sHandleId] = sHandleId;
1543         },
1544
1545         /**
1546          * Utility function to determine if a given element has been
1547          * registered as a drag drop item.
1548          * @method isDragDrop
1549          * @param {String} id the element id to check
1550          * @return {boolean} true if this element is a DragDrop item,
1551          * false otherwise
1552          * @static
1553          */
1554         isDragDrop: function(id) {
1555             return ( this.getDDById(id) ) ? true : false;
1556         },
1557
1558         /**
1559          * Returns the drag and drop instances that are in all groups the
1560          * passed in instance belongs to.
1561          * @method getRelated
1562          * @param {DragDrop} p_oDD the obj to get related data for
1563          * @param {boolean} bTargetsOnly if true, only return targetable objs
1564          * @return {DragDrop[]} the related instances
1565          * @static
1566          */
1567         getRelated: function(p_oDD, bTargetsOnly) {
1568             var oDDs = [];
1569             for (var i in p_oDD.groups) {
1570                 for (j in this.ids[i]) {
1571                     var dd = this.ids[i][j];
1572                     if (! this.isTypeOfDD(dd)) {
1573                         continue;
1574                     }
1575                     if (!bTargetsOnly || dd.isTarget) {
1576                         oDDs[oDDs.length] = dd;
1577                     }
1578                 }
1579             }
1580
1581             return oDDs;
1582         },
1583
1584         /**
1585          * Returns true if the specified dd target is a legal target for
1586          * the specifice drag obj
1587          * @method isLegalTarget
1588          * @param {DragDrop} the drag obj
1589          * @param {DragDrop} the target
1590          * @return {boolean} true if the target is a legal target for the
1591          * dd obj
1592          * @static
1593          */
1594         isLegalTarget: function (oDD, oTargetDD) {
1595             var targets = this.getRelated(oDD, true);
1596             for (var i=0, len=targets.length;i<len;++i) {
1597                 if (targets[i].id == oTargetDD.id) {
1598                     return true;
1599                 }
1600             }
1601
1602             return false;
1603         },
1604
1605         /**
1606          * My goal is to be able to transparently determine if an object is
1607          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1608          * returns "object", oDD.constructor.toString() always returns
1609          * "DragDrop" and not the name of the subclass.  So for now it just
1610          * evaluates a well-known variable in DragDrop.
1611          * @method isTypeOfDD
1612          * @param {Object} the object to evaluate
1613          * @return {boolean} true if typeof oDD = DragDrop
1614          * @static
1615          */
1616         isTypeOfDD: function (oDD) {
1617             return (oDD && oDD.__ygDragDrop);
1618         },
1619
1620         /**
1621          * Utility function to determine if a given element has been
1622          * registered as a drag drop handle for the given Drag Drop object.
1623          * @method isHandle
1624          * @param {String} id the element id to check
1625          * @return {boolean} true if this element is a DragDrop handle, false
1626          * otherwise
1627          * @static
1628          */
1629         isHandle: function(sDDId, sHandleId) {
1630             return ( this.handleIds[sDDId] &&
1631                             this.handleIds[sDDId][sHandleId] );
1632         },
1633
1634         /**
1635          * Returns the DragDrop instance for a given id
1636          * @method getDDById
1637          * @param {String} id the id of the DragDrop object
1638          * @return {DragDrop} the drag drop object, null if it is not found
1639          * @static
1640          */
1641         getDDById: function(id) {
1642             for (var i in this.ids) {
1643                 if (this.ids[i][id]) {
1644                     return this.ids[i][id];
1645                 }
1646             }
1647             return null;
1648         },
1649
1650         /**
1651          * Fired after a registered DragDrop object gets the mousedown event.
1652          * Sets up the events required to track the object being dragged
1653          * @method handleMouseDown
1654          * @param {Event} e the event
1655          * @param oDD the DragDrop object being dragged
1656          * @private
1657          * @static
1658          */
1659         handleMouseDown: function(e, oDD) {
1660             if(Roo.QuickTips){
1661                 Roo.QuickTips.disable();
1662             }
1663             this.currentTarget = e.getTarget();
1664
1665             this.dragCurrent = oDD;
1666
1667             var el = oDD.getEl();
1668
1669             // track start position
1670             this.startX = e.getPageX();
1671             this.startY = e.getPageY();
1672
1673             this.deltaX = this.startX - el.offsetLeft;
1674             this.deltaY = this.startY - el.offsetTop;
1675
1676             this.dragThreshMet = false;
1677
1678             this.clickTimeout = setTimeout(
1679                     function() {
1680                         var DDM = Roo.dd.DDM;
1681                         DDM.startDrag(DDM.startX, DDM.startY);
1682                     },
1683                     this.clickTimeThresh );
1684         },
1685
1686         /**
1687          * Fired when either the drag pixel threshol or the mousedown hold
1688          * time threshold has been met.
1689          * @method startDrag
1690          * @param x {int} the X position of the original mousedown
1691          * @param y {int} the Y position of the original mousedown
1692          * @static
1693          */
1694         startDrag: function(x, y) {
1695             clearTimeout(this.clickTimeout);
1696             if (this.dragCurrent) {
1697                 this.dragCurrent.b4StartDrag(x, y);
1698                 this.dragCurrent.startDrag(x, y);
1699             }
1700             this.dragThreshMet = true;
1701         },
1702
1703         /**
1704          * Internal function to handle the mouseup event.  Will be invoked
1705          * from the context of the document.
1706          * @method handleMouseUp
1707          * @param {Event} e the event
1708          * @private
1709          * @static
1710          */
1711         handleMouseUp: function(e) {
1712
1713             if(Roo.QuickTips){
1714                 Roo.QuickTips.enable();
1715             }
1716             if (! this.dragCurrent) {
1717                 return;
1718             }
1719
1720             clearTimeout(this.clickTimeout);
1721
1722             if (this.dragThreshMet) {
1723                 this.fireEvents(e, true);
1724             } else {
1725             }
1726
1727             this.stopDrag(e);
1728
1729             this.stopEvent(e);
1730         },
1731
1732         /**
1733          * Utility to stop event propagation and event default, if these
1734          * features are turned on.
1735          * @method stopEvent
1736          * @param {Event} e the event as returned by this.getEvent()
1737          * @static
1738          */
1739         stopEvent: function(e){
1740             if(this.stopPropagation) {
1741                 e.stopPropagation();
1742             }
1743
1744             if (this.preventDefault) {
1745                 e.preventDefault();
1746             }
1747         },
1748
1749         /**
1750          * Internal function to clean up event handlers after the drag
1751          * operation is complete
1752          * @method stopDrag
1753          * @param {Event} e the event
1754          * @private
1755          * @static
1756          */
1757         stopDrag: function(e) {
1758             // Fire the drag end event for the item that was dragged
1759             if (this.dragCurrent) {
1760                 if (this.dragThreshMet) {
1761                     this.dragCurrent.b4EndDrag(e);
1762                     this.dragCurrent.endDrag(e);
1763                 }
1764
1765                 this.dragCurrent.onMouseUp(e);
1766             }
1767
1768             this.dragCurrent = null;
1769             this.dragOvers = {};
1770         },
1771
1772         /**
1773          * Internal function to handle the mousemove event.  Will be invoked
1774          * from the context of the html element.
1775          *
1776          * @TODO figure out what we can do about mouse events lost when the
1777          * user drags objects beyond the window boundary.  Currently we can
1778          * detect this in internet explorer by verifying that the mouse is
1779          * down during the mousemove event.  Firefox doesn't give us the
1780          * button state on the mousemove event.
1781          * @method handleMouseMove
1782          * @param {Event} e the event
1783          * @private
1784          * @static
1785          */
1786         handleMouseMove: function(e) {
1787             if (! this.dragCurrent) {
1788                 return true;
1789             }
1790
1791             // var button = e.which || e.button;
1792
1793             // check for IE mouseup outside of page boundary
1794             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1795                 this.stopEvent(e);
1796                 return this.handleMouseUp(e);
1797             }
1798
1799             if (!this.dragThreshMet) {
1800                 var diffX = Math.abs(this.startX - e.getPageX());
1801                 var diffY = Math.abs(this.startY - e.getPageY());
1802                 if (diffX > this.clickPixelThresh ||
1803                             diffY > this.clickPixelThresh) {
1804                     this.startDrag(this.startX, this.startY);
1805                 }
1806             }
1807
1808             if (this.dragThreshMet) {
1809                 this.dragCurrent.b4Drag(e);
1810                 this.dragCurrent.onDrag(e);
1811                 if(!this.dragCurrent.moveOnly){
1812                     this.fireEvents(e, false);
1813                 }
1814             }
1815
1816             this.stopEvent(e);
1817
1818             return true;
1819         },
1820
1821         /**
1822          * Iterates over all of the DragDrop elements to find ones we are
1823          * hovering over or dropping on
1824          * @method fireEvents
1825          * @param {Event} e the event
1826          * @param {boolean} isDrop is this a drop op or a mouseover op?
1827          * @private
1828          * @static
1829          */
1830         fireEvents: function(e, isDrop) {
1831             var dc = this.dragCurrent;
1832
1833             // If the user did the mouse up outside of the window, we could
1834             // get here even though we have ended the drag.
1835             if (!dc || dc.isLocked()) {
1836                 return;
1837             }
1838
1839             var pt = e.getPoint();
1840
1841             // cache the previous dragOver array
1842             var oldOvers = [];
1843
1844             var outEvts   = [];
1845             var overEvts  = [];
1846             var dropEvts  = [];
1847             var enterEvts = [];
1848
1849             // Check to see if the object(s) we were hovering over is no longer
1850             // being hovered over so we can fire the onDragOut event
1851             for (var i in this.dragOvers) {
1852
1853                 var ddo = this.dragOvers[i];
1854
1855                 if (! this.isTypeOfDD(ddo)) {
1856                     continue;
1857                 }
1858
1859                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1860                     outEvts.push( ddo );
1861                 }
1862
1863                 oldOvers[i] = true;
1864                 delete this.dragOvers[i];
1865             }
1866
1867             for (var sGroup in dc.groups) {
1868
1869                 if ("string" != typeof sGroup) {
1870                     continue;
1871                 }
1872
1873                 for (i in this.ids[sGroup]) {
1874                     var oDD = this.ids[sGroup][i];
1875                     if (! this.isTypeOfDD(oDD)) {
1876                         continue;
1877                     }
1878
1879                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1880                         if (this.isOverTarget(pt, oDD, this.mode)) {
1881                             // look for drop interactions
1882                             if (isDrop) {
1883                                 dropEvts.push( oDD );
1884                             // look for drag enter and drag over interactions
1885                             } else {
1886
1887                                 // initial drag over: dragEnter fires
1888                                 if (!oldOvers[oDD.id]) {
1889                                     enterEvts.push( oDD );
1890                                 // subsequent drag overs: dragOver fires
1891                                 } else {
1892                                     overEvts.push( oDD );
1893                                 }
1894
1895                                 this.dragOvers[oDD.id] = oDD;
1896                             }
1897                         }
1898                     }
1899                 }
1900             }
1901
1902             if (this.mode) {
1903                 if (outEvts.length) {
1904                     dc.b4DragOut(e, outEvts);
1905                     dc.onDragOut(e, outEvts);
1906                 }
1907
1908                 if (enterEvts.length) {
1909                     dc.onDragEnter(e, enterEvts);
1910                 }
1911
1912                 if (overEvts.length) {
1913                     dc.b4DragOver(e, overEvts);
1914                     dc.onDragOver(e, overEvts);
1915                 }
1916
1917                 if (dropEvts.length) {
1918                     dc.b4DragDrop(e, dropEvts);
1919                     dc.onDragDrop(e, dropEvts);
1920                 }
1921
1922             } else {
1923                 // fire dragout events
1924                 var len = 0;
1925                 for (i=0, len=outEvts.length; i<len; ++i) {
1926                     dc.b4DragOut(e, outEvts[i].id);
1927                     dc.onDragOut(e, outEvts[i].id);
1928                 }
1929
1930                 // fire enter events
1931                 for (i=0,len=enterEvts.length; i<len; ++i) {
1932                     // dc.b4DragEnter(e, oDD.id);
1933                     dc.onDragEnter(e, enterEvts[i].id);
1934                 }
1935
1936                 // fire over events
1937                 for (i=0,len=overEvts.length; i<len; ++i) {
1938                     dc.b4DragOver(e, overEvts[i].id);
1939                     dc.onDragOver(e, overEvts[i].id);
1940                 }
1941
1942                 // fire drop events
1943                 for (i=0, len=dropEvts.length; i<len; ++i) {
1944                     dc.b4DragDrop(e, dropEvts[i].id);
1945                     dc.onDragDrop(e, dropEvts[i].id);
1946                 }
1947
1948             }
1949
1950             // notify about a drop that did not find a target
1951             if (isDrop && !dropEvts.length) {
1952                 dc.onInvalidDrop(e);
1953             }
1954
1955         },
1956
1957         /**
1958          * Helper function for getting the best match from the list of drag
1959          * and drop objects returned by the drag and drop events when we are
1960          * in INTERSECT mode.  It returns either the first object that the
1961          * cursor is over, or the object that has the greatest overlap with
1962          * the dragged element.
1963          * @method getBestMatch
1964          * @param  {DragDrop[]} dds The array of drag and drop objects
1965          * targeted
1966          * @return {DragDrop}       The best single match
1967          * @static
1968          */
1969         getBestMatch: function(dds) {
1970             var winner = null;
1971             // Return null if the input is not what we expect
1972             //if (!dds || !dds.length || dds.length == 0) {
1973                // winner = null;
1974             // If there is only one item, it wins
1975             //} else if (dds.length == 1) {
1976
1977             var len = dds.length;
1978
1979             if (len == 1) {
1980                 winner = dds[0];
1981             } else {
1982                 // Loop through the targeted items
1983                 for (var i=0; i<len; ++i) {
1984                     var dd = dds[i];
1985                     // If the cursor is over the object, it wins.  If the
1986                     // cursor is over multiple matches, the first one we come
1987                     // to wins.
1988                     if (dd.cursorIsOver) {
1989                         winner = dd;
1990                         break;
1991                     // Otherwise the object with the most overlap wins
1992                     } else {
1993                         if (!winner ||
1994                             winner.overlap.getArea() < dd.overlap.getArea()) {
1995                             winner = dd;
1996                         }
1997                     }
1998                 }
1999             }
2000
2001             return winner;
2002         },
2003
2004         /**
2005          * Refreshes the cache of the top-left and bottom-right points of the
2006          * drag and drop objects in the specified group(s).  This is in the
2007          * format that is stored in the drag and drop instance, so typical
2008          * usage is:
2009          * <code>
2010          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2011          * </code>
2012          * Alternatively:
2013          * <code>
2014          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2015          * </code>
2016          * @TODO this really should be an indexed array.  Alternatively this
2017          * method could accept both.
2018          * @method refreshCache
2019          * @param {Object} groups an associative array of groups to refresh
2020          * @static
2021          */
2022         refreshCache: function(groups) {
2023             for (var sGroup in groups) {
2024                 if ("string" != typeof sGroup) {
2025                     continue;
2026                 }
2027                 for (var i in this.ids[sGroup]) {
2028                     var oDD = this.ids[sGroup][i];
2029
2030                     if (this.isTypeOfDD(oDD)) {
2031                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2032                         var loc = this.getLocation(oDD);
2033                         if (loc) {
2034                             this.locationCache[oDD.id] = loc;
2035                         } else {
2036                             delete this.locationCache[oDD.id];
2037                             // this will unregister the drag and drop object if
2038                             // the element is not in a usable state
2039                             // oDD.unreg();
2040                         }
2041                     }
2042                 }
2043             }
2044         },
2045
2046         /**
2047          * This checks to make sure an element exists and is in the DOM.  The
2048          * main purpose is to handle cases where innerHTML is used to remove
2049          * drag and drop objects from the DOM.  IE provides an 'unspecified
2050          * error' when trying to access the offsetParent of such an element
2051          * @method verifyEl
2052          * @param {HTMLElement} el the element to check
2053          * @return {boolean} true if the element looks usable
2054          * @static
2055          */
2056         verifyEl: function(el) {
2057             if (el) {
2058                 var parent;
2059                 if(Roo.isIE){
2060                     try{
2061                         parent = el.offsetParent;
2062                     }catch(e){}
2063                 }else{
2064                     parent = el.offsetParent;
2065                 }
2066                 if (parent) {
2067                     return true;
2068                 }
2069             }
2070
2071             return false;
2072         },
2073
2074         /**
2075          * Returns a Region object containing the drag and drop element's position
2076          * and size, including the padding configured for it
2077          * @method getLocation
2078          * @param {DragDrop} oDD the drag and drop object to get the
2079          *                       location for
2080          * @return {Roo.lib.Region} a Region object representing the total area
2081          *                             the element occupies, including any padding
2082          *                             the instance is configured for.
2083          * @static
2084          */
2085         getLocation: function(oDD) {
2086             if (! this.isTypeOfDD(oDD)) {
2087                 return null;
2088             }
2089
2090             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2091
2092             try {
2093                 pos= Roo.lib.Dom.getXY(el);
2094             } catch (e) { }
2095
2096             if (!pos) {
2097                 return null;
2098             }
2099
2100             x1 = pos[0];
2101             x2 = x1 + el.offsetWidth;
2102             y1 = pos[1];
2103             y2 = y1 + el.offsetHeight;
2104
2105             t = y1 - oDD.padding[0];
2106             r = x2 + oDD.padding[1];
2107             b = y2 + oDD.padding[2];
2108             l = x1 - oDD.padding[3];
2109
2110             return new Roo.lib.Region( t, r, b, l );
2111         },
2112
2113         /**
2114          * Checks the cursor location to see if it over the target
2115          * @method isOverTarget
2116          * @param {Roo.lib.Point} pt The point to evaluate
2117          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2118          * @return {boolean} true if the mouse is over the target
2119          * @private
2120          * @static
2121          */
2122         isOverTarget: function(pt, oTarget, intersect) {
2123             // use cache if available
2124             var loc = this.locationCache[oTarget.id];
2125             if (!loc || !this.useCache) {
2126                 loc = this.getLocation(oTarget);
2127                 this.locationCache[oTarget.id] = loc;
2128
2129             }
2130
2131             if (!loc) {
2132                 return false;
2133             }
2134
2135             oTarget.cursorIsOver = loc.contains( pt );
2136
2137             // DragDrop is using this as a sanity check for the initial mousedown
2138             // in this case we are done.  In POINT mode, if the drag obj has no
2139             // contraints, we are also done. Otherwise we need to evaluate the
2140             // location of the target as related to the actual location of the
2141             // dragged element.
2142             var dc = this.dragCurrent;
2143             if (!dc || !dc.getTargetCoord ||
2144                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2145                 return oTarget.cursorIsOver;
2146             }
2147
2148             oTarget.overlap = null;
2149
2150             // Get the current location of the drag element, this is the
2151             // location of the mouse event less the delta that represents
2152             // where the original mousedown happened on the element.  We
2153             // need to consider constraints and ticks as well.
2154             var pos = dc.getTargetCoord(pt.x, pt.y);
2155
2156             var el = dc.getDragEl();
2157             var curRegion = new Roo.lib.Region( pos.y,
2158                                                    pos.x + el.offsetWidth,
2159                                                    pos.y + el.offsetHeight,
2160                                                    pos.x );
2161
2162             var overlap = curRegion.intersect(loc);
2163
2164             if (overlap) {
2165                 oTarget.overlap = overlap;
2166                 return (intersect) ? true : oTarget.cursorIsOver;
2167             } else {
2168                 return false;
2169             }
2170         },
2171
2172         /**
2173          * unload event handler
2174          * @method _onUnload
2175          * @private
2176          * @static
2177          */
2178         _onUnload: function(e, me) {
2179             Roo.dd.DragDropMgr.unregAll();
2180         },
2181
2182         /**
2183          * Cleans up the drag and drop events and objects.
2184          * @method unregAll
2185          * @private
2186          * @static
2187          */
2188         unregAll: function() {
2189
2190             if (this.dragCurrent) {
2191                 this.stopDrag();
2192                 this.dragCurrent = null;
2193             }
2194
2195             this._execOnAll("unreg", []);
2196
2197             for (i in this.elementCache) {
2198                 delete this.elementCache[i];
2199             }
2200
2201             this.elementCache = {};
2202             this.ids = {};
2203         },
2204
2205         /**
2206          * A cache of DOM elements
2207          * @property elementCache
2208          * @private
2209          * @static
2210          */
2211         elementCache: {},
2212
2213         /**
2214          * Get the wrapper for the DOM element specified
2215          * @method getElWrapper
2216          * @param {String} id the id of the element to get
2217          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2218          * @private
2219          * @deprecated This wrapper isn't that useful
2220          * @static
2221          */
2222         getElWrapper: function(id) {
2223             var oWrapper = this.elementCache[id];
2224             if (!oWrapper || !oWrapper.el) {
2225                 oWrapper = this.elementCache[id] =
2226                     new this.ElementWrapper(Roo.getDom(id));
2227             }
2228             return oWrapper;
2229         },
2230
2231         /**
2232          * Returns the actual DOM element
2233          * @method getElement
2234          * @param {String} id the id of the elment to get
2235          * @return {Object} The element
2236          * @deprecated use Roo.getDom instead
2237          * @static
2238          */
2239         getElement: function(id) {
2240             return Roo.getDom(id);
2241         },
2242
2243         /**
2244          * Returns the style property for the DOM element (i.e.,
2245          * document.getElById(id).style)
2246          * @method getCss
2247          * @param {String} id the id of the elment to get
2248          * @return {Object} The style property of the element
2249          * @deprecated use Roo.getDom instead
2250          * @static
2251          */
2252         getCss: function(id) {
2253             var el = Roo.getDom(id);
2254             return (el) ? el.style : null;
2255         },
2256
2257         /**
2258          * Inner class for cached elements
2259          * @class DragDropMgr.ElementWrapper
2260          * @for DragDropMgr
2261          * @private
2262          * @deprecated
2263          */
2264         ElementWrapper: function(el) {
2265                 /**
2266                  * The element
2267                  * @property el
2268                  */
2269                 this.el = el || null;
2270                 /**
2271                  * The element id
2272                  * @property id
2273                  */
2274                 this.id = this.el && el.id;
2275                 /**
2276                  * A reference to the style property
2277                  * @property css
2278                  */
2279                 this.css = this.el && el.style;
2280             },
2281
2282         /**
2283          * Returns the X position of an html element
2284          * @method getPosX
2285          * @param el the element for which to get the position
2286          * @return {int} the X coordinate
2287          * @for DragDropMgr
2288          * @deprecated use Roo.lib.Dom.getX instead
2289          * @static
2290          */
2291         getPosX: function(el) {
2292             return Roo.lib.Dom.getX(el);
2293         },
2294
2295         /**
2296          * Returns the Y position of an html element
2297          * @method getPosY
2298          * @param el the element for which to get the position
2299          * @return {int} the Y coordinate
2300          * @deprecated use Roo.lib.Dom.getY instead
2301          * @static
2302          */
2303         getPosY: function(el) {
2304             return Roo.lib.Dom.getY(el);
2305         },
2306
2307         /**
2308          * Swap two nodes.  In IE, we use the native method, for others we
2309          * emulate the IE behavior
2310          * @method swapNode
2311          * @param n1 the first node to swap
2312          * @param n2 the other node to swap
2313          * @static
2314          */
2315         swapNode: function(n1, n2) {
2316             if (n1.swapNode) {
2317                 n1.swapNode(n2);
2318             } else {
2319                 var p = n2.parentNode;
2320                 var s = n2.nextSibling;
2321
2322                 if (s == n1) {
2323                     p.insertBefore(n1, n2);
2324                 } else if (n2 == n1.nextSibling) {
2325                     p.insertBefore(n2, n1);
2326                 } else {
2327                     n1.parentNode.replaceChild(n2, n1);
2328                     p.insertBefore(n1, s);
2329                 }
2330             }
2331         },
2332
2333         /**
2334          * Returns the current scroll position
2335          * @method getScroll
2336          * @private
2337          * @static
2338          */
2339         getScroll: function () {
2340             var t, l, dde=document.documentElement, db=document.body;
2341             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2342                 t = dde.scrollTop;
2343                 l = dde.scrollLeft;
2344             } else if (db) {
2345                 t = db.scrollTop;
2346                 l = db.scrollLeft;
2347             } else {
2348
2349             }
2350             return { top: t, left: l };
2351         },
2352
2353         /**
2354          * Returns the specified element style property
2355          * @method getStyle
2356          * @param {HTMLElement} el          the element
2357          * @param {string}      styleProp   the style property
2358          * @return {string} The value of the style property
2359          * @deprecated use Roo.lib.Dom.getStyle
2360          * @static
2361          */
2362         getStyle: function(el, styleProp) {
2363             return Roo.fly(el).getStyle(styleProp);
2364         },
2365
2366         /**
2367          * Gets the scrollTop
2368          * @method getScrollTop
2369          * @return {int} the document's scrollTop
2370          * @static
2371          */
2372         getScrollTop: function () { return this.getScroll().top; },
2373
2374         /**
2375          * Gets the scrollLeft
2376          * @method getScrollLeft
2377          * @return {int} the document's scrollTop
2378          * @static
2379          */
2380         getScrollLeft: function () { return this.getScroll().left; },
2381
2382         /**
2383          * Sets the x/y position of an element to the location of the
2384          * target element.
2385          * @method moveToEl
2386          * @param {HTMLElement} moveEl      The element to move
2387          * @param {HTMLElement} targetEl    The position reference element
2388          * @static
2389          */
2390         moveToEl: function (moveEl, targetEl) {
2391             var aCoord = Roo.lib.Dom.getXY(targetEl);
2392             Roo.lib.Dom.setXY(moveEl, aCoord);
2393         },
2394
2395         /**
2396          * Numeric array sort function
2397          * @method numericSort
2398          * @static
2399          */
2400         numericSort: function(a, b) { return (a - b); },
2401
2402         /**
2403          * Internal counter
2404          * @property _timeoutCount
2405          * @private
2406          * @static
2407          */
2408         _timeoutCount: 0,
2409
2410         /**
2411          * Trying to make the load order less important.  Without this we get
2412          * an error if this file is loaded before the Event Utility.
2413          * @method _addListeners
2414          * @private
2415          * @static
2416          */
2417         _addListeners: function() {
2418             var DDM = Roo.dd.DDM;
2419             if ( Roo.lib.Event && document ) {
2420                 DDM._onLoad();
2421             } else {
2422                 if (DDM._timeoutCount > 2000) {
2423                 } else {
2424                     setTimeout(DDM._addListeners, 10);
2425                     if (document && document.body) {
2426                         DDM._timeoutCount += 1;
2427                     }
2428                 }
2429             }
2430         },
2431
2432         /**
2433          * Recursively searches the immediate parent and all child nodes for
2434          * the handle element in order to determine wheter or not it was
2435          * clicked.
2436          * @method handleWasClicked
2437          * @param node the html element to inspect
2438          * @static
2439          */
2440         handleWasClicked: function(node, id) {
2441             if (this.isHandle(id, node.id)) {
2442                 return true;
2443             } else {
2444                 // check to see if this is a text node child of the one we want
2445                 var p = node.parentNode;
2446
2447                 while (p) {
2448                     if (this.isHandle(id, p.id)) {
2449                         return true;
2450                     } else {
2451                         p = p.parentNode;
2452                     }
2453                 }
2454             }
2455
2456             return false;
2457         }
2458
2459     };
2460
2461 }();
2462
2463 // shorter alias, save a few bytes
2464 Roo.dd.DDM = Roo.dd.DragDropMgr;
2465 Roo.dd.DDM._addListeners();
2466
2467 }/*
2468  * Based on:
2469  * Ext JS Library 1.1.1
2470  * Copyright(c) 2006-2007, Ext JS, LLC.
2471  *
2472  * Originally Released Under LGPL - original licence link has changed is not relivant.
2473  *
2474  * Fork - LGPL
2475  * <script type="text/javascript">
2476  */
2477
2478 /**
2479  * @class Roo.dd.DD
2480  * A DragDrop implementation where the linked element follows the
2481  * mouse cursor during a drag.
2482  * @extends Roo.dd.DragDrop
2483  * @constructor
2484  * @param {String} id the id of the linked element
2485  * @param {String} sGroup the group of related DragDrop items
2486  * @param {object} config an object containing configurable attributes
2487  *                Valid properties for DD:
2488  *                    scroll
2489  */
2490 Roo.dd.DD = function(id, sGroup, config) {
2491     if (id) {
2492         this.init(id, sGroup, config);
2493     }
2494 };
2495
2496 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2497
2498     /**
2499      * When set to true, the utility automatically tries to scroll the browser
2500      * window wehn a drag and drop element is dragged near the viewport boundary.
2501      * Defaults to true.
2502      * @property scroll
2503      * @type boolean
2504      */
2505     scroll: true,
2506
2507     /**
2508      * Sets the pointer offset to the distance between the linked element's top
2509      * left corner and the location the element was clicked
2510      * @method autoOffset
2511      * @param {int} iPageX the X coordinate of the click
2512      * @param {int} iPageY the Y coordinate of the click
2513      */
2514     autoOffset: function(iPageX, iPageY) {
2515         var x = iPageX - this.startPageX;
2516         var y = iPageY - this.startPageY;
2517         this.setDelta(x, y);
2518     },
2519
2520     /**
2521      * Sets the pointer offset.  You can call this directly to force the
2522      * offset to be in a particular location (e.g., pass in 0,0 to set it
2523      * to the center of the object)
2524      * @method setDelta
2525      * @param {int} iDeltaX the distance from the left
2526      * @param {int} iDeltaY the distance from the top
2527      */
2528     setDelta: function(iDeltaX, iDeltaY) {
2529         this.deltaX = iDeltaX;
2530         this.deltaY = iDeltaY;
2531     },
2532
2533     /**
2534      * Sets the drag element to the location of the mousedown or click event,
2535      * maintaining the cursor location relative to the location on the element
2536      * that was clicked.  Override this if you want to place the element in a
2537      * location other than where the cursor is.
2538      * @method setDragElPos
2539      * @param {int} iPageX the X coordinate of the mousedown or drag event
2540      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2541      */
2542     setDragElPos: function(iPageX, iPageY) {
2543         // the first time we do this, we are going to check to make sure
2544         // the element has css positioning
2545
2546         var el = this.getDragEl();
2547         this.alignElWithMouse(el, iPageX, iPageY);
2548     },
2549
2550     /**
2551      * Sets the element to the location of the mousedown or click event,
2552      * maintaining the cursor location relative to the location on the element
2553      * that was clicked.  Override this if you want to place the element in a
2554      * location other than where the cursor is.
2555      * @method alignElWithMouse
2556      * @param {HTMLElement} el the element to move
2557      * @param {int} iPageX the X coordinate of the mousedown or drag event
2558      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2559      */
2560     alignElWithMouse: function(el, iPageX, iPageY) {
2561         var oCoord = this.getTargetCoord(iPageX, iPageY);
2562         var fly = el.dom ? el : Roo.fly(el);
2563         if (!this.deltaSetXY) {
2564             var aCoord = [oCoord.x, oCoord.y];
2565             fly.setXY(aCoord);
2566             var newLeft = fly.getLeft(true);
2567             var newTop  = fly.getTop(true);
2568             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2569         } else {
2570             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2571         }
2572
2573         this.cachePosition(oCoord.x, oCoord.y);
2574         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2575         return oCoord;
2576     },
2577
2578     /**
2579      * Saves the most recent position so that we can reset the constraints and
2580      * tick marks on-demand.  We need to know this so that we can calculate the
2581      * number of pixels the element is offset from its original position.
2582      * @method cachePosition
2583      * @param iPageX the current x position (optional, this just makes it so we
2584      * don't have to look it up again)
2585      * @param iPageY the current y position (optional, this just makes it so we
2586      * don't have to look it up again)
2587      */
2588     cachePosition: function(iPageX, iPageY) {
2589         if (iPageX) {
2590             this.lastPageX = iPageX;
2591             this.lastPageY = iPageY;
2592         } else {
2593             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2594             this.lastPageX = aCoord[0];
2595             this.lastPageY = aCoord[1];
2596         }
2597     },
2598
2599     /**
2600      * Auto-scroll the window if the dragged object has been moved beyond the
2601      * visible window boundary.
2602      * @method autoScroll
2603      * @param {int} x the drag element's x position
2604      * @param {int} y the drag element's y position
2605      * @param {int} h the height of the drag element
2606      * @param {int} w the width of the drag element
2607      * @private
2608      */
2609     autoScroll: function(x, y, h, w) {
2610
2611         if (this.scroll) {
2612             // The client height
2613             var clientH = Roo.lib.Dom.getViewWidth();
2614
2615             // The client width
2616             var clientW = Roo.lib.Dom.getViewHeight();
2617
2618             // The amt scrolled down
2619             var st = this.DDM.getScrollTop();
2620
2621             // The amt scrolled right
2622             var sl = this.DDM.getScrollLeft();
2623
2624             // Location of the bottom of the element
2625             var bot = h + y;
2626
2627             // Location of the right of the element
2628             var right = w + x;
2629
2630             // The distance from the cursor to the bottom of the visible area,
2631             // adjusted so that we don't scroll if the cursor is beyond the
2632             // element drag constraints
2633             var toBot = (clientH + st - y - this.deltaY);
2634
2635             // The distance from the cursor to the right of the visible area
2636             var toRight = (clientW + sl - x - this.deltaX);
2637
2638
2639             // How close to the edge the cursor must be before we scroll
2640             // var thresh = (document.all) ? 100 : 40;
2641             var thresh = 40;
2642
2643             // How many pixels to scroll per autoscroll op.  This helps to reduce
2644             // clunky scrolling. IE is more sensitive about this ... it needs this
2645             // value to be higher.
2646             var scrAmt = (document.all) ? 80 : 30;
2647
2648             // Scroll down if we are near the bottom of the visible page and the
2649             // obj extends below the crease
2650             if ( bot > clientH && toBot < thresh ) {
2651                 window.scrollTo(sl, st + scrAmt);
2652             }
2653
2654             // Scroll up if the window is scrolled down and the top of the object
2655             // goes above the top border
2656             if ( y < st && st > 0 && y - st < thresh ) {
2657                 window.scrollTo(sl, st - scrAmt);
2658             }
2659
2660             // Scroll right if the obj is beyond the right border and the cursor is
2661             // near the border.
2662             if ( right > clientW && toRight < thresh ) {
2663                 window.scrollTo(sl + scrAmt, st);
2664             }
2665
2666             // Scroll left if the window has been scrolled to the right and the obj
2667             // extends past the left border
2668             if ( x < sl && sl > 0 && x - sl < thresh ) {
2669                 window.scrollTo(sl - scrAmt, st);
2670             }
2671         }
2672     },
2673
2674     /**
2675      * Finds the location the element should be placed if we want to move
2676      * it to where the mouse location less the click offset would place us.
2677      * @method getTargetCoord
2678      * @param {int} iPageX the X coordinate of the click
2679      * @param {int} iPageY the Y coordinate of the click
2680      * @return an object that contains the coordinates (Object.x and Object.y)
2681      * @private
2682      */
2683     getTargetCoord: function(iPageX, iPageY) {
2684
2685
2686         var x = iPageX - this.deltaX;
2687         var y = iPageY - this.deltaY;
2688
2689         if (this.constrainX) {
2690             if (x < this.minX) { x = this.minX; }
2691             if (x > this.maxX) { x = this.maxX; }
2692         }
2693
2694         if (this.constrainY) {
2695             if (y < this.minY) { y = this.minY; }
2696             if (y > this.maxY) { y = this.maxY; }
2697         }
2698
2699         x = this.getTick(x, this.xTicks);
2700         y = this.getTick(y, this.yTicks);
2701
2702
2703         return {x:x, y:y};
2704     },
2705
2706     /*
2707      * Sets up config options specific to this class. Overrides
2708      * Roo.dd.DragDrop, but all versions of this method through the
2709      * inheritance chain are called
2710      */
2711     applyConfig: function() {
2712         Roo.dd.DD.superclass.applyConfig.call(this);
2713         this.scroll = (this.config.scroll !== false);
2714     },
2715
2716     /*
2717      * Event that fires prior to the onMouseDown event.  Overrides
2718      * Roo.dd.DragDrop.
2719      */
2720     b4MouseDown: function(e) {
2721         // this.resetConstraints();
2722         this.autoOffset(e.getPageX(),
2723                             e.getPageY());
2724     },
2725
2726     /*
2727      * Event that fires prior to the onDrag event.  Overrides
2728      * Roo.dd.DragDrop.
2729      */
2730     b4Drag: function(e) {
2731         this.setDragElPos(e.getPageX(),
2732                             e.getPageY());
2733     },
2734
2735     toString: function() {
2736         return ("DD " + this.id);
2737     }
2738
2739     //////////////////////////////////////////////////////////////////////////
2740     // Debugging ygDragDrop events that can be overridden
2741     //////////////////////////////////////////////////////////////////////////
2742     /*
2743     startDrag: function(x, y) {
2744     },
2745
2746     onDrag: function(e) {
2747     },
2748
2749     onDragEnter: function(e, id) {
2750     },
2751
2752     onDragOver: function(e, id) {
2753     },
2754
2755     onDragOut: function(e, id) {
2756     },
2757
2758     onDragDrop: function(e, id) {
2759     },
2760
2761     endDrag: function(e) {
2762     }
2763
2764     */
2765
2766 });/*
2767  * Based on:
2768  * Ext JS Library 1.1.1
2769  * Copyright(c) 2006-2007, Ext JS, LLC.
2770  *
2771  * Originally Released Under LGPL - original licence link has changed is not relivant.
2772  *
2773  * Fork - LGPL
2774  * <script type="text/javascript">
2775  */
2776
2777 /**
2778  * @class Roo.dd.DDProxy
2779  * A DragDrop implementation that inserts an empty, bordered div into
2780  * the document that follows the cursor during drag operations.  At the time of
2781  * the click, the frame div is resized to the dimensions of the linked html
2782  * element, and moved to the exact location of the linked element.
2783  *
2784  * References to the "frame" element refer to the single proxy element that
2785  * was created to be dragged in place of all DDProxy elements on the
2786  * page.
2787  *
2788  * @extends Roo.dd.DD
2789  * @constructor
2790  * @param {String} id the id of the linked html element
2791  * @param {String} sGroup the group of related DragDrop objects
2792  * @param {object} config an object containing configurable attributes
2793  *                Valid properties for DDProxy in addition to those in DragDrop:
2794  *                   resizeFrame, centerFrame, dragElId
2795  */
2796 Roo.dd.DDProxy = function(id, sGroup, config) {
2797     if (id) {
2798         this.init(id, sGroup, config);
2799         this.initFrame();
2800     }
2801 };
2802
2803 /**
2804  * The default drag frame div id
2805  * @property Roo.dd.DDProxy.dragElId
2806  * @type String
2807  * @static
2808  */
2809 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2810
2811 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2812
2813     /**
2814      * By default we resize the drag frame to be the same size as the element
2815      * we want to drag (this is to get the frame effect).  We can turn it off
2816      * if we want a different behavior.
2817      * @property resizeFrame
2818      * @type boolean
2819      */
2820     resizeFrame: true,
2821
2822     /**
2823      * By default the frame is positioned exactly where the drag element is, so
2824      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2825      * you do not have constraints on the obj is to have the drag frame centered
2826      * around the cursor.  Set centerFrame to true for this effect.
2827      * @property centerFrame
2828      * @type boolean
2829      */
2830     centerFrame: false,
2831
2832     /**
2833      * Creates the proxy element if it does not yet exist
2834      * @method createFrame
2835      */
2836     createFrame: function() {
2837         var self = this;
2838         var body = document.body;
2839
2840         if (!body || !body.firstChild) {
2841             setTimeout( function() { self.createFrame(); }, 50 );
2842             return;
2843         }
2844
2845         var div = this.getDragEl();
2846
2847         if (!div) {
2848             div    = document.createElement("div");
2849             div.id = this.dragElId;
2850             var s  = div.style;
2851
2852             s.position   = "absolute";
2853             s.visibility = "hidden";
2854             s.cursor     = "move";
2855             s.border     = "2px solid #aaa";
2856             s.zIndex     = 999;
2857
2858             // appendChild can blow up IE if invoked prior to the window load event
2859             // while rendering a table.  It is possible there are other scenarios
2860             // that would cause this to happen as well.
2861             body.insertBefore(div, body.firstChild);
2862         }
2863     },
2864
2865     /**
2866      * Initialization for the drag frame element.  Must be called in the
2867      * constructor of all subclasses
2868      * @method initFrame
2869      */
2870     initFrame: function() {
2871         this.createFrame();
2872     },
2873
2874     applyConfig: function() {
2875         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2876
2877         this.resizeFrame = (this.config.resizeFrame !== false);
2878         this.centerFrame = (this.config.centerFrame);
2879         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2880     },
2881
2882     /**
2883      * Resizes the drag frame to the dimensions of the clicked object, positions
2884      * it over the object, and finally displays it
2885      * @method showFrame
2886      * @param {int} iPageX X click position
2887      * @param {int} iPageY Y click position
2888      * @private
2889      */
2890     showFrame: function(iPageX, iPageY) {
2891         var el = this.getEl();
2892         var dragEl = this.getDragEl();
2893         var s = dragEl.style;
2894
2895         this._resizeProxy();
2896
2897         if (this.centerFrame) {
2898             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2899                            Math.round(parseInt(s.height, 10)/2) );
2900         }
2901
2902         this.setDragElPos(iPageX, iPageY);
2903
2904         Roo.fly(dragEl).show();
2905     },
2906
2907     /**
2908      * The proxy is automatically resized to the dimensions of the linked
2909      * element when a drag is initiated, unless resizeFrame is set to false
2910      * @method _resizeProxy
2911      * @private
2912      */
2913     _resizeProxy: function() {
2914         if (this.resizeFrame) {
2915             var el = this.getEl();
2916             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2917         }
2918     },
2919
2920     // overrides Roo.dd.DragDrop
2921     b4MouseDown: function(e) {
2922         var x = e.getPageX();
2923         var y = e.getPageY();
2924         this.autoOffset(x, y);
2925         this.setDragElPos(x, y);
2926     },
2927
2928     // overrides Roo.dd.DragDrop
2929     b4StartDrag: function(x, y) {
2930         // show the drag frame
2931         this.showFrame(x, y);
2932     },
2933
2934     // overrides Roo.dd.DragDrop
2935     b4EndDrag: function(e) {
2936         Roo.fly(this.getDragEl()).hide();
2937     },
2938
2939     // overrides Roo.dd.DragDrop
2940     // By default we try to move the element to the last location of the frame.
2941     // This is so that the default behavior mirrors that of Roo.dd.DD.
2942     endDrag: function(e) {
2943
2944         var lel = this.getEl();
2945         var del = this.getDragEl();
2946
2947         // Show the drag frame briefly so we can get its position
2948         del.style.visibility = "";
2949
2950         this.beforeMove();
2951         // Hide the linked element before the move to get around a Safari
2952         // rendering bug.
2953         lel.style.visibility = "hidden";
2954         Roo.dd.DDM.moveToEl(lel, del);
2955         del.style.visibility = "hidden";
2956         lel.style.visibility = "";
2957
2958         this.afterDrag();
2959     },
2960
2961     beforeMove : function(){
2962
2963     },
2964
2965     afterDrag : function(){
2966
2967     },
2968
2969     toString: function() {
2970         return ("DDProxy " + this.id);
2971     }
2972
2973 });
2974 /*
2975  * Based on:
2976  * Ext JS Library 1.1.1
2977  * Copyright(c) 2006-2007, Ext JS, LLC.
2978  *
2979  * Originally Released Under LGPL - original licence link has changed is not relivant.
2980  *
2981  * Fork - LGPL
2982  * <script type="text/javascript">
2983  */
2984
2985  /**
2986  * @class Roo.dd.DDTarget
2987  * A DragDrop implementation that does not move, but can be a drop
2988  * target.  You would get the same result by simply omitting implementation
2989  * for the event callbacks, but this way we reduce the processing cost of the
2990  * event listener and the callbacks.
2991  * @extends Roo.dd.DragDrop
2992  * @constructor
2993  * @param {String} id the id of the element that is a drop target
2994  * @param {String} sGroup the group of related DragDrop objects
2995  * @param {object} config an object containing configurable attributes
2996  *                 Valid properties for DDTarget in addition to those in
2997  *                 DragDrop:
2998  *                    none
2999  */
3000 Roo.dd.DDTarget = function(id, sGroup, config) {
3001     if (id) {
3002         this.initTarget(id, sGroup, config);
3003     }
3004     if (config.listeners || config.events) { 
3005        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3006             listeners : config.listeners || {}, 
3007             events : config.events || {} 
3008         });    
3009     }
3010 };
3011
3012 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3013 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3014     toString: function() {
3015         return ("DDTarget " + this.id);
3016     }
3017 });
3018 /*
3019  * Based on:
3020  * Ext JS Library 1.1.1
3021  * Copyright(c) 2006-2007, Ext JS, LLC.
3022  *
3023  * Originally Released Under LGPL - original licence link has changed is not relivant.
3024  *
3025  * Fork - LGPL
3026  * <script type="text/javascript">
3027  */
3028  
3029
3030 /**
3031  * @class Roo.dd.ScrollManager
3032  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3033  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3034  * @singleton
3035  */
3036 Roo.dd.ScrollManager = function(){
3037     var ddm = Roo.dd.DragDropMgr;
3038     var els = {};
3039     var dragEl = null;
3040     var proc = {};
3041     
3042     var onStop = function(e){
3043         dragEl = null;
3044         clearProc();
3045     };
3046     
3047     var triggerRefresh = function(){
3048         if(ddm.dragCurrent){
3049              ddm.refreshCache(ddm.dragCurrent.groups);
3050         }
3051     };
3052     
3053     var doScroll = function(){
3054         if(ddm.dragCurrent){
3055             var dds = Roo.dd.ScrollManager;
3056             if(!dds.animate){
3057                 if(proc.el.scroll(proc.dir, dds.increment)){
3058                     triggerRefresh();
3059                 }
3060             }else{
3061                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3062             }
3063         }
3064     };
3065     
3066     var clearProc = function(){
3067         if(proc.id){
3068             clearInterval(proc.id);
3069         }
3070         proc.id = 0;
3071         proc.el = null;
3072         proc.dir = "";
3073     };
3074     
3075     var startProc = function(el, dir){
3076         clearProc();
3077         proc.el = el;
3078         proc.dir = dir;
3079         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3080     };
3081     
3082     var onFire = function(e, isDrop){
3083         if(isDrop || !ddm.dragCurrent){ return; }
3084         var dds = Roo.dd.ScrollManager;
3085         if(!dragEl || dragEl != ddm.dragCurrent){
3086             dragEl = ddm.dragCurrent;
3087             // refresh regions on drag start
3088             dds.refreshCache();
3089         }
3090         
3091         var xy = Roo.lib.Event.getXY(e);
3092         var pt = new Roo.lib.Point(xy[0], xy[1]);
3093         for(var id in els){
3094             var el = els[id], r = el._region;
3095             if(r && r.contains(pt) && el.isScrollable()){
3096                 if(r.bottom - pt.y <= dds.thresh){
3097                     if(proc.el != el){
3098                         startProc(el, "down");
3099                     }
3100                     return;
3101                 }else if(r.right - pt.x <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "left");
3104                     }
3105                     return;
3106                 }else if(pt.y - r.top <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "up");
3109                     }
3110                     return;
3111                 }else if(pt.x - r.left <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "right");
3114                     }
3115                     return;
3116                 }
3117             }
3118         }
3119         clearProc();
3120     };
3121     
3122     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3123     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3124     
3125     return {
3126         /**
3127          * Registers new overflow element(s) to auto scroll
3128          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3129          */
3130         register : function(el){
3131             if(el instanceof Array){
3132                 for(var i = 0, len = el.length; i < len; i++) {
3133                         this.register(el[i]);
3134                 }
3135             }else{
3136                 el = Roo.get(el);
3137                 els[el.id] = el;
3138             }
3139         },
3140         
3141         /**
3142          * Unregisters overflow element(s) so they are no longer scrolled
3143          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3144          */
3145         unregister : function(el){
3146             if(el instanceof Array){
3147                 for(var i = 0, len = el.length; i < len; i++) {
3148                         this.unregister(el[i]);
3149                 }
3150             }else{
3151                 el = Roo.get(el);
3152                 delete els[el.id];
3153             }
3154         },
3155         
3156         /**
3157          * The number of pixels from the edge of a container the pointer needs to be to 
3158          * trigger scrolling (defaults to 25)
3159          * @type Number
3160          */
3161         thresh : 25,
3162         
3163         /**
3164          * The number of pixels to scroll in each scroll increment (defaults to 50)
3165          * @type Number
3166          */
3167         increment : 100,
3168         
3169         /**
3170          * The frequency of scrolls in milliseconds (defaults to 500)
3171          * @type Number
3172          */
3173         frequency : 500,
3174         
3175         /**
3176          * True to animate the scroll (defaults to true)
3177          * @type Boolean
3178          */
3179         animate: true,
3180         
3181         /**
3182          * The animation duration in seconds - 
3183          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3184          * @type Number
3185          */
3186         animDuration: .4,
3187         
3188         /**
3189          * Manually trigger a cache refresh.
3190          */
3191         refreshCache : function(){
3192             for(var id in els){
3193                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3194                     els[id]._region = els[id].getRegion();
3195                 }
3196             }
3197         }
3198     };
3199 }();/*
3200  * Based on:
3201  * Ext JS Library 1.1.1
3202  * Copyright(c) 2006-2007, Ext JS, LLC.
3203  *
3204  * Originally Released Under LGPL - original licence link has changed is not relivant.
3205  *
3206  * Fork - LGPL
3207  * <script type="text/javascript">
3208  */
3209  
3210
3211 /**
3212  * @class Roo.dd.Registry
3213  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3214  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3215  * @singleton
3216  */
3217 Roo.dd.Registry = function(){
3218     var elements = {}; 
3219     var handles = {}; 
3220     var autoIdSeed = 0;
3221
3222     var getId = function(el, autogen){
3223         if(typeof el == "string"){
3224             return el;
3225         }
3226         var id = el.id;
3227         if(!id && autogen !== false){
3228             id = "roodd-" + (++autoIdSeed);
3229             el.id = id;
3230         }
3231         return id;
3232     };
3233     
3234     return {
3235     /**
3236      * Register a drag drop element
3237      * @param {String|HTMLElement} element The id or DOM node to register
3238      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3239      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3240      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3241      * populated in the data object (if applicable):
3242      * <pre>
3243 Value      Description<br />
3244 ---------  ------------------------------------------<br />
3245 handles    Array of DOM nodes that trigger dragging<br />
3246            for the element being registered<br />
3247 isHandle   True if the element passed in triggers<br />
3248            dragging itself, else false
3249 </pre>
3250      */
3251         register : function(el, data){
3252             data = data || {};
3253             if(typeof el == "string"){
3254                 el = document.getElementById(el);
3255             }
3256             data.ddel = el;
3257             elements[getId(el)] = data;
3258             if(data.isHandle !== false){
3259                 handles[data.ddel.id] = data;
3260             }
3261             if(data.handles){
3262                 var hs = data.handles;
3263                 for(var i = 0, len = hs.length; i < len; i++){
3264                         handles[getId(hs[i])] = data;
3265                 }
3266             }
3267         },
3268
3269     /**
3270      * Unregister a drag drop element
3271      * @param {String|HTMLElement}  element The id or DOM node to unregister
3272      */
3273         unregister : function(el){
3274             var id = getId(el, false);
3275             var data = elements[id];
3276             if(data){
3277                 delete elements[id];
3278                 if(data.handles){
3279                     var hs = data.handles;
3280                     for(var i = 0, len = hs.length; i < len; i++){
3281                         delete handles[getId(hs[i], false)];
3282                     }
3283                 }
3284             }
3285         },
3286
3287     /**
3288      * Returns the handle registered for a DOM Node by id
3289      * @param {String|HTMLElement} id The DOM node or id to look up
3290      * @return {Object} handle The custom handle data
3291      */
3292         getHandle : function(id){
3293             if(typeof id != "string"){ // must be element?
3294                 id = id.id;
3295             }
3296             return handles[id];
3297         },
3298
3299     /**
3300      * Returns the handle that is registered for the DOM node that is the target of the event
3301      * @param {Event} e The event
3302      * @return {Object} handle The custom handle data
3303      */
3304         getHandleFromEvent : function(e){
3305             var t = Roo.lib.Event.getTarget(e);
3306             return t ? handles[t.id] : null;
3307         },
3308
3309     /**
3310      * Returns a custom data object that is registered for a DOM node by id
3311      * @param {String|HTMLElement} id The DOM node or id to look up
3312      * @return {Object} data The custom data
3313      */
3314         getTarget : function(id){
3315             if(typeof id != "string"){ // must be element?
3316                 id = id.id;
3317             }
3318             return elements[id];
3319         },
3320
3321     /**
3322      * Returns a custom data object that is registered for the DOM node that is the target of the event
3323      * @param {Event} e The event
3324      * @return {Object} data The custom data
3325      */
3326         getTargetFromEvent : function(e){
3327             var t = Roo.lib.Event.getTarget(e);
3328             return t ? elements[t.id] || handles[t.id] : null;
3329         }
3330     };
3331 }();/*
3332  * Based on:
3333  * Ext JS Library 1.1.1
3334  * Copyright(c) 2006-2007, Ext JS, LLC.
3335  *
3336  * Originally Released Under LGPL - original licence link has changed is not relivant.
3337  *
3338  * Fork - LGPL
3339  * <script type="text/javascript">
3340  */
3341  
3342
3343 /**
3344  * @class Roo.dd.StatusProxy
3345  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3346  * default drag proxy used by all Roo.dd components.
3347  * @constructor
3348  * @param {Object} config
3349  */
3350 Roo.dd.StatusProxy = function(config){
3351     Roo.apply(this, config);
3352     this.id = this.id || Roo.id();
3353     this.el = new Roo.Layer({
3354         dh: {
3355             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3356                 {tag: "div", cls: "x-dd-drop-icon"},
3357                 {tag: "div", cls: "x-dd-drag-ghost"}
3358             ]
3359         }, 
3360         shadow: !config || config.shadow !== false
3361     });
3362     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3363     this.dropStatus = this.dropNotAllowed;
3364 };
3365
3366 Roo.dd.StatusProxy.prototype = {
3367     /**
3368      * @cfg {String} dropAllowed
3369      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3370      */
3371     dropAllowed : "x-dd-drop-ok",
3372     /**
3373      * @cfg {String} dropNotAllowed
3374      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3375      */
3376     dropNotAllowed : "x-dd-drop-nodrop",
3377
3378     /**
3379      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3380      * over the current target element.
3381      * @param {String} cssClass The css class for the new drop status indicator image
3382      */
3383     setStatus : function(cssClass){
3384         cssClass = cssClass || this.dropNotAllowed;
3385         if(this.dropStatus != cssClass){
3386             this.el.replaceClass(this.dropStatus, cssClass);
3387             this.dropStatus = cssClass;
3388         }
3389     },
3390
3391     /**
3392      * Resets the status indicator to the default dropNotAllowed value
3393      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3394      */
3395     reset : function(clearGhost){
3396         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3397         this.dropStatus = this.dropNotAllowed;
3398         if(clearGhost){
3399             this.ghost.update("");
3400         }
3401     },
3402
3403     /**
3404      * Updates the contents of the ghost element
3405      * @param {String} html The html that will replace the current innerHTML of the ghost element
3406      */
3407     update : function(html){
3408         if(typeof html == "string"){
3409             this.ghost.update(html);
3410         }else{
3411             this.ghost.update("");
3412             html.style.margin = "0";
3413             this.ghost.dom.appendChild(html);
3414         }
3415         // ensure float = none set?? cant remember why though.
3416         var el = this.ghost.dom.firstChild;
3417                 if(el){
3418                         Roo.fly(el).setStyle('float', 'none');
3419                 }
3420     },
3421     
3422     /**
3423      * Returns the underlying proxy {@link Roo.Layer}
3424      * @return {Roo.Layer} el
3425     */
3426     getEl : function(){
3427         return this.el;
3428     },
3429
3430     /**
3431      * Returns the ghost element
3432      * @return {Roo.Element} el
3433      */
3434     getGhost : function(){
3435         return this.ghost;
3436     },
3437
3438     /**
3439      * Hides the proxy
3440      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3441      */
3442     hide : function(clear){
3443         this.el.hide();
3444         if(clear){
3445             this.reset(true);
3446         }
3447     },
3448
3449     /**
3450      * Stops the repair animation if it's currently running
3451      */
3452     stop : function(){
3453         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3454             this.anim.stop();
3455         }
3456     },
3457
3458     /**
3459      * Displays this proxy
3460      */
3461     show : function(){
3462         this.el.show();
3463     },
3464
3465     /**
3466      * Force the Layer to sync its shadow and shim positions to the element
3467      */
3468     sync : function(){
3469         this.el.sync();
3470     },
3471
3472     /**
3473      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3474      * invalid drop operation by the item being dragged.
3475      * @param {Array} xy The XY position of the element ([x, y])
3476      * @param {Function} callback The function to call after the repair is complete
3477      * @param {Object} scope The scope in which to execute the callback
3478      */
3479     repair : function(xy, callback, scope){
3480         this.callback = callback;
3481         this.scope = scope;
3482         if(xy && this.animRepair !== false){
3483             this.el.addClass("x-dd-drag-repair");
3484             this.el.hideUnders(true);
3485             this.anim = this.el.shift({
3486                 duration: this.repairDuration || .5,
3487                 easing: 'easeOut',
3488                 xy: xy,
3489                 stopFx: true,
3490                 callback: this.afterRepair,
3491                 scope: this
3492             });
3493         }else{
3494             this.afterRepair();
3495         }
3496     },
3497
3498     // private
3499     afterRepair : function(){
3500         this.hide(true);
3501         if(typeof this.callback == "function"){
3502             this.callback.call(this.scope || this);
3503         }
3504         this.callback = null;
3505         this.scope = null;
3506     }
3507 };/*
3508  * Based on:
3509  * Ext JS Library 1.1.1
3510  * Copyright(c) 2006-2007, Ext JS, LLC.
3511  *
3512  * Originally Released Under LGPL - original licence link has changed is not relivant.
3513  *
3514  * Fork - LGPL
3515  * <script type="text/javascript">
3516  */
3517
3518 /**
3519  * @class Roo.dd.DragSource
3520  * @extends Roo.dd.DDProxy
3521  * A simple class that provides the basic implementation needed to make any element draggable.
3522  * @constructor
3523  * @param {String/HTMLElement/Element} el The container element
3524  * @param {Object} config
3525  */
3526 Roo.dd.DragSource = function(el, config){
3527     this.el = Roo.get(el);
3528     this.dragData = {};
3529     
3530     Roo.apply(this, config);
3531     
3532     if(!this.proxy){
3533         this.proxy = new Roo.dd.StatusProxy();
3534     }
3535
3536     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3537           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3538     
3539     this.dragging = false;
3540 };
3541
3542 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3543     /**
3544      * @cfg {String} dropAllowed
3545      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3546      */
3547     dropAllowed : "x-dd-drop-ok",
3548     /**
3549      * @cfg {String} dropNotAllowed
3550      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3551      */
3552     dropNotAllowed : "x-dd-drop-nodrop",
3553
3554     /**
3555      * Returns the data object associated with this drag source
3556      * @return {Object} data An object containing arbitrary data
3557      */
3558     getDragData : function(e){
3559         return this.dragData;
3560     },
3561
3562     // private
3563     onDragEnter : function(e, id){
3564         var target = Roo.dd.DragDropMgr.getDDById(id);
3565         this.cachedTarget = target;
3566         if(this.beforeDragEnter(target, e, id) !== false){
3567             if(target.isNotifyTarget){
3568                 var status = target.notifyEnter(this, e, this.dragData);
3569                 this.proxy.setStatus(status);
3570             }else{
3571                 this.proxy.setStatus(this.dropAllowed);
3572             }
3573             
3574             if(this.afterDragEnter){
3575                 /**
3576                  * An empty function by default, but provided so that you can perform a custom action
3577                  * when the dragged item enters the drop target by providing an implementation.
3578                  * @param {Roo.dd.DragDrop} target The drop target
3579                  * @param {Event} e The event object
3580                  * @param {String} id The id of the dragged element
3581                  * @method afterDragEnter
3582                  */
3583                 this.afterDragEnter(target, e, id);
3584             }
3585         }
3586     },
3587
3588     /**
3589      * An empty function by default, but provided so that you can perform a custom action
3590      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3591      * @param {Roo.dd.DragDrop} target The drop target
3592      * @param {Event} e The event object
3593      * @param {String} id The id of the dragged element
3594      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3595      */
3596     beforeDragEnter : function(target, e, id){
3597         return true;
3598     },
3599
3600     // private
3601     alignElWithMouse: function() {
3602         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3603         this.proxy.sync();
3604     },
3605
3606     // private
3607     onDragOver : function(e, id){
3608         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3609         if(this.beforeDragOver(target, e, id) !== false){
3610             if(target.isNotifyTarget){
3611                 var status = target.notifyOver(this, e, this.dragData);
3612                 this.proxy.setStatus(status);
3613             }
3614
3615             if(this.afterDragOver){
3616                 /**
3617                  * An empty function by default, but provided so that you can perform a custom action
3618                  * while the dragged item is over the drop target by providing an implementation.
3619                  * @param {Roo.dd.DragDrop} target The drop target
3620                  * @param {Event} e The event object
3621                  * @param {String} id The id of the dragged element
3622                  * @method afterDragOver
3623                  */
3624                 this.afterDragOver(target, e, id);
3625             }
3626         }
3627     },
3628
3629     /**
3630      * An empty function by default, but provided so that you can perform a custom action
3631      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3632      * @param {Roo.dd.DragDrop} target The drop target
3633      * @param {Event} e The event object
3634      * @param {String} id The id of the dragged element
3635      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3636      */
3637     beforeDragOver : function(target, e, id){
3638         return true;
3639     },
3640
3641     // private
3642     onDragOut : function(e, id){
3643         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3644         if(this.beforeDragOut(target, e, id) !== false){
3645             if(target.isNotifyTarget){
3646                 target.notifyOut(this, e, this.dragData);
3647             }
3648             this.proxy.reset();
3649             if(this.afterDragOut){
3650                 /**
3651                  * An empty function by default, but provided so that you can perform a custom action
3652                  * after the dragged item is dragged out of the target without dropping.
3653                  * @param {Roo.dd.DragDrop} target The drop target
3654                  * @param {Event} e The event object
3655                  * @param {String} id The id of the dragged element
3656                  * @method afterDragOut
3657                  */
3658                 this.afterDragOut(target, e, id);
3659             }
3660         }
3661         this.cachedTarget = null;
3662     },
3663
3664     /**
3665      * An empty function by default, but provided so that you can perform a custom action before the dragged
3666      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3667      * @param {Roo.dd.DragDrop} target The drop target
3668      * @param {Event} e The event object
3669      * @param {String} id The id of the dragged element
3670      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3671      */
3672     beforeDragOut : function(target, e, id){
3673         return true;
3674     },
3675     
3676     // private
3677     onDragDrop : function(e, id){
3678         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3679         if(this.beforeDragDrop(target, e, id) !== false){
3680             if(target.isNotifyTarget){
3681                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3682                     this.onValidDrop(target, e, id);
3683                 }else{
3684                     this.onInvalidDrop(target, e, id);
3685                 }
3686             }else{
3687                 this.onValidDrop(target, e, id);
3688             }
3689             
3690             if(this.afterDragDrop){
3691                 /**
3692                  * An empty function by default, but provided so that you can perform a custom action
3693                  * after a valid drag drop has occurred by providing an implementation.
3694                  * @param {Roo.dd.DragDrop} target The drop target
3695                  * @param {Event} e The event object
3696                  * @param {String} id The id of the dropped element
3697                  * @method afterDragDrop
3698                  */
3699                 this.afterDragDrop(target, e, id);
3700             }
3701         }
3702         delete this.cachedTarget;
3703     },
3704
3705     /**
3706      * An empty function by default, but provided so that you can perform a custom action before the dragged
3707      * item is dropped onto the target and optionally cancel the onDragDrop.
3708      * @param {Roo.dd.DragDrop} target The drop target
3709      * @param {Event} e The event object
3710      * @param {String} id The id of the dragged element
3711      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3712      */
3713     beforeDragDrop : function(target, e, id){
3714         return true;
3715     },
3716
3717     // private
3718     onValidDrop : function(target, e, id){
3719         this.hideProxy();
3720         if(this.afterValidDrop){
3721             /**
3722              * An empty function by default, but provided so that you can perform a custom action
3723              * after a valid drop has occurred by providing an implementation.
3724              * @param {Object} target The target DD 
3725              * @param {Event} e The event object
3726              * @param {String} id The id of the dropped element
3727              * @method afterInvalidDrop
3728              */
3729             this.afterValidDrop(target, e, id);
3730         }
3731     },
3732
3733     // private
3734     getRepairXY : function(e, data){
3735         return this.el.getXY();  
3736     },
3737
3738     // private
3739     onInvalidDrop : function(target, e, id){
3740         this.beforeInvalidDrop(target, e, id);
3741         if(this.cachedTarget){
3742             if(this.cachedTarget.isNotifyTarget){
3743                 this.cachedTarget.notifyOut(this, e, this.dragData);
3744             }
3745             this.cacheTarget = null;
3746         }
3747         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3748
3749         if(this.afterInvalidDrop){
3750             /**
3751              * An empty function by default, but provided so that you can perform a custom action
3752              * after an invalid drop has occurred by providing an implementation.
3753              * @param {Event} e The event object
3754              * @param {String} id The id of the dropped element
3755              * @method afterInvalidDrop
3756              */
3757             this.afterInvalidDrop(e, id);
3758         }
3759     },
3760
3761     // private
3762     afterRepair : function(){
3763         if(Roo.enableFx){
3764             this.el.highlight(this.hlColor || "c3daf9");
3765         }
3766         this.dragging = false;
3767     },
3768
3769     /**
3770      * An empty function by default, but provided so that you can perform a custom action after an invalid
3771      * drop has occurred.
3772      * @param {Roo.dd.DragDrop} target The drop target
3773      * @param {Event} e The event object
3774      * @param {String} id The id of the dragged element
3775      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3776      */
3777     beforeInvalidDrop : function(target, e, id){
3778         return true;
3779     },
3780
3781     // private
3782     handleMouseDown : function(e){
3783         if(this.dragging) {
3784             return;
3785         }
3786         var data = this.getDragData(e);
3787         if(data && this.onBeforeDrag(data, e) !== false){
3788             this.dragData = data;
3789             this.proxy.stop();
3790             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3791         } 
3792     },
3793
3794     /**
3795      * An empty function by default, but provided so that you can perform a custom action before the initial
3796      * drag event begins and optionally cancel it.
3797      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3798      * @param {Event} e The event object
3799      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3800      */
3801     onBeforeDrag : function(data, e){
3802         return true;
3803     },
3804
3805     /**
3806      * An empty function by default, but provided so that you can perform a custom action once the initial
3807      * drag event has begun.  The drag cannot be canceled from this function.
3808      * @param {Number} x The x position of the click on the dragged object
3809      * @param {Number} y The y position of the click on the dragged object
3810      */
3811     onStartDrag : Roo.emptyFn,
3812
3813     // private - YUI override
3814     startDrag : function(x, y){
3815         this.proxy.reset();
3816         this.dragging = true;
3817         this.proxy.update("");
3818         this.onInitDrag(x, y);
3819         this.proxy.show();
3820     },
3821
3822     // private
3823     onInitDrag : function(x, y){
3824         var clone = this.el.dom.cloneNode(true);
3825         clone.id = Roo.id(); // prevent duplicate ids
3826         this.proxy.update(clone);
3827         this.onStartDrag(x, y);
3828         return true;
3829     },
3830
3831     /**
3832      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3833      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3834      */
3835     getProxy : function(){
3836         return this.proxy;  
3837     },
3838
3839     /**
3840      * Hides the drag source's {@link Roo.dd.StatusProxy}
3841      */
3842     hideProxy : function(){
3843         this.proxy.hide();  
3844         this.proxy.reset(true);
3845         this.dragging = false;
3846     },
3847
3848     // private
3849     triggerCacheRefresh : function(){
3850         Roo.dd.DDM.refreshCache(this.groups);
3851     },
3852
3853     // private - override to prevent hiding
3854     b4EndDrag: function(e) {
3855     },
3856
3857     // private - override to prevent moving
3858     endDrag : function(e){
3859         this.onEndDrag(this.dragData, e);
3860     },
3861
3862     // private
3863     onEndDrag : function(data, e){
3864     },
3865     
3866     // private - pin to cursor
3867     autoOffset : function(x, y) {
3868         this.setDelta(-12, -20);
3869     }    
3870 });/*
3871  * Based on:
3872  * Ext JS Library 1.1.1
3873  * Copyright(c) 2006-2007, Ext JS, LLC.
3874  *
3875  * Originally Released Under LGPL - original licence link has changed is not relivant.
3876  *
3877  * Fork - LGPL
3878  * <script type="text/javascript">
3879  */
3880
3881
3882 /**
3883  * @class Roo.dd.DropTarget
3884  * @extends Roo.dd.DDTarget
3885  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3886  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3887  * @constructor
3888  * @param {String/HTMLElement/Element} el The container element
3889  * @param {Object} config
3890  */
3891 Roo.dd.DropTarget = function(el, config){
3892     this.el = Roo.get(el);
3893     
3894     var listeners = config.listeners;
3895     if (listeners) {
3896         delete config.listeners;
3897     }
3898     Roo.apply(this, config);
3899     
3900     if(this.containerScroll){
3901         Roo.dd.ScrollManager.register(this.el);
3902     }
3903     this.addEvents( {
3904          /**
3905          * @scope Roo.dd.DropTarget
3906          */
3907          
3908          /**
3909          * @event enter
3910          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3911          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3912          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3913          * 
3914          * IMPORTANT : it should set this.overClass and this.dropAllowed
3915          * 
3916          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3917          * @param {Event} e The event
3918          * @param {Object} data An object containing arbitrary data supplied by the drag source
3919          */
3920         "enter" : true,
3921         
3922          /**
3923          * @event over
3924          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3925          * This method will be called on every mouse movement while the drag source is over the drop target.
3926          * This default implementation simply returns the dropAllowed config value.
3927          * 
3928          * IMPORTANT : it should set this.dropAllowed
3929          * 
3930          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3931          * @param {Event} e The event
3932          * @param {Object} data An object containing arbitrary data supplied by the drag source
3933          
3934          */
3935         "over" : true,
3936         /**
3937          * @event out
3938          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3939          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3940          * overClass (if any) from the drop element.
3941          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3942          * @param {Event} e The event
3943          * @param {Object} data An object containing arbitrary data supplied by the drag source
3944          */
3945          "out" : true,
3946          
3947         /**
3948          * @event drop
3949          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3950          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3951          * implementation that does something to process the drop event and returns true so that the drag source's
3952          * repair action does not run.
3953          * 
3954          * IMPORTANT : it should set this.success
3955          * 
3956          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3957          * @param {Event} e The event
3958          * @param {Object} data An object containing arbitrary data supplied by the drag source
3959         */
3960          "drop" : true
3961     });
3962             
3963      
3964     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3965         this.el.dom, 
3966         this.ddGroup || this.group,
3967         {
3968             isTarget: true,
3969             listeners : listeners || {} 
3970            
3971         
3972         }
3973     );
3974
3975 };
3976
3977 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3978     /**
3979      * @cfg {String} overClass
3980      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3981      */
3982      /**
3983      * @cfg {String} ddGroup
3984      * The drag drop group to handle drop events for
3985      */
3986      
3987     /**
3988      * @cfg {String} dropAllowed
3989      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3990      */
3991     dropAllowed : "x-dd-drop-ok",
3992     /**
3993      * @cfg {String} dropNotAllowed
3994      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3995      */
3996     dropNotAllowed : "x-dd-drop-nodrop",
3997     /**
3998      * @cfg {boolean} success
3999      * set this after drop listener.. 
4000      */
4001     success : false,
4002     /**
4003      * @cfg {boolean} valid
4004      * if the drop point is valid for over/enter..
4005      */
4006     valid : false,
4007     // private
4008     isTarget : true,
4009
4010     // private
4011     isNotifyTarget : true,
4012     
4013     /**
4014      * @hide
4015      */
4016     notifyEnter : function(dd, e, data){
4017         this.valid = true;
4018         this.fireEvent('enter', this, dd, e, data);
4019         if(this.overClass){
4020             this.el.addClass(this.overClass);
4021         }
4022         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4023     },
4024
4025     /**
4026      * @hide
4027      */
4028     notifyOver : function(dd, e, data){
4029         this.valid = true;
4030         this.fireEvent('over', this, dd, e, data);
4031         return this.valid ? this.dropAllowed : this.dropNotAllowed;
4032     },
4033
4034     /**
4035      * @hide
4036      */
4037     notifyOut : function(dd, e, data){
4038         this.fireEvent('out', this, dd, e, data);
4039         if(this.overClass){
4040             this.el.removeClass(this.overClass);
4041         }
4042     },
4043
4044     /**
4045      * @hide
4046      */
4047     notifyDrop : function(dd, e, data){
4048         this.success = false;
4049         this.fireEvent('drop', this, dd, e, data);
4050         return this.success;
4051     }
4052 });/*
4053  * Based on:
4054  * Ext JS Library 1.1.1
4055  * Copyright(c) 2006-2007, Ext JS, LLC.
4056  *
4057  * Originally Released Under LGPL - original licence link has changed is not relivant.
4058  *
4059  * Fork - LGPL
4060  * <script type="text/javascript">
4061  */
4062
4063
4064 /**
4065  * @class Roo.dd.DragZone
4066  * @extends Roo.dd.DragSource
4067  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4068  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4069  * @constructor
4070  * @param {String/HTMLElement/Element} el The container element
4071  * @param {Object} config
4072  */
4073 Roo.dd.DragZone = function(el, config){
4074     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4075     if(this.containerScroll){
4076         Roo.dd.ScrollManager.register(this.el);
4077     }
4078 };
4079
4080 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4081     /**
4082      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4083      * for auto scrolling during drag operations.
4084      */
4085     /**
4086      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4087      * method after a failed drop (defaults to "c3daf9" - light blue)
4088      */
4089
4090     /**
4091      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4092      * for a valid target to drag based on the mouse down. Override this method
4093      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4094      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4095      * @param {EventObject} e The mouse down event
4096      * @return {Object} The dragData
4097      */
4098     getDragData : function(e){
4099         return Roo.dd.Registry.getHandleFromEvent(e);
4100     },
4101     
4102     /**
4103      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4104      * this.dragData.ddel
4105      * @param {Number} x The x position of the click on the dragged object
4106      * @param {Number} y The y position of the click on the dragged object
4107      * @return {Boolean} true to continue the drag, false to cancel
4108      */
4109     onInitDrag : function(x, y){
4110         this.proxy.update(this.dragData.ddel.cloneNode(true));
4111         this.onStartDrag(x, y);
4112         return true;
4113     },
4114     
4115     /**
4116      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4117      */
4118     afterRepair : function(){
4119         if(Roo.enableFx){
4120             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4121         }
4122         this.dragging = false;
4123     },
4124
4125     /**
4126      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4127      * the XY of this.dragData.ddel
4128      * @param {EventObject} e The mouse up event
4129      * @return {Array} The xy location (e.g. [100, 200])
4130      */
4131     getRepairXY : function(e){
4132         return Roo.Element.fly(this.dragData.ddel).getXY();  
4133     }
4134 });/*
4135  * Based on:
4136  * Ext JS Library 1.1.1
4137  * Copyright(c) 2006-2007, Ext JS, LLC.
4138  *
4139  * Originally Released Under LGPL - original licence link has changed is not relivant.
4140  *
4141  * Fork - LGPL
4142  * <script type="text/javascript">
4143  */
4144 /**
4145  * @class Roo.dd.DropZone
4146  * @extends Roo.dd.DropTarget
4147  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4148  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4149  * @constructor
4150  * @param {String/HTMLElement/Element} el The container element
4151  * @param {Object} config
4152  */
4153 Roo.dd.DropZone = function(el, config){
4154     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4155 };
4156
4157 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4158     /**
4159      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4160      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4161      * provide your own custom lookup.
4162      * @param {Event} e The event
4163      * @return {Object} data The custom data
4164      */
4165     getTargetFromEvent : function(e){
4166         return Roo.dd.Registry.getTargetFromEvent(e);
4167     },
4168
4169     /**
4170      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4171      * that it has registered.  This method has no default implementation and should be overridden to provide
4172      * node-specific processing if necessary.
4173      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4174      * {@link #getTargetFromEvent} for this node)
4175      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4176      * @param {Event} e The event
4177      * @param {Object} data An object containing arbitrary data supplied by the drag source
4178      */
4179     onNodeEnter : function(n, dd, e, data){
4180         
4181     },
4182
4183     /**
4184      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4185      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4186      * overridden to provide the proper feedback.
4187      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4188      * {@link #getTargetFromEvent} for this node)
4189      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4190      * @param {Event} e The event
4191      * @param {Object} data An object containing arbitrary data supplied by the drag source
4192      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4193      * underlying {@link Roo.dd.StatusProxy} can be updated
4194      */
4195     onNodeOver : function(n, dd, e, data){
4196         return this.dropAllowed;
4197     },
4198
4199     /**
4200      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4201      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4202      * node-specific processing if necessary.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      */
4209     onNodeOut : function(n, dd, e, data){
4210         
4211     },
4212
4213     /**
4214      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4215      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4216      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4217      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4218      * {@link #getTargetFromEvent} for this node)
4219      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4220      * @param {Event} e The event
4221      * @param {Object} data An object containing arbitrary data supplied by the drag source
4222      * @return {Boolean} True if the drop was valid, else false
4223      */
4224     onNodeDrop : function(n, dd, e, data){
4225         return false;
4226     },
4227
4228     /**
4229      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4230      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4231      * it should be overridden to provide the proper feedback if necessary.
4232      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4233      * @param {Event} e The event
4234      * @param {Object} data An object containing arbitrary data supplied by the drag source
4235      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4236      * underlying {@link Roo.dd.StatusProxy} can be updated
4237      */
4238     onContainerOver : function(dd, e, data){
4239         return this.dropNotAllowed;
4240     },
4241
4242     /**
4243      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4244      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4245      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4246      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4247      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4248      * @param {Event} e The event
4249      * @param {Object} data An object containing arbitrary data supplied by the drag source
4250      * @return {Boolean} True if the drop was valid, else false
4251      */
4252     onContainerDrop : function(dd, e, data){
4253         return false;
4254     },
4255
4256     /**
4257      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4258      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4259      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4260      * you should override this method and provide a custom implementation.
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     notifyEnter : function(dd, e, data){
4268         return this.dropNotAllowed;
4269     },
4270
4271     /**
4272      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4273      * This method will be called on every mouse movement while the drag source is over the drop zone.
4274      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4275      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4276      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4277      * registered node, it will call {@link #onContainerOver}.
4278      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4279      * @param {Event} e The event
4280      * @param {Object} data An object containing arbitrary data supplied by the drag source
4281      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4282      * underlying {@link Roo.dd.StatusProxy} can be updated
4283      */
4284     notifyOver : function(dd, e, data){
4285         var n = this.getTargetFromEvent(e);
4286         if(!n){ // not over valid drop target
4287             if(this.lastOverNode){
4288                 this.onNodeOut(this.lastOverNode, dd, e, data);
4289                 this.lastOverNode = null;
4290             }
4291             return this.onContainerOver(dd, e, data);
4292         }
4293         if(this.lastOverNode != n){
4294             if(this.lastOverNode){
4295                 this.onNodeOut(this.lastOverNode, dd, e, data);
4296             }
4297             this.onNodeEnter(n, dd, e, data);
4298             this.lastOverNode = n;
4299         }
4300         return this.onNodeOver(n, dd, e, data);
4301     },
4302
4303     /**
4304      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4305      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4306      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4307      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4308      * @param {Event} e The event
4309      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4310      */
4311     notifyOut : function(dd, e, data){
4312         if(this.lastOverNode){
4313             this.onNodeOut(this.lastOverNode, dd, e, data);
4314             this.lastOverNode = null;
4315         }
4316     },
4317
4318     /**
4319      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4320      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4321      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4322      * otherwise it will call {@link #onContainerDrop}.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag source
4326      * @return {Boolean} True if the drop was valid, else false
4327      */
4328     notifyDrop : function(dd, e, data){
4329         if(this.lastOverNode){
4330             this.onNodeOut(this.lastOverNode, dd, e, data);
4331             this.lastOverNode = null;
4332         }
4333         var n = this.getTargetFromEvent(e);
4334         return n ?
4335             this.onNodeDrop(n, dd, e, data) :
4336             this.onContainerDrop(dd, e, data);
4337     },
4338
4339     // private
4340     triggerCacheRefresh : function(){
4341         Roo.dd.DDM.refreshCache(this.groups);
4342     }  
4343 });/*
4344  * Based on:
4345  * Ext JS Library 1.1.1
4346  * Copyright(c) 2006-2007, Ext JS, LLC.
4347  *
4348  * Originally Released Under LGPL - original licence link has changed is not relivant.
4349  *
4350  * Fork - LGPL
4351  * <script type="text/javascript">
4352  */
4353
4354
4355 /**
4356  * @class Roo.data.SortTypes
4357  * @singleton
4358  * Defines the default sorting (casting?) comparison functions used when sorting data.
4359  */
4360 Roo.data.SortTypes = {
4361     /**
4362      * Default sort that does nothing
4363      * @param {Mixed} s The value being converted
4364      * @return {Mixed} The comparison value
4365      */
4366     none : function(s){
4367         return s;
4368     },
4369     
4370     /**
4371      * The regular expression used to strip tags
4372      * @type {RegExp}
4373      * @property
4374      */
4375     stripTagsRE : /<\/?[^>]+>/gi,
4376     
4377     /**
4378      * Strips all HTML tags to sort on text only
4379      * @param {Mixed} s The value being converted
4380      * @return {String} The comparison value
4381      */
4382     asText : function(s){
4383         return String(s).replace(this.stripTagsRE, "");
4384     },
4385     
4386     /**
4387      * Strips all HTML tags to sort on text only - Case insensitive
4388      * @param {Mixed} s The value being converted
4389      * @return {String} The comparison value
4390      */
4391     asUCText : function(s){
4392         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4393     },
4394     
4395     /**
4396      * Case insensitive string
4397      * @param {Mixed} s The value being converted
4398      * @return {String} The comparison value
4399      */
4400     asUCString : function(s) {
4401         return String(s).toUpperCase();
4402     },
4403     
4404     /**
4405      * Date sorting
4406      * @param {Mixed} s The value being converted
4407      * @return {Number} The comparison value
4408      */
4409     asDate : function(s) {
4410         if(!s){
4411             return 0;
4412         }
4413         if(s instanceof Date){
4414             return s.getTime();
4415         }
4416         return Date.parse(String(s));
4417     },
4418     
4419     /**
4420      * Float sorting
4421      * @param {Mixed} s The value being converted
4422      * @return {Float} The comparison value
4423      */
4424     asFloat : function(s) {
4425         var val = parseFloat(String(s).replace(/,/g, ""));
4426         if(isNaN(val)) val = 0;
4427         return val;
4428     },
4429     
4430     /**
4431      * Integer sorting
4432      * @param {Mixed} s The value being converted
4433      * @return {Number} The comparison value
4434      */
4435     asInt : function(s) {
4436         var val = parseInt(String(s).replace(/,/g, ""));
4437         if(isNaN(val)) val = 0;
4438         return val;
4439     }
4440 };/*
4441  * Based on:
4442  * Ext JS Library 1.1.1
4443  * Copyright(c) 2006-2007, Ext JS, LLC.
4444  *
4445  * Originally Released Under LGPL - original licence link has changed is not relivant.
4446  *
4447  * Fork - LGPL
4448  * <script type="text/javascript">
4449  */
4450
4451 /**
4452 * @class Roo.data.Record
4453  * Instances of this class encapsulate both record <em>definition</em> information, and record
4454  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4455  * to access Records cached in an {@link Roo.data.Store} object.<br>
4456  * <p>
4457  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4458  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4459  * objects.<br>
4460  * <p>
4461  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4462  * @constructor
4463  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4464  * {@link #create}. The parameters are the same.
4465  * @param {Array} data An associative Array of data values keyed by the field name.
4466  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4467  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4468  * not specified an integer id is generated.
4469  */
4470 Roo.data.Record = function(data, id){
4471     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4472     this.data = data;
4473 };
4474
4475 /**
4476  * Generate a constructor for a specific record layout.
4477  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4478  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4479  * Each field definition object may contain the following properties: <ul>
4480  * <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,
4481  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4482  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4483  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4484  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4485  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4486  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4487  * this may be omitted.</p></li>
4488  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4489  * <ul><li>auto (Default, implies no conversion)</li>
4490  * <li>string</li>
4491  * <li>int</li>
4492  * <li>float</li>
4493  * <li>boolean</li>
4494  * <li>date</li></ul></p></li>
4495  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4496  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4497  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4498  * by the Reader into an object that will be stored in the Record. It is passed the
4499  * following parameters:<ul>
4500  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4501  * </ul></p></li>
4502  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4503  * </ul>
4504  * <br>usage:<br><pre><code>
4505 var TopicRecord = Roo.data.Record.create(
4506     {name: 'title', mapping: 'topic_title'},
4507     {name: 'author', mapping: 'username'},
4508     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4509     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4510     {name: 'lastPoster', mapping: 'user2'},
4511     {name: 'excerpt', mapping: 'post_text'}
4512 );
4513
4514 var myNewRecord = new TopicRecord({
4515     title: 'Do my job please',
4516     author: 'noobie',
4517     totalPosts: 1,
4518     lastPost: new Date(),
4519     lastPoster: 'Animal',
4520     excerpt: 'No way dude!'
4521 });
4522 myStore.add(myNewRecord);
4523 </code></pre>
4524  * @method create
4525  * @static
4526  */
4527 Roo.data.Record.create = function(o){
4528     var f = function(){
4529         f.superclass.constructor.apply(this, arguments);
4530     };
4531     Roo.extend(f, Roo.data.Record);
4532     var p = f.prototype;
4533     p.fields = new Roo.util.MixedCollection(false, function(field){
4534         return field.name;
4535     });
4536     for(var i = 0, len = o.length; i < len; i++){
4537         p.fields.add(new Roo.data.Field(o[i]));
4538     }
4539     f.getField = function(name){
4540         return p.fields.get(name);  
4541     };
4542     return f;
4543 };
4544
4545 Roo.data.Record.AUTO_ID = 1000;
4546 Roo.data.Record.EDIT = 'edit';
4547 Roo.data.Record.REJECT = 'reject';
4548 Roo.data.Record.COMMIT = 'commit';
4549
4550 Roo.data.Record.prototype = {
4551     /**
4552      * Readonly flag - true if this record has been modified.
4553      * @type Boolean
4554      */
4555     dirty : false,
4556     editing : false,
4557     error: null,
4558     modified: null,
4559
4560     // private
4561     join : function(store){
4562         this.store = store;
4563     },
4564
4565     /**
4566      * Set the named field to the specified value.
4567      * @param {String} name The name of the field to set.
4568      * @param {Object} value The value to set the field to.
4569      */
4570     set : function(name, value){
4571         if(this.data[name] == value){
4572             return;
4573         }
4574         this.dirty = true;
4575         if(!this.modified){
4576             this.modified = {};
4577         }
4578         if(typeof this.modified[name] == 'undefined'){
4579             this.modified[name] = this.data[name];
4580         }
4581         this.data[name] = value;
4582         if(!this.editing){
4583             this.store.afterEdit(this);
4584         }       
4585     },
4586
4587     /**
4588      * Get the value of the named field.
4589      * @param {String} name The name of the field to get the value of.
4590      * @return {Object} The value of the field.
4591      */
4592     get : function(name){
4593         return this.data[name]; 
4594     },
4595
4596     // private
4597     beginEdit : function(){
4598         this.editing = true;
4599         this.modified = {}; 
4600     },
4601
4602     // private
4603     cancelEdit : function(){
4604         this.editing = false;
4605         delete this.modified;
4606     },
4607
4608     // private
4609     endEdit : function(){
4610         this.editing = false;
4611         if(this.dirty && this.store){
4612             this.store.afterEdit(this);
4613         }
4614     },
4615
4616     /**
4617      * Usually called by the {@link Roo.data.Store} which owns the Record.
4618      * Rejects all changes made to the Record since either creation, or the last commit operation.
4619      * Modified fields are reverted to their original values.
4620      * <p>
4621      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4622      * of reject operations.
4623      */
4624     reject : function(){
4625         var m = this.modified;
4626         for(var n in m){
4627             if(typeof m[n] != "function"){
4628                 this.data[n] = m[n];
4629             }
4630         }
4631         this.dirty = false;
4632         delete this.modified;
4633         this.editing = false;
4634         if(this.store){
4635             this.store.afterReject(this);
4636         }
4637     },
4638
4639     /**
4640      * Usually called by the {@link Roo.data.Store} which owns the Record.
4641      * Commits all changes made to the Record since either creation, or the last commit operation.
4642      * <p>
4643      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4644      * of commit operations.
4645      */
4646     commit : function(){
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterCommit(this);
4652         }
4653     },
4654
4655     // private
4656     hasError : function(){
4657         return this.error != null;
4658     },
4659
4660     // private
4661     clearError : function(){
4662         this.error = null;
4663     },
4664
4665     /**
4666      * Creates a copy of this record.
4667      * @param {String} id (optional) A new record id if you don't want to use this record's id
4668      * @return {Record}
4669      */
4670     copy : function(newId) {
4671         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4672     }
4673 };/*
4674  * Based on:
4675  * Ext JS Library 1.1.1
4676  * Copyright(c) 2006-2007, Ext JS, LLC.
4677  *
4678  * Originally Released Under LGPL - original licence link has changed is not relivant.
4679  *
4680  * Fork - LGPL
4681  * <script type="text/javascript">
4682  */
4683
4684
4685
4686 /**
4687  * @class Roo.data.Store
4688  * @extends Roo.util.Observable
4689  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4690  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4691  * <p>
4692  * 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
4693  * has no knowledge of the format of the data returned by the Proxy.<br>
4694  * <p>
4695  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4696  * instances from the data object. These records are cached and made available through accessor functions.
4697  * @constructor
4698  * Creates a new Store.
4699  * @param {Object} config A config object containing the objects needed for the Store to access data,
4700  * and read the data into Records.
4701  */
4702 Roo.data.Store = function(config){
4703     this.data = new Roo.util.MixedCollection(false);
4704     this.data.getKey = function(o){
4705         return o.id;
4706     };
4707     this.baseParams = {};
4708     // private
4709     this.paramNames = {
4710         "start" : "start",
4711         "limit" : "limit",
4712         "sort" : "sort",
4713         "dir" : "dir"
4714     };
4715
4716     if(config && config.data){
4717         this.inlineData = config.data;
4718         delete config.data;
4719     }
4720
4721     Roo.apply(this, config);
4722     
4723     if(this.reader){ // reader passed
4724         this.reader = Roo.factory(this.reader, Roo.data);
4725         this.reader.xmodule = this.xmodule || false;
4726         if(!this.recordType){
4727             this.recordType = this.reader.recordType;
4728         }
4729         if(this.reader.onMetaChange){
4730             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4731         }
4732     }
4733
4734     if(this.recordType){
4735         this.fields = this.recordType.prototype.fields;
4736     }
4737     this.modified = [];
4738
4739     this.addEvents({
4740         /**
4741          * @event datachanged
4742          * Fires when the data cache has changed, and a widget which is using this Store
4743          * as a Record cache should refresh its view.
4744          * @param {Store} this
4745          */
4746         datachanged : true,
4747         /**
4748          * @event metachange
4749          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4750          * @param {Store} this
4751          * @param {Object} meta The JSON metadata
4752          */
4753         metachange : true,
4754         /**
4755          * @event add
4756          * Fires when Records have been added to the Store
4757          * @param {Store} this
4758          * @param {Roo.data.Record[]} records The array of Records added
4759          * @param {Number} index The index at which the record(s) were added
4760          */
4761         add : true,
4762         /**
4763          * @event remove
4764          * Fires when a Record has been removed from the Store
4765          * @param {Store} this
4766          * @param {Roo.data.Record} record The Record that was removed
4767          * @param {Number} index The index at which the record was removed
4768          */
4769         remove : true,
4770         /**
4771          * @event update
4772          * Fires when a Record has been updated
4773          * @param {Store} this
4774          * @param {Roo.data.Record} record The Record that was updated
4775          * @param {String} operation The update operation being performed.  Value may be one of:
4776          * <pre><code>
4777  Roo.data.Record.EDIT
4778  Roo.data.Record.REJECT
4779  Roo.data.Record.COMMIT
4780          * </code></pre>
4781          */
4782         update : true,
4783         /**
4784          * @event clear
4785          * Fires when the data cache has been cleared.
4786          * @param {Store} this
4787          */
4788         clear : true,
4789         /**
4790          * @event beforeload
4791          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4792          * the load action will be canceled.
4793          * @param {Store} this
4794          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4795          */
4796         beforeload : true,
4797         /**
4798          * @event load
4799          * Fires after a new set of Records has been loaded.
4800          * @param {Store} this
4801          * @param {Roo.data.Record[]} records The Records that were loaded
4802          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4803          */
4804         load : true,
4805         /**
4806          * @event loadexception
4807          * Fires if an exception occurs in the Proxy during loading.
4808          * Called with the signature of the Proxy's "loadexception" event.
4809          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4810          * 
4811          * @param {Proxy} 
4812          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4813          * @param {Object} load options 
4814          * @param {Object} jsonData from your request (normally this contains the Exception)
4815          */
4816         loadexception : true
4817     });
4818     
4819     if(this.proxy){
4820         this.proxy = Roo.factory(this.proxy, Roo.data);
4821         this.proxy.xmodule = this.xmodule || false;
4822         this.relayEvents(this.proxy,  ["loadexception"]);
4823     }
4824     this.sortToggle = {};
4825
4826     Roo.data.Store.superclass.constructor.call(this);
4827
4828     if(this.inlineData){
4829         this.loadData(this.inlineData);
4830         delete this.inlineData;
4831     }
4832 };
4833 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4834      /**
4835     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4836     * without a remote query - used by combo/forms at present.
4837     */
4838     
4839     /**
4840     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4841     */
4842     /**
4843     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4844     */
4845     /**
4846     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4847     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4848     */
4849     /**
4850     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4851     * on any HTTP request
4852     */
4853     /**
4854     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4855     */
4856     /**
4857     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4858     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4859     */
4860     remoteSort : false,
4861
4862     /**
4863     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4864      * loaded or when a record is removed. (defaults to false).
4865     */
4866     pruneModifiedRecords : false,
4867
4868     // private
4869     lastOptions : null,
4870
4871     /**
4872      * Add Records to the Store and fires the add event.
4873      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4874      */
4875     add : function(records){
4876         records = [].concat(records);
4877         for(var i = 0, len = records.length; i < len; i++){
4878             records[i].join(this);
4879         }
4880         var index = this.data.length;
4881         this.data.addAll(records);
4882         this.fireEvent("add", this, records, index);
4883     },
4884
4885     /**
4886      * Remove a Record from the Store and fires the remove event.
4887      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4888      */
4889     remove : function(record){
4890         var index = this.data.indexOf(record);
4891         this.data.removeAt(index);
4892         if(this.pruneModifiedRecords){
4893             this.modified.remove(record);
4894         }
4895         this.fireEvent("remove", this, record, index);
4896     },
4897
4898     /**
4899      * Remove all Records from the Store and fires the clear event.
4900      */
4901     removeAll : function(){
4902         this.data.clear();
4903         if(this.pruneModifiedRecords){
4904             this.modified = [];
4905         }
4906         this.fireEvent("clear", this);
4907     },
4908
4909     /**
4910      * Inserts Records to the Store at the given index and fires the add event.
4911      * @param {Number} index The start index at which to insert the passed Records.
4912      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4913      */
4914     insert : function(index, records){
4915         records = [].concat(records);
4916         for(var i = 0, len = records.length; i < len; i++){
4917             this.data.insert(index, records[i]);
4918             records[i].join(this);
4919         }
4920         this.fireEvent("add", this, records, index);
4921     },
4922
4923     /**
4924      * Get the index within the cache of the passed Record.
4925      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4926      * @return {Number} The index of the passed Record. Returns -1 if not found.
4927      */
4928     indexOf : function(record){
4929         return this.data.indexOf(record);
4930     },
4931
4932     /**
4933      * Get the index within the cache of the Record with the passed id.
4934      * @param {String} id The id of the Record to find.
4935      * @return {Number} The index of the Record. Returns -1 if not found.
4936      */
4937     indexOfId : function(id){
4938         return this.data.indexOfKey(id);
4939     },
4940
4941     /**
4942      * Get the Record with the specified id.
4943      * @param {String} id The id of the Record to find.
4944      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4945      */
4946     getById : function(id){
4947         return this.data.key(id);
4948     },
4949
4950     /**
4951      * Get the Record at the specified index.
4952      * @param {Number} index The index of the Record to find.
4953      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4954      */
4955     getAt : function(index){
4956         return this.data.itemAt(index);
4957     },
4958
4959     /**
4960      * Returns a range of Records between specified indices.
4961      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4962      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4963      * @return {Roo.data.Record[]} An array of Records
4964      */
4965     getRange : function(start, end){
4966         return this.data.getRange(start, end);
4967     },
4968
4969     // private
4970     storeOptions : function(o){
4971         o = Roo.apply({}, o);
4972         delete o.callback;
4973         delete o.scope;
4974         this.lastOptions = o;
4975     },
4976
4977     /**
4978      * Loads the Record cache from the configured Proxy using the configured Reader.
4979      * <p>
4980      * If using remote paging, then the first load call must specify the <em>start</em>
4981      * and <em>limit</em> properties in the options.params property to establish the initial
4982      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4983      * <p>
4984      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4985      * and this call will return before the new data has been loaded. Perform any post-processing
4986      * in a callback function, or in a "load" event handler.</strong>
4987      * <p>
4988      * @param {Object} options An object containing properties which control loading options:<ul>
4989      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4990      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4991      * passed the following arguments:<ul>
4992      * <li>r : Roo.data.Record[]</li>
4993      * <li>options: Options object from the load call</li>
4994      * <li>success: Boolean success indicator</li></ul></li>
4995      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
4996      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
4997      * </ul>
4998      */
4999     load : function(options){
5000         options = options || {};
5001         if(this.fireEvent("beforeload", this, options) !== false){
5002             this.storeOptions(options);
5003             var p = Roo.apply(options.params || {}, this.baseParams);
5004             // if meta was not loaded from remote source.. try requesting it.
5005             if (!this.reader.metaFromRemote) {
5006                 p._requestMeta = 1;
5007             }
5008             if(this.sortInfo && this.remoteSort){
5009                 var pn = this.paramNames;
5010                 p[pn["sort"]] = this.sortInfo.field;
5011                 p[pn["dir"]] = this.sortInfo.direction;
5012             }
5013             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5014         }
5015     },
5016
5017     /**
5018      * Reloads the Record cache from the configured Proxy using the configured Reader and
5019      * the options from the last load operation performed.
5020      * @param {Object} options (optional) An object containing properties which may override the options
5021      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5022      * the most recently used options are reused).
5023      */
5024     reload : function(options){
5025         this.load(Roo.applyIf(options||{}, this.lastOptions));
5026     },
5027
5028     // private
5029     // Called as a callback by the Reader during a load operation.
5030     loadRecords : function(o, options, success){
5031         if(!o || success === false){
5032             if(success !== false){
5033                 this.fireEvent("load", this, [], options);
5034             }
5035             if(options.callback){
5036                 options.callback.call(options.scope || this, [], options, false);
5037             }
5038             return;
5039         }
5040         // if data returned failure - throw an exception.
5041         if (o.success === false) {
5042             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5043             return;
5044         }
5045         var r = o.records, t = o.totalRecords || r.length;
5046         if(!options || options.add !== true){
5047             if(this.pruneModifiedRecords){
5048                 this.modified = [];
5049             }
5050             for(var i = 0, len = r.length; i < len; i++){
5051                 r[i].join(this);
5052             }
5053             if(this.snapshot){
5054                 this.data = this.snapshot;
5055                 delete this.snapshot;
5056             }
5057             this.data.clear();
5058             this.data.addAll(r);
5059             this.totalLength = t;
5060             this.applySort();
5061             this.fireEvent("datachanged", this);
5062         }else{
5063             this.totalLength = Math.max(t, this.data.length+r.length);
5064             this.add(r);
5065         }
5066         this.fireEvent("load", this, r, options);
5067         if(options.callback){
5068             options.callback.call(options.scope || this, r, options, true);
5069         }
5070     },
5071
5072     /**
5073      * Loads data from a passed data block. A Reader which understands the format of the data
5074      * must have been configured in the constructor.
5075      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5076      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5077      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5078      */
5079     loadData : function(o, append){
5080         var r = this.reader.readRecords(o);
5081         this.loadRecords(r, {add: append}, true);
5082     },
5083
5084     /**
5085      * Gets the number of cached records.
5086      * <p>
5087      * <em>If using paging, this may not be the total size of the dataset. If the data object
5088      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5089      * the data set size</em>
5090      */
5091     getCount : function(){
5092         return this.data.length || 0;
5093     },
5094
5095     /**
5096      * Gets the total number of records in the dataset as returned by the server.
5097      * <p>
5098      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5099      * the dataset size</em>
5100      */
5101     getTotalCount : function(){
5102         return this.totalLength || 0;
5103     },
5104
5105     /**
5106      * Returns the sort state of the Store as an object with two properties:
5107      * <pre><code>
5108  field {String} The name of the field by which the Records are sorted
5109  direction {String} The sort order, "ASC" or "DESC"
5110      * </code></pre>
5111      */
5112     getSortState : function(){
5113         return this.sortInfo;
5114     },
5115
5116     // private
5117     applySort : function(){
5118         if(this.sortInfo && !this.remoteSort){
5119             var s = this.sortInfo, f = s.field;
5120             var st = this.fields.get(f).sortType;
5121             var fn = function(r1, r2){
5122                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5123                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5124             };
5125             this.data.sort(s.direction, fn);
5126             if(this.snapshot && this.snapshot != this.data){
5127                 this.snapshot.sort(s.direction, fn);
5128             }
5129         }
5130     },
5131
5132     /**
5133      * Sets the default sort column and order to be used by the next load operation.
5134      * @param {String} fieldName The name of the field to sort by.
5135      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5136      */
5137     setDefaultSort : function(field, dir){
5138         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5139     },
5140
5141     /**
5142      * Sort the Records.
5143      * If remote sorting is used, the sort is performed on the server, and the cache is
5144      * reloaded. If local sorting is used, the cache is sorted internally.
5145      * @param {String} fieldName The name of the field to sort by.
5146      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5147      */
5148     sort : function(fieldName, dir){
5149         var f = this.fields.get(fieldName);
5150         if(!dir){
5151             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5152                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5153             }else{
5154                 dir = f.sortDir;
5155             }
5156         }
5157         this.sortToggle[f.name] = dir;
5158         this.sortInfo = {field: f.name, direction: dir};
5159         if(!this.remoteSort){
5160             this.applySort();
5161             this.fireEvent("datachanged", this);
5162         }else{
5163             this.load(this.lastOptions);
5164         }
5165     },
5166
5167     /**
5168      * Calls the specified function for each of the Records in the cache.
5169      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5170      * Returning <em>false</em> aborts and exits the iteration.
5171      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5172      */
5173     each : function(fn, scope){
5174         this.data.each(fn, scope);
5175     },
5176
5177     /**
5178      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5179      * (e.g., during paging).
5180      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5181      */
5182     getModifiedRecords : function(){
5183         return this.modified;
5184     },
5185
5186     // private
5187     createFilterFn : function(property, value, anyMatch){
5188         if(!value.exec){ // not a regex
5189             value = String(value);
5190             if(value.length == 0){
5191                 return false;
5192             }
5193             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5194         }
5195         return function(r){
5196             return value.test(r.data[property]);
5197         };
5198     },
5199
5200     /**
5201      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5202      * @param {String} property A field on your records
5203      * @param {Number} start The record index to start at (defaults to 0)
5204      * @param {Number} end The last record index to include (defaults to length - 1)
5205      * @return {Number} The sum
5206      */
5207     sum : function(property, start, end){
5208         var rs = this.data.items, v = 0;
5209         start = start || 0;
5210         end = (end || end === 0) ? end : rs.length-1;
5211
5212         for(var i = start; i <= end; i++){
5213             v += (rs[i].data[property] || 0);
5214         }
5215         return v;
5216     },
5217
5218     /**
5219      * Filter the records by a specified property.
5220      * @param {String} field A field on your records
5221      * @param {String/RegExp} value Either a string that the field
5222      * should start with or a RegExp to test against the field
5223      * @param {Boolean} anyMatch True to match any part not just the beginning
5224      */
5225     filter : function(property, value, anyMatch){
5226         var fn = this.createFilterFn(property, value, anyMatch);
5227         return fn ? this.filterBy(fn) : this.clearFilter();
5228     },
5229
5230     /**
5231      * Filter by a function. The specified function will be called with each
5232      * record in this data source. If the function returns true the record is included,
5233      * otherwise it is filtered.
5234      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5235      * @param {Object} scope (optional) The scope of the function (defaults to this)
5236      */
5237     filterBy : function(fn, scope){
5238         this.snapshot = this.snapshot || this.data;
5239         this.data = this.queryBy(fn, scope||this);
5240         this.fireEvent("datachanged", this);
5241     },
5242
5243     /**
5244      * Query the records by a specified property.
5245      * @param {String} field A field on your records
5246      * @param {String/RegExp} value Either a string that the field
5247      * should start with or a RegExp to test against the field
5248      * @param {Boolean} anyMatch True to match any part not just the beginning
5249      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5250      */
5251     query : function(property, value, anyMatch){
5252         var fn = this.createFilterFn(property, value, anyMatch);
5253         return fn ? this.queryBy(fn) : this.data.clone();
5254     },
5255
5256     /**
5257      * Query by a function. The specified function will be called with each
5258      * record in this data source. If the function returns true the record is included
5259      * in the results.
5260      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5261      * @param {Object} scope (optional) The scope of the function (defaults to this)
5262       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5263      **/
5264     queryBy : function(fn, scope){
5265         var data = this.snapshot || this.data;
5266         return data.filterBy(fn, scope||this);
5267     },
5268
5269     /**
5270      * Collects unique values for a particular dataIndex from this store.
5271      * @param {String} dataIndex The property to collect
5272      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5273      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5274      * @return {Array} An array of the unique values
5275      **/
5276     collect : function(dataIndex, allowNull, bypassFilter){
5277         var d = (bypassFilter === true && this.snapshot) ?
5278                 this.snapshot.items : this.data.items;
5279         var v, sv, r = [], l = {};
5280         for(var i = 0, len = d.length; i < len; i++){
5281             v = d[i].data[dataIndex];
5282             sv = String(v);
5283             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5284                 l[sv] = true;
5285                 r[r.length] = v;
5286             }
5287         }
5288         return r;
5289     },
5290
5291     /**
5292      * Revert to a view of the Record cache with no filtering applied.
5293      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5294      */
5295     clearFilter : function(suppressEvent){
5296         if(this.snapshot && this.snapshot != this.data){
5297             this.data = this.snapshot;
5298             delete this.snapshot;
5299             if(suppressEvent !== true){
5300                 this.fireEvent("datachanged", this);
5301             }
5302         }
5303     },
5304
5305     // private
5306     afterEdit : function(record){
5307         if(this.modified.indexOf(record) == -1){
5308             this.modified.push(record);
5309         }
5310         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5311     },
5312
5313     // private
5314     afterReject : function(record){
5315         this.modified.remove(record);
5316         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5317     },
5318
5319     // private
5320     afterCommit : function(record){
5321         this.modified.remove(record);
5322         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5323     },
5324
5325     /**
5326      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5327      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5328      */
5329     commitChanges : function(){
5330         var m = this.modified.slice(0);
5331         this.modified = [];
5332         for(var i = 0, len = m.length; i < len; i++){
5333             m[i].commit();
5334         }
5335     },
5336
5337     /**
5338      * Cancel outstanding changes on all changed records.
5339      */
5340     rejectChanges : function(){
5341         var m = this.modified.slice(0);
5342         this.modified = [];
5343         for(var i = 0, len = m.length; i < len; i++){
5344             m[i].reject();
5345         }
5346     },
5347
5348     onMetaChange : function(meta, rtype, o){
5349         this.recordType = rtype;
5350         this.fields = rtype.prototype.fields;
5351         delete this.snapshot;
5352         this.sortInfo = meta.sortInfo || this.sortInfo;
5353         this.modified = [];
5354         this.fireEvent('metachange', this, this.reader.meta);
5355     }
5356 });/*
5357  * Based on:
5358  * Ext JS Library 1.1.1
5359  * Copyright(c) 2006-2007, Ext JS, LLC.
5360  *
5361  * Originally Released Under LGPL - original licence link has changed is not relivant.
5362  *
5363  * Fork - LGPL
5364  * <script type="text/javascript">
5365  */
5366
5367 /**
5368  * @class Roo.data.SimpleStore
5369  * @extends Roo.data.Store
5370  * Small helper class to make creating Stores from Array data easier.
5371  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5372  * @cfg {Array} fields An array of field definition objects, or field name strings.
5373  * @cfg {Array} data The multi-dimensional array of data
5374  * @constructor
5375  * @param {Object} config
5376  */
5377 Roo.data.SimpleStore = function(config){
5378     Roo.data.SimpleStore.superclass.constructor.call(this, {
5379         isLocal : true,
5380         reader: new Roo.data.ArrayReader({
5381                 id: config.id
5382             },
5383             Roo.data.Record.create(config.fields)
5384         ),
5385         proxy : new Roo.data.MemoryProxy(config.data)
5386     });
5387     this.load();
5388 };
5389 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5390  * Based on:
5391  * Ext JS Library 1.1.1
5392  * Copyright(c) 2006-2007, Ext JS, LLC.
5393  *
5394  * Originally Released Under LGPL - original licence link has changed is not relivant.
5395  *
5396  * Fork - LGPL
5397  * <script type="text/javascript">
5398  */
5399
5400 /**
5401 /**
5402  * @extends Roo.data.Store
5403  * @class Roo.data.JsonStore
5404  * Small helper class to make creating Stores for JSON data easier. <br/>
5405 <pre><code>
5406 var store = new Roo.data.JsonStore({
5407     url: 'get-images.php',
5408     root: 'images',
5409     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5410 });
5411 </code></pre>
5412  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5413  * JsonReader and HttpProxy (unless inline data is provided).</b>
5414  * @cfg {Array} fields An array of field definition objects, or field name strings.
5415  * @constructor
5416  * @param {Object} config
5417  */
5418 Roo.data.JsonStore = function(c){
5419     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5420         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5421         reader: new Roo.data.JsonReader(c, c.fields)
5422     }));
5423 };
5424 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435  
5436 Roo.data.Field = function(config){
5437     if(typeof config == "string"){
5438         config = {name: config};
5439     }
5440     Roo.apply(this, config);
5441     
5442     if(!this.type){
5443         this.type = "auto";
5444     }
5445     
5446     var st = Roo.data.SortTypes;
5447     // named sortTypes are supported, here we look them up
5448     if(typeof this.sortType == "string"){
5449         this.sortType = st[this.sortType];
5450     }
5451     
5452     // set default sortType for strings and dates
5453     if(!this.sortType){
5454         switch(this.type){
5455             case "string":
5456                 this.sortType = st.asUCString;
5457                 break;
5458             case "date":
5459                 this.sortType = st.asDate;
5460                 break;
5461             default:
5462                 this.sortType = st.none;
5463         }
5464     }
5465
5466     // define once
5467     var stripRe = /[\$,%]/g;
5468
5469     // prebuilt conversion function for this field, instead of
5470     // switching every time we're reading a value
5471     if(!this.convert){
5472         var cv, dateFormat = this.dateFormat;
5473         switch(this.type){
5474             case "":
5475             case "auto":
5476             case undefined:
5477                 cv = function(v){ return v; };
5478                 break;
5479             case "string":
5480                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5481                 break;
5482             case "int":
5483                 cv = function(v){
5484                     return v !== undefined && v !== null && v !== '' ?
5485                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5486                     };
5487                 break;
5488             case "float":
5489                 cv = function(v){
5490                     return v !== undefined && v !== null && v !== '' ?
5491                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5492                     };
5493                 break;
5494             case "bool":
5495             case "boolean":
5496                 cv = function(v){ return v === true || v === "true" || v == 1; };
5497                 break;
5498             case "date":
5499                 cv = function(v){
5500                     if(!v){
5501                         return '';
5502                     }
5503                     if(v instanceof Date){
5504                         return v;
5505                     }
5506                     if(dateFormat){
5507                         if(dateFormat == "timestamp"){
5508                             return new Date(v*1000);
5509                         }
5510                         return Date.parseDate(v, dateFormat);
5511                     }
5512                     var parsed = Date.parse(v);
5513                     return parsed ? new Date(parsed) : null;
5514                 };
5515              break;
5516             
5517         }
5518         this.convert = cv;
5519     }
5520 };
5521
5522 Roo.data.Field.prototype = {
5523     dateFormat: null,
5524     defaultValue: "",
5525     mapping: null,
5526     sortType : null,
5527     sortDir : "ASC"
5528 };/*
5529  * Based on:
5530  * Ext JS Library 1.1.1
5531  * Copyright(c) 2006-2007, Ext JS, LLC.
5532  *
5533  * Originally Released Under LGPL - original licence link has changed is not relivant.
5534  *
5535  * Fork - LGPL
5536  * <script type="text/javascript">
5537  */
5538  
5539 // Base class for reading structured data from a data source.  This class is intended to be
5540 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5541
5542 /**
5543  * @class Roo.data.DataReader
5544  * Base class for reading structured data from a data source.  This class is intended to be
5545  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5546  */
5547
5548 Roo.data.DataReader = function(meta, recordType){
5549     
5550     this.meta = meta;
5551     
5552     this.recordType = recordType instanceof Array ? 
5553         Roo.data.Record.create(recordType) : recordType;
5554 };
5555
5556 Roo.data.DataReader.prototype = {
5557      /**
5558      * Create an empty record
5559      * @param {Object} data (optional) - overlay some values
5560      * @return {Roo.data.Record} record created.
5561      */
5562     newRow :  function(d) {
5563         var da =  {};
5564         this.recordType.prototype.fields.each(function(c) {
5565             switch( c.type) {
5566                 case 'int' : da[c.name] = 0; break;
5567                 case 'date' : da[c.name] = new Date(); break;
5568                 case 'float' : da[c.name] = 0.0; break;
5569                 case 'boolean' : da[c.name] = false; break;
5570                 default : da[c.name] = ""; break;
5571             }
5572             
5573         });
5574         return new this.recordType(Roo.apply(da, d));
5575     }
5576     
5577 };/*
5578  * Based on:
5579  * Ext JS Library 1.1.1
5580  * Copyright(c) 2006-2007, Ext JS, LLC.
5581  *
5582  * Originally Released Under LGPL - original licence link has changed is not relivant.
5583  *
5584  * Fork - LGPL
5585  * <script type="text/javascript">
5586  */
5587
5588 /**
5589  * @class Roo.data.DataProxy
5590  * @extends Roo.data.Observable
5591  * This class is an abstract base class for implementations which provide retrieval of
5592  * unformatted data objects.<br>
5593  * <p>
5594  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5595  * (of the appropriate type which knows how to parse the data object) to provide a block of
5596  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5597  * <p>
5598  * Custom implementations must implement the load method as described in
5599  * {@link Roo.data.HttpProxy#load}.
5600  */
5601 Roo.data.DataProxy = function(){
5602     this.addEvents({
5603         /**
5604          * @event beforeload
5605          * Fires before a network request is made to retrieve a data object.
5606          * @param {Object} This DataProxy object.
5607          * @param {Object} params The params parameter to the load function.
5608          */
5609         beforeload : true,
5610         /**
5611          * @event load
5612          * Fires before the load method's callback is called.
5613          * @param {Object} This DataProxy object.
5614          * @param {Object} o The data object.
5615          * @param {Object} arg The callback argument object passed to the load function.
5616          */
5617         load : true,
5618         /**
5619          * @event loadexception
5620          * Fires if an Exception occurs during data retrieval.
5621          * @param {Object} This DataProxy object.
5622          * @param {Object} o The data object.
5623          * @param {Object} arg The callback argument object passed to the load function.
5624          * @param {Object} e The Exception.
5625          */
5626         loadexception : true
5627     });
5628     Roo.data.DataProxy.superclass.constructor.call(this);
5629 };
5630
5631 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5632
5633     /**
5634      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5635      */
5636 /*
5637  * Based on:
5638  * Ext JS Library 1.1.1
5639  * Copyright(c) 2006-2007, Ext JS, LLC.
5640  *
5641  * Originally Released Under LGPL - original licence link has changed is not relivant.
5642  *
5643  * Fork - LGPL
5644  * <script type="text/javascript">
5645  */
5646 /**
5647  * @class Roo.data.MemoryProxy
5648  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5649  * to the Reader when its load method is called.
5650  * @constructor
5651  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5652  */
5653 Roo.data.MemoryProxy = function(data){
5654     if (data.data) {
5655         data = data.data;
5656     }
5657     Roo.data.MemoryProxy.superclass.constructor.call(this);
5658     this.data = data;
5659 };
5660
5661 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5662     /**
5663      * Load data from the requested source (in this case an in-memory
5664      * data object passed to the constructor), read the data object into
5665      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5666      * process that block using the passed callback.
5667      * @param {Object} params This parameter is not used by the MemoryProxy class.
5668      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5669      * object into a block of Roo.data.Records.
5670      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5671      * The function must be passed <ul>
5672      * <li>The Record block object</li>
5673      * <li>The "arg" argument from the load function</li>
5674      * <li>A boolean success indicator</li>
5675      * </ul>
5676      * @param {Object} scope The scope in which to call the callback
5677      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5678      */
5679     load : function(params, reader, callback, scope, arg){
5680         params = params || {};
5681         var result;
5682         try {
5683             result = reader.readRecords(this.data);
5684         }catch(e){
5685             this.fireEvent("loadexception", this, arg, null, e);
5686             callback.call(scope, null, arg, false);
5687             return;
5688         }
5689         callback.call(scope, result, arg, true);
5690     },
5691     
5692     // private
5693     update : function(params, records){
5694         
5695     }
5696 });/*
5697  * Based on:
5698  * Ext JS Library 1.1.1
5699  * Copyright(c) 2006-2007, Ext JS, LLC.
5700  *
5701  * Originally Released Under LGPL - original licence link has changed is not relivant.
5702  *
5703  * Fork - LGPL
5704  * <script type="text/javascript">
5705  */
5706 /**
5707  * @class Roo.data.HttpProxy
5708  * @extends Roo.data.DataProxy
5709  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5710  * configured to reference a certain URL.<br><br>
5711  * <p>
5712  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5713  * from which the running page was served.<br><br>
5714  * <p>
5715  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5716  * <p>
5717  * Be aware that to enable the browser to parse an XML document, the server must set
5718  * the Content-Type header in the HTTP response to "text/xml".
5719  * @constructor
5720  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5721  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5722  * will be used to make the request.
5723  */
5724 Roo.data.HttpProxy = function(conn){
5725     Roo.data.HttpProxy.superclass.constructor.call(this);
5726     // is conn a conn config or a real conn?
5727     this.conn = conn;
5728     this.useAjax = !conn || !conn.events;
5729   
5730 };
5731
5732 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5733     // thse are take from connection...
5734     
5735     /**
5736      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5737      */
5738     /**
5739      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5740      * extra parameters to each request made by this object. (defaults to undefined)
5741      */
5742     /**
5743      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5744      *  to each request made by this object. (defaults to undefined)
5745      */
5746     /**
5747      * @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)
5748      */
5749     /**
5750      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5751      */
5752      /**
5753      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5754      * @type Boolean
5755      */
5756   
5757
5758     /**
5759      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5760      * @type Boolean
5761      */
5762     /**
5763      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5764      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5765      * a finer-grained basis than the DataProxy events.
5766      */
5767     getConnection : function(){
5768         return this.useAjax ? Roo.Ajax : this.conn;
5769     },
5770
5771     /**
5772      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5773      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5774      * process that block using the passed callback.
5775      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5776      * for the request to the remote server.
5777      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5778      * object into a block of Roo.data.Records.
5779      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5780      * The function must be passed <ul>
5781      * <li>The Record block object</li>
5782      * <li>The "arg" argument from the load function</li>
5783      * <li>A boolean success indicator</li>
5784      * </ul>
5785      * @param {Object} scope The scope in which to call the callback
5786      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5787      */
5788     load : function(params, reader, callback, scope, arg){
5789         if(this.fireEvent("beforeload", this, params) !== false){
5790             var  o = {
5791                 params : params || {},
5792                 request: {
5793                     callback : callback,
5794                     scope : scope,
5795                     arg : arg
5796                 },
5797                 reader: reader,
5798                 callback : this.loadResponse,
5799                 scope: this
5800             };
5801             if(this.useAjax){
5802                 Roo.applyIf(o, this.conn);
5803                 if(this.activeRequest){
5804                     Roo.Ajax.abort(this.activeRequest);
5805                 }
5806                 this.activeRequest = Roo.Ajax.request(o);
5807             }else{
5808                 this.conn.request(o);
5809             }
5810         }else{
5811             callback.call(scope||this, null, arg, false);
5812         }
5813     },
5814
5815     // private
5816     loadResponse : function(o, success, response){
5817         delete this.activeRequest;
5818         if(!success){
5819             this.fireEvent("loadexception", this, o, response);
5820             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5821             return;
5822         }
5823         var result;
5824         try {
5825             result = o.reader.read(response);
5826         }catch(e){
5827             this.fireEvent("loadexception", this, o, response, e);
5828             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5829             return;
5830         }
5831         
5832         this.fireEvent("load", this, o, o.request.arg);
5833         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5834     },
5835
5836     // private
5837     update : function(dataSet){
5838
5839     },
5840
5841     // private
5842     updateResponse : function(dataSet){
5843
5844     }
5845 });/*
5846  * Based on:
5847  * Ext JS Library 1.1.1
5848  * Copyright(c) 2006-2007, Ext JS, LLC.
5849  *
5850  * Originally Released Under LGPL - original licence link has changed is not relivant.
5851  *
5852  * Fork - LGPL
5853  * <script type="text/javascript">
5854  */
5855
5856 /**
5857  * @class Roo.data.ScriptTagProxy
5858  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5859  * other than the originating domain of the running page.<br><br>
5860  * <p>
5861  * <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
5862  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5863  * <p>
5864  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5865  * source code that is used as the source inside a &lt;script> tag.<br><br>
5866  * <p>
5867  * In order for the browser to process the returned data, the server must wrap the data object
5868  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5869  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5870  * depending on whether the callback name was passed:
5871  * <p>
5872  * <pre><code>
5873 boolean scriptTag = false;
5874 String cb = request.getParameter("callback");
5875 if (cb != null) {
5876     scriptTag = true;
5877     response.setContentType("text/javascript");
5878 } else {
5879     response.setContentType("application/x-json");
5880 }
5881 Writer out = response.getWriter();
5882 if (scriptTag) {
5883     out.write(cb + "(");
5884 }
5885 out.print(dataBlock.toJsonString());
5886 if (scriptTag) {
5887     out.write(");");
5888 }
5889 </pre></code>
5890  *
5891  * @constructor
5892  * @param {Object} config A configuration object.
5893  */
5894 Roo.data.ScriptTagProxy = function(config){
5895     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5896     Roo.apply(this, config);
5897     this.head = document.getElementsByTagName("head")[0];
5898 };
5899
5900 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5901
5902 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5903     /**
5904      * @cfg {String} url The URL from which to request the data object.
5905      */
5906     /**
5907      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5908      */
5909     timeout : 30000,
5910     /**
5911      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5912      * the server the name of the callback function set up by the load call to process the returned data object.
5913      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5914      * javascript output which calls this named function passing the data object as its only parameter.
5915      */
5916     callbackParam : "callback",
5917     /**
5918      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5919      * name to the request.
5920      */
5921     nocache : true,
5922
5923     /**
5924      * Load data from the configured URL, read the data object into
5925      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5926      * process that block using the passed callback.
5927      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5928      * for the request to the remote server.
5929      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5930      * object into a block of Roo.data.Records.
5931      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5932      * The function must be passed <ul>
5933      * <li>The Record block object</li>
5934      * <li>The "arg" argument from the load function</li>
5935      * <li>A boolean success indicator</li>
5936      * </ul>
5937      * @param {Object} scope The scope in which to call the callback
5938      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5939      */
5940     load : function(params, reader, callback, scope, arg){
5941         if(this.fireEvent("beforeload", this, params) !== false){
5942
5943             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5944
5945             var url = this.url;
5946             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5947             if(this.nocache){
5948                 url += "&_dc=" + (new Date().getTime());
5949             }
5950             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5951             var trans = {
5952                 id : transId,
5953                 cb : "stcCallback"+transId,
5954                 scriptId : "stcScript"+transId,
5955                 params : params,
5956                 arg : arg,
5957                 url : url,
5958                 callback : callback,
5959                 scope : scope,
5960                 reader : reader
5961             };
5962             var conn = this;
5963
5964             window[trans.cb] = function(o){
5965                 conn.handleResponse(o, trans);
5966             };
5967
5968             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5969
5970             if(this.autoAbort !== false){
5971                 this.abort();
5972             }
5973
5974             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5975
5976             var script = document.createElement("script");
5977             script.setAttribute("src", url);
5978             script.setAttribute("type", "text/javascript");
5979             script.setAttribute("id", trans.scriptId);
5980             this.head.appendChild(script);
5981
5982             this.trans = trans;
5983         }else{
5984             callback.call(scope||this, null, arg, false);
5985         }
5986     },
5987
5988     // private
5989     isLoading : function(){
5990         return this.trans ? true : false;
5991     },
5992
5993     /**
5994      * Abort the current server request.
5995      */
5996     abort : function(){
5997         if(this.isLoading()){
5998             this.destroyTrans(this.trans);
5999         }
6000     },
6001
6002     // private
6003     destroyTrans : function(trans, isLoaded){
6004         this.head.removeChild(document.getElementById(trans.scriptId));
6005         clearTimeout(trans.timeoutId);
6006         if(isLoaded){
6007             window[trans.cb] = undefined;
6008             try{
6009                 delete window[trans.cb];
6010             }catch(e){}
6011         }else{
6012             // if hasn't been loaded, wait for load to remove it to prevent script error
6013             window[trans.cb] = function(){
6014                 window[trans.cb] = undefined;
6015                 try{
6016                     delete window[trans.cb];
6017                 }catch(e){}
6018             };
6019         }
6020     },
6021
6022     // private
6023     handleResponse : function(o, trans){
6024         this.trans = false;
6025         this.destroyTrans(trans, true);
6026         var result;
6027         try {
6028             result = trans.reader.readRecords(o);
6029         }catch(e){
6030             this.fireEvent("loadexception", this, o, trans.arg, e);
6031             trans.callback.call(trans.scope||window, null, trans.arg, false);
6032             return;
6033         }
6034         this.fireEvent("load", this, o, trans.arg);
6035         trans.callback.call(trans.scope||window, result, trans.arg, true);
6036     },
6037
6038     // private
6039     handleFailure : function(trans){
6040         this.trans = false;
6041         this.destroyTrans(trans, false);
6042         this.fireEvent("loadexception", this, null, trans.arg);
6043         trans.callback.call(trans.scope||window, null, trans.arg, false);
6044     }
6045 });/*
6046  * Based on:
6047  * Ext JS Library 1.1.1
6048  * Copyright(c) 2006-2007, Ext JS, LLC.
6049  *
6050  * Originally Released Under LGPL - original licence link has changed is not relivant.
6051  *
6052  * Fork - LGPL
6053  * <script type="text/javascript">
6054  */
6055
6056 /**
6057  * @class Roo.data.JsonReader
6058  * @extends Roo.data.DataReader
6059  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6060  * based on mappings in a provided Roo.data.Record constructor.
6061  * 
6062  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6063  * in the reply previously. 
6064  * 
6065  * <p>
6066  * Example code:
6067  * <pre><code>
6068 var RecordDef = Roo.data.Record.create([
6069     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6070     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6071 ]);
6072 var myReader = new Roo.data.JsonReader({
6073     totalProperty: "results",    // The property which contains the total dataset size (optional)
6074     root: "rows",                // The property which contains an Array of row objects
6075     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6076 }, RecordDef);
6077 </code></pre>
6078  * <p>
6079  * This would consume a JSON file like this:
6080  * <pre><code>
6081 { 'results': 2, 'rows': [
6082     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6083     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6084 }
6085 </code></pre>
6086  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6087  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6088  * paged from the remote server.
6089  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6090  * @cfg {String} root name of the property which contains the Array of row objects.
6091  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6092  * @constructor
6093  * Create a new JsonReader
6094  * @param {Object} meta Metadata configuration options
6095  * @param {Object} recordType Either an Array of field definition objects,
6096  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6097  */
6098 Roo.data.JsonReader = function(meta, recordType){
6099     
6100     meta = meta || {};
6101     // set some defaults:
6102     Roo.applyIf(meta, {
6103         totalProperty: 'total',
6104         successProperty : 'success',
6105         root : 'data',
6106         id : 'id'
6107     });
6108     
6109     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6110 };
6111 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6112     
6113     /**
6114      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6115      * Used by Store query builder to append _requestMeta to params.
6116      * 
6117      */
6118     metaFromRemote : false,
6119     /**
6120      * This method is only used by a DataProxy which has retrieved data from a remote server.
6121      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6122      * @return {Object} data A data block which is used by an Roo.data.Store object as
6123      * a cache of Roo.data.Records.
6124      */
6125     read : function(response){
6126         var json = response.responseText;
6127        
6128         var o = /* eval:var:o */ eval("("+json+")");
6129         if(!o) {
6130             throw {message: "JsonReader.read: Json object not found"};
6131         }
6132         
6133         if(o.metaData){
6134             
6135             delete this.ef;
6136             this.metaFromRemote = true;
6137             this.meta = o.metaData;
6138             this.recordType = Roo.data.Record.create(o.metaData.fields);
6139             this.onMetaChange(this.meta, this.recordType, o);
6140         }
6141         return this.readRecords(o);
6142     },
6143
6144     // private function a store will implement
6145     onMetaChange : function(meta, recordType, o){
6146
6147     },
6148
6149     /**
6150          * @ignore
6151          */
6152     simpleAccess: function(obj, subsc) {
6153         return obj[subsc];
6154     },
6155
6156         /**
6157          * @ignore
6158          */
6159     getJsonAccessor: function(){
6160         var re = /[\[\.]/;
6161         return function(expr) {
6162             try {
6163                 return(re.test(expr))
6164                     ? new Function("obj", "return obj." + expr)
6165                     : function(obj){
6166                         return obj[expr];
6167                     };
6168             } catch(e){}
6169             return Roo.emptyFn;
6170         };
6171     }(),
6172
6173     /**
6174      * Create a data block containing Roo.data.Records from an XML document.
6175      * @param {Object} o An object which contains an Array of row objects in the property specified
6176      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6177      * which contains the total size of the dataset.
6178      * @return {Object} data A data block which is used by an Roo.data.Store object as
6179      * a cache of Roo.data.Records.
6180      */
6181     readRecords : function(o){
6182         /**
6183          * After any data loads, the raw JSON data is available for further custom processing.
6184          * @type Object
6185          */
6186         this.jsonData = o;
6187         var s = this.meta, Record = this.recordType,
6188             f = Record.prototype.fields, fi = f.items, fl = f.length;
6189
6190 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6191         if (!this.ef) {
6192             if(s.totalProperty) {
6193                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6194                 }
6195                 if(s.successProperty) {
6196                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6197                 }
6198                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6199                 if (s.id) {
6200                         var g = this.getJsonAccessor(s.id);
6201                         this.getId = function(rec) {
6202                                 var r = g(rec);
6203                                 return (r === undefined || r === "") ? null : r;
6204                         };
6205                 } else {
6206                         this.getId = function(){return null;};
6207                 }
6208             this.ef = [];
6209             for(var jj = 0; jj < fl; jj++){
6210                 f = fi[jj];
6211                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6212                 this.ef[jj] = this.getJsonAccessor(map);
6213             }
6214         }
6215
6216         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6217         if(s.totalProperty){
6218             var vt = parseInt(this.getTotal(o), 10);
6219             if(!isNaN(vt)){
6220                 totalRecords = vt;
6221             }
6222         }
6223         if(s.successProperty){
6224             var vs = this.getSuccess(o);
6225             if(vs === false || vs === 'false'){
6226                 success = false;
6227             }
6228         }
6229         var records = [];
6230             for(var i = 0; i < c; i++){
6231                     var n = root[i];
6232                 var values = {};
6233                 var id = this.getId(n);
6234                 for(var j = 0; j < fl; j++){
6235                     f = fi[j];
6236                 var v = this.ef[j](n);
6237                 if (!f.convert) {
6238                     Roo.log('missing convert for ' + f.name);
6239                     Roo.log(f);
6240                     continue;
6241                 }
6242                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6243                 }
6244                 var record = new Record(values, id);
6245                 record.json = n;
6246                 records[i] = record;
6247             }
6248             return {
6249                 success : success,
6250                 records : records,
6251                 totalRecords : totalRecords
6252             };
6253     }
6254 });/*
6255  * Based on:
6256  * Ext JS Library 1.1.1
6257  * Copyright(c) 2006-2007, Ext JS, LLC.
6258  *
6259  * Originally Released Under LGPL - original licence link has changed is not relivant.
6260  *
6261  * Fork - LGPL
6262  * <script type="text/javascript">
6263  */
6264
6265 /**
6266  * @class Roo.data.XmlReader
6267  * @extends Roo.data.DataReader
6268  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6269  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6270  * <p>
6271  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6272  * header in the HTTP response must be set to "text/xml".</em>
6273  * <p>
6274  * Example code:
6275  * <pre><code>
6276 var RecordDef = Roo.data.Record.create([
6277    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6278    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6279 ]);
6280 var myReader = new Roo.data.XmlReader({
6281    totalRecords: "results", // The element which contains the total dataset size (optional)
6282    record: "row",           // The repeated element which contains row information
6283    id: "id"                 // The element within the row that provides an ID for the record (optional)
6284 }, RecordDef);
6285 </code></pre>
6286  * <p>
6287  * This would consume an XML file like this:
6288  * <pre><code>
6289 &lt;?xml?>
6290 &lt;dataset>
6291  &lt;results>2&lt;/results>
6292  &lt;row>
6293    &lt;id>1&lt;/id>
6294    &lt;name>Bill&lt;/name>
6295    &lt;occupation>Gardener&lt;/occupation>
6296  &lt;/row>
6297  &lt;row>
6298    &lt;id>2&lt;/id>
6299    &lt;name>Ben&lt;/name>
6300    &lt;occupation>Horticulturalist&lt;/occupation>
6301  &lt;/row>
6302 &lt;/dataset>
6303 </code></pre>
6304  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6305  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6306  * paged from the remote server.
6307  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6308  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6309  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6310  * a record identifier value.
6311  * @constructor
6312  * Create a new XmlReader
6313  * @param {Object} meta Metadata configuration options
6314  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6315  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6316  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6317  */
6318 Roo.data.XmlReader = function(meta, recordType){
6319     meta = meta || {};
6320     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6321 };
6322 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6323     /**
6324      * This method is only used by a DataProxy which has retrieved data from a remote server.
6325          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6326          * to contain a method called 'responseXML' that returns an XML document object.
6327      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6328      * a cache of Roo.data.Records.
6329      */
6330     read : function(response){
6331         var doc = response.responseXML;
6332         if(!doc) {
6333             throw {message: "XmlReader.read: XML Document not available"};
6334         }
6335         return this.readRecords(doc);
6336     },
6337
6338     /**
6339      * Create a data block containing Roo.data.Records from an XML document.
6340          * @param {Object} doc A parsed XML document.
6341      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6342      * a cache of Roo.data.Records.
6343      */
6344     readRecords : function(doc){
6345         /**
6346          * After any data loads/reads, the raw XML Document is available for further custom processing.
6347          * @type XMLDocument
6348          */
6349         this.xmlData = doc;
6350         var root = doc.documentElement || doc;
6351         var q = Roo.DomQuery;
6352         var recordType = this.recordType, fields = recordType.prototype.fields;
6353         var sid = this.meta.id;
6354         var totalRecords = 0, success = true;
6355         if(this.meta.totalRecords){
6356             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6357         }
6358         
6359         if(this.meta.success){
6360             var sv = q.selectValue(this.meta.success, root, true);
6361             success = sv !== false && sv !== 'false';
6362         }
6363         var records = [];
6364         var ns = q.select(this.meta.record, root);
6365         for(var i = 0, len = ns.length; i < len; i++) {
6366                 var n = ns[i];
6367                 var values = {};
6368                 var id = sid ? q.selectValue(sid, n) : undefined;
6369                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6370                     var f = fields.items[j];
6371                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6372                     v = f.convert(v);
6373                     values[f.name] = v;
6374                 }
6375                 var record = new recordType(values, id);
6376                 record.node = n;
6377                 records[records.length] = record;
6378             }
6379
6380             return {
6381                 success : success,
6382                 records : records,
6383                 totalRecords : totalRecords || records.length
6384             };
6385     }
6386 });/*
6387  * Based on:
6388  * Ext JS Library 1.1.1
6389  * Copyright(c) 2006-2007, Ext JS, LLC.
6390  *
6391  * Originally Released Under LGPL - original licence link has changed is not relivant.
6392  *
6393  * Fork - LGPL
6394  * <script type="text/javascript">
6395  */
6396
6397 /**
6398  * @class Roo.data.ArrayReader
6399  * @extends Roo.data.DataReader
6400  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6401  * Each element of that Array represents a row of data fields. The
6402  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6403  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6404  * <p>
6405  * Example code:.
6406  * <pre><code>
6407 var RecordDef = Roo.data.Record.create([
6408     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6409     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6410 ]);
6411 var myReader = new Roo.data.ArrayReader({
6412     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6413 }, RecordDef);
6414 </code></pre>
6415  * <p>
6416  * This would consume an Array like this:
6417  * <pre><code>
6418 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6419   </code></pre>
6420  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6421  * @constructor
6422  * Create a new JsonReader
6423  * @param {Object} meta Metadata configuration options.
6424  * @param {Object} recordType Either an Array of field definition objects
6425  * as specified to {@link Roo.data.Record#create},
6426  * or an {@link Roo.data.Record} object
6427  * created using {@link Roo.data.Record#create}.
6428  */
6429 Roo.data.ArrayReader = function(meta, recordType){
6430     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6431 };
6432
6433 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6434     /**
6435      * Create a data block containing Roo.data.Records from an XML document.
6436      * @param {Object} o An Array of row objects which represents the dataset.
6437      * @return {Object} data A data block which is used by an Roo.data.Store object as
6438      * a cache of Roo.data.Records.
6439      */
6440     readRecords : function(o){
6441         var sid = this.meta ? this.meta.id : null;
6442         var recordType = this.recordType, fields = recordType.prototype.fields;
6443         var records = [];
6444         var root = o;
6445             for(var i = 0; i < root.length; i++){
6446                     var n = root[i];
6447                 var values = {};
6448                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6449                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6450                 var f = fields.items[j];
6451                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6452                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6453                 v = f.convert(v);
6454                 values[f.name] = v;
6455             }
6456                 var record = new recordType(values, id);
6457                 record.json = n;
6458                 records[records.length] = record;
6459             }
6460             return {
6461                 records : records,
6462                 totalRecords : records.length
6463             };
6464     }
6465 });/*
6466  * Based on:
6467  * Ext JS Library 1.1.1
6468  * Copyright(c) 2006-2007, Ext JS, LLC.
6469  *
6470  * Originally Released Under LGPL - original licence link has changed is not relivant.
6471  *
6472  * Fork - LGPL
6473  * <script type="text/javascript">
6474  */
6475
6476
6477 /**
6478  * @class Roo.data.Tree
6479  * @extends Roo.util.Observable
6480  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6481  * in the tree have most standard DOM functionality.
6482  * @constructor
6483  * @param {Node} root (optional) The root node
6484  */
6485 Roo.data.Tree = function(root){
6486    this.nodeHash = {};
6487    /**
6488     * The root node for this tree
6489     * @type Node
6490     */
6491    this.root = null;
6492    if(root){
6493        this.setRootNode(root);
6494    }
6495    this.addEvents({
6496        /**
6497         * @event append
6498         * Fires when a new child node is appended to a node in this tree.
6499         * @param {Tree} tree The owner tree
6500         * @param {Node} parent The parent node
6501         * @param {Node} node The newly appended node
6502         * @param {Number} index The index of the newly appended node
6503         */
6504        "append" : true,
6505        /**
6506         * @event remove
6507         * Fires when a child node is removed from a node in this tree.
6508         * @param {Tree} tree The owner tree
6509         * @param {Node} parent The parent node
6510         * @param {Node} node The child node removed
6511         */
6512        "remove" : true,
6513        /**
6514         * @event move
6515         * Fires when a node is moved to a new location in the tree
6516         * @param {Tree} tree The owner tree
6517         * @param {Node} node The node moved
6518         * @param {Node} oldParent The old parent of this node
6519         * @param {Node} newParent The new parent of this node
6520         * @param {Number} index The index it was moved to
6521         */
6522        "move" : true,
6523        /**
6524         * @event insert
6525         * Fires when a new child node is inserted in a node in this tree.
6526         * @param {Tree} tree The owner tree
6527         * @param {Node} parent The parent node
6528         * @param {Node} node The child node inserted
6529         * @param {Node} refNode The child node the node was inserted before
6530         */
6531        "insert" : true,
6532        /**
6533         * @event beforeappend
6534         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6535         * @param {Tree} tree The owner tree
6536         * @param {Node} parent The parent node
6537         * @param {Node} node The child node to be appended
6538         */
6539        "beforeappend" : true,
6540        /**
6541         * @event beforeremove
6542         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node to be removed
6546         */
6547        "beforeremove" : true,
6548        /**
6549         * @event beforemove
6550         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node being moved
6553         * @param {Node} oldParent The parent of the node
6554         * @param {Node} newParent The new parent the node is moving to
6555         * @param {Number} index The index it is being moved to
6556         */
6557        "beforemove" : true,
6558        /**
6559         * @event beforeinsert
6560         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node to be inserted
6564         * @param {Node} refNode The child node the node is being inserted before
6565         */
6566        "beforeinsert" : true
6567    });
6568
6569     Roo.data.Tree.superclass.constructor.call(this);
6570 };
6571
6572 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6573     pathSeparator: "/",
6574
6575     proxyNodeEvent : function(){
6576         return this.fireEvent.apply(this, arguments);
6577     },
6578
6579     /**
6580      * Returns the root node for this tree.
6581      * @return {Node}
6582      */
6583     getRootNode : function(){
6584         return this.root;
6585     },
6586
6587     /**
6588      * Sets the root node for this tree.
6589      * @param {Node} node
6590      * @return {Node}
6591      */
6592     setRootNode : function(node){
6593         this.root = node;
6594         node.ownerTree = this;
6595         node.isRoot = true;
6596         this.registerNode(node);
6597         return node;
6598     },
6599
6600     /**
6601      * Gets a node in this tree by its id.
6602      * @param {String} id
6603      * @return {Node}
6604      */
6605     getNodeById : function(id){
6606         return this.nodeHash[id];
6607     },
6608
6609     registerNode : function(node){
6610         this.nodeHash[node.id] = node;
6611     },
6612
6613     unregisterNode : function(node){
6614         delete this.nodeHash[node.id];
6615     },
6616
6617     toString : function(){
6618         return "[Tree"+(this.id?" "+this.id:"")+"]";
6619     }
6620 });
6621
6622 /**
6623  * @class Roo.data.Node
6624  * @extends Roo.util.Observable
6625  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6626  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6627  * @constructor
6628  * @param {Object} attributes The attributes/config for the node
6629  */
6630 Roo.data.Node = function(attributes){
6631     /**
6632      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6633      * @type {Object}
6634      */
6635     this.attributes = attributes || {};
6636     this.leaf = this.attributes.leaf;
6637     /**
6638      * The node id. @type String
6639      */
6640     this.id = this.attributes.id;
6641     if(!this.id){
6642         this.id = Roo.id(null, "ynode-");
6643         this.attributes.id = this.id;
6644     }
6645     /**
6646      * All child nodes of this node. @type Array
6647      */
6648     this.childNodes = [];
6649     if(!this.childNodes.indexOf){ // indexOf is a must
6650         this.childNodes.indexOf = function(o){
6651             for(var i = 0, len = this.length; i < len; i++){
6652                 if(this[i] == o) {
6653                     return i;
6654                 }
6655             }
6656             return -1;
6657         };
6658     }
6659     /**
6660      * The parent node for this node. @type Node
6661      */
6662     this.parentNode = null;
6663     /**
6664      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6665      */
6666     this.firstChild = null;
6667     /**
6668      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6669      */
6670     this.lastChild = null;
6671     /**
6672      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6673      */
6674     this.previousSibling = null;
6675     /**
6676      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6677      */
6678     this.nextSibling = null;
6679
6680     this.addEvents({
6681        /**
6682         * @event append
6683         * Fires when a new child node is appended
6684         * @param {Tree} tree The owner tree
6685         * @param {Node} this This node
6686         * @param {Node} node The newly appended node
6687         * @param {Number} index The index of the newly appended node
6688         */
6689        "append" : true,
6690        /**
6691         * @event remove
6692         * Fires when a child node is removed
6693         * @param {Tree} tree The owner tree
6694         * @param {Node} this This node
6695         * @param {Node} node The removed node
6696         */
6697        "remove" : true,
6698        /**
6699         * @event move
6700         * Fires when this node is moved to a new location in the tree
6701         * @param {Tree} tree The owner tree
6702         * @param {Node} this This node
6703         * @param {Node} oldParent The old parent of this node
6704         * @param {Node} newParent The new parent of this node
6705         * @param {Number} index The index it was moved to
6706         */
6707        "move" : true,
6708        /**
6709         * @event insert
6710         * Fires when a new child node is inserted.
6711         * @param {Tree} tree The owner tree
6712         * @param {Node} this This node
6713         * @param {Node} node The child node inserted
6714         * @param {Node} refNode The child node the node was inserted before
6715         */
6716        "insert" : true,
6717        /**
6718         * @event beforeappend
6719         * Fires before a new child is appended, return false to cancel the append.
6720         * @param {Tree} tree The owner tree
6721         * @param {Node} this This node
6722         * @param {Node} node The child node to be appended
6723         */
6724        "beforeappend" : true,
6725        /**
6726         * @event beforeremove
6727         * Fires before a child is removed, return false to cancel the remove.
6728         * @param {Tree} tree The owner tree
6729         * @param {Node} this This node
6730         * @param {Node} node The child node to be removed
6731         */
6732        "beforeremove" : true,
6733        /**
6734         * @event beforemove
6735         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6736         * @param {Tree} tree The owner tree
6737         * @param {Node} this This node
6738         * @param {Node} oldParent The parent of this node
6739         * @param {Node} newParent The new parent this node is moving to
6740         * @param {Number} index The index it is being moved to
6741         */
6742        "beforemove" : true,
6743        /**
6744         * @event beforeinsert
6745         * Fires before a new child is inserted, return false to cancel the insert.
6746         * @param {Tree} tree The owner tree
6747         * @param {Node} this This node
6748         * @param {Node} node The child node to be inserted
6749         * @param {Node} refNode The child node the node is being inserted before
6750         */
6751        "beforeinsert" : true
6752    });
6753     this.listeners = this.attributes.listeners;
6754     Roo.data.Node.superclass.constructor.call(this);
6755 };
6756
6757 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6758     fireEvent : function(evtName){
6759         // first do standard event for this node
6760         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6761             return false;
6762         }
6763         // then bubble it up to the tree if the event wasn't cancelled
6764         var ot = this.getOwnerTree();
6765         if(ot){
6766             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6767                 return false;
6768             }
6769         }
6770         return true;
6771     },
6772
6773     /**
6774      * Returns true if this node is a leaf
6775      * @return {Boolean}
6776      */
6777     isLeaf : function(){
6778         return this.leaf === true;
6779     },
6780
6781     // private
6782     setFirstChild : function(node){
6783         this.firstChild = node;
6784     },
6785
6786     //private
6787     setLastChild : function(node){
6788         this.lastChild = node;
6789     },
6790
6791
6792     /**
6793      * Returns true if this node is the last child of its parent
6794      * @return {Boolean}
6795      */
6796     isLast : function(){
6797        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6798     },
6799
6800     /**
6801      * Returns true if this node is the first child of its parent
6802      * @return {Boolean}
6803      */
6804     isFirst : function(){
6805        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6806     },
6807
6808     hasChildNodes : function(){
6809         return !this.isLeaf() && this.childNodes.length > 0;
6810     },
6811
6812     /**
6813      * Insert node(s) as the last child node of this node.
6814      * @param {Node/Array} node The node or Array of nodes to append
6815      * @return {Node} The appended node if single append, or null if an array was passed
6816      */
6817     appendChild : function(node){
6818         var multi = false;
6819         if(node instanceof Array){
6820             multi = node;
6821         }else if(arguments.length > 1){
6822             multi = arguments;
6823         }
6824         // if passed an array or multiple args do them one by one
6825         if(multi){
6826             for(var i = 0, len = multi.length; i < len; i++) {
6827                 this.appendChild(multi[i]);
6828             }
6829         }else{
6830             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6831                 return false;
6832             }
6833             var index = this.childNodes.length;
6834             var oldParent = node.parentNode;
6835             // it's a move, make sure we move it cleanly
6836             if(oldParent){
6837                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6838                     return false;
6839                 }
6840                 oldParent.removeChild(node);
6841             }
6842             index = this.childNodes.length;
6843             if(index == 0){
6844                 this.setFirstChild(node);
6845             }
6846             this.childNodes.push(node);
6847             node.parentNode = this;
6848             var ps = this.childNodes[index-1];
6849             if(ps){
6850                 node.previousSibling = ps;
6851                 ps.nextSibling = node;
6852             }else{
6853                 node.previousSibling = null;
6854             }
6855             node.nextSibling = null;
6856             this.setLastChild(node);
6857             node.setOwnerTree(this.getOwnerTree());
6858             this.fireEvent("append", this.ownerTree, this, node, index);
6859             if(oldParent){
6860                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6861             }
6862             return node;
6863         }
6864     },
6865
6866     /**
6867      * Removes a child node from this node.
6868      * @param {Node} node The node to remove
6869      * @return {Node} The removed node
6870      */
6871     removeChild : function(node){
6872         var index = this.childNodes.indexOf(node);
6873         if(index == -1){
6874             return false;
6875         }
6876         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6877             return false;
6878         }
6879
6880         // remove it from childNodes collection
6881         this.childNodes.splice(index, 1);
6882
6883         // update siblings
6884         if(node.previousSibling){
6885             node.previousSibling.nextSibling = node.nextSibling;
6886         }
6887         if(node.nextSibling){
6888             node.nextSibling.previousSibling = node.previousSibling;
6889         }
6890
6891         // update child refs
6892         if(this.firstChild == node){
6893             this.setFirstChild(node.nextSibling);
6894         }
6895         if(this.lastChild == node){
6896             this.setLastChild(node.previousSibling);
6897         }
6898
6899         node.setOwnerTree(null);
6900         // clear any references from the node
6901         node.parentNode = null;
6902         node.previousSibling = null;
6903         node.nextSibling = null;
6904         this.fireEvent("remove", this.ownerTree, this, node);
6905         return node;
6906     },
6907
6908     /**
6909      * Inserts the first node before the second node in this nodes childNodes collection.
6910      * @param {Node} node The node to insert
6911      * @param {Node} refNode The node to insert before (if null the node is appended)
6912      * @return {Node} The inserted node
6913      */
6914     insertBefore : function(node, refNode){
6915         if(!refNode){ // like standard Dom, refNode can be null for append
6916             return this.appendChild(node);
6917         }
6918         // nothing to do
6919         if(node == refNode){
6920             return false;
6921         }
6922
6923         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6924             return false;
6925         }
6926         var index = this.childNodes.indexOf(refNode);
6927         var oldParent = node.parentNode;
6928         var refIndex = index;
6929
6930         // when moving internally, indexes will change after remove
6931         if(oldParent == this && this.childNodes.indexOf(node) < index){
6932             refIndex--;
6933         }
6934
6935         // it's a move, make sure we move it cleanly
6936         if(oldParent){
6937             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6938                 return false;
6939             }
6940             oldParent.removeChild(node);
6941         }
6942         if(refIndex == 0){
6943             this.setFirstChild(node);
6944         }
6945         this.childNodes.splice(refIndex, 0, node);
6946         node.parentNode = this;
6947         var ps = this.childNodes[refIndex-1];
6948         if(ps){
6949             node.previousSibling = ps;
6950             ps.nextSibling = node;
6951         }else{
6952             node.previousSibling = null;
6953         }
6954         node.nextSibling = refNode;
6955         refNode.previousSibling = node;
6956         node.setOwnerTree(this.getOwnerTree());
6957         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6958         if(oldParent){
6959             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6960         }
6961         return node;
6962     },
6963
6964     /**
6965      * Returns the child node at the specified index.
6966      * @param {Number} index
6967      * @return {Node}
6968      */
6969     item : function(index){
6970         return this.childNodes[index];
6971     },
6972
6973     /**
6974      * Replaces one child node in this node with another.
6975      * @param {Node} newChild The replacement node
6976      * @param {Node} oldChild The node to replace
6977      * @return {Node} The replaced node
6978      */
6979     replaceChild : function(newChild, oldChild){
6980         this.insertBefore(newChild, oldChild);
6981         this.removeChild(oldChild);
6982         return oldChild;
6983     },
6984
6985     /**
6986      * Returns the index of a child node
6987      * @param {Node} node
6988      * @return {Number} The index of the node or -1 if it was not found
6989      */
6990     indexOf : function(child){
6991         return this.childNodes.indexOf(child);
6992     },
6993
6994     /**
6995      * Returns the tree this node is in.
6996      * @return {Tree}
6997      */
6998     getOwnerTree : function(){
6999         // if it doesn't have one, look for one
7000         if(!this.ownerTree){
7001             var p = this;
7002             while(p){
7003                 if(p.ownerTree){
7004                     this.ownerTree = p.ownerTree;
7005                     break;
7006                 }
7007                 p = p.parentNode;
7008             }
7009         }
7010         return this.ownerTree;
7011     },
7012
7013     /**
7014      * Returns depth of this node (the root node has a depth of 0)
7015      * @return {Number}
7016      */
7017     getDepth : function(){
7018         var depth = 0;
7019         var p = this;
7020         while(p.parentNode){
7021             ++depth;
7022             p = p.parentNode;
7023         }
7024         return depth;
7025     },
7026
7027     // private
7028     setOwnerTree : function(tree){
7029         // if it's move, we need to update everyone
7030         if(tree != this.ownerTree){
7031             if(this.ownerTree){
7032                 this.ownerTree.unregisterNode(this);
7033             }
7034             this.ownerTree = tree;
7035             var cs = this.childNodes;
7036             for(var i = 0, len = cs.length; i < len; i++) {
7037                 cs[i].setOwnerTree(tree);
7038             }
7039             if(tree){
7040                 tree.registerNode(this);
7041             }
7042         }
7043     },
7044
7045     /**
7046      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7047      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7048      * @return {String} The path
7049      */
7050     getPath : function(attr){
7051         attr = attr || "id";
7052         var p = this.parentNode;
7053         var b = [this.attributes[attr]];
7054         while(p){
7055             b.unshift(p.attributes[attr]);
7056             p = p.parentNode;
7057         }
7058         var sep = this.getOwnerTree().pathSeparator;
7059         return sep + b.join(sep);
7060     },
7061
7062     /**
7063      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7064      * function call will be the scope provided or the current node. The arguments to the function
7065      * will be the args provided or the current node. If the function returns false at any point,
7066      * the bubble is stopped.
7067      * @param {Function} fn The function to call
7068      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7069      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7070      */
7071     bubble : function(fn, scope, args){
7072         var p = this;
7073         while(p){
7074             if(fn.call(scope || p, args || p) === false){
7075                 break;
7076             }
7077             p = p.parentNode;
7078         }
7079     },
7080
7081     /**
7082      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7083      * function call will be the scope provided or the current node. The arguments to the function
7084      * will be the args provided or the current node. If the function returns false at any point,
7085      * the cascade is stopped on that branch.
7086      * @param {Function} fn The function to call
7087      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7088      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7089      */
7090     cascade : function(fn, scope, args){
7091         if(fn.call(scope || this, args || this) !== false){
7092             var cs = this.childNodes;
7093             for(var i = 0, len = cs.length; i < len; i++) {
7094                 cs[i].cascade(fn, scope, args);
7095             }
7096         }
7097     },
7098
7099     /**
7100      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the iteration stops.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     eachChild : function(fn, scope, args){
7109         var cs = this.childNodes;
7110         for(var i = 0, len = cs.length; i < len; i++) {
7111                 if(fn.call(scope || this, args || cs[i]) === false){
7112                     break;
7113                 }
7114         }
7115     },
7116
7117     /**
7118      * Finds the first child that has the attribute with the specified value.
7119      * @param {String} attribute The attribute name
7120      * @param {Mixed} value The value to search for
7121      * @return {Node} The found child or null if none was found
7122      */
7123     findChild : function(attribute, value){
7124         var cs = this.childNodes;
7125         for(var i = 0, len = cs.length; i < len; i++) {
7126                 if(cs[i].attributes[attribute] == value){
7127                     return cs[i];
7128                 }
7129         }
7130         return null;
7131     },
7132
7133     /**
7134      * Finds the first child by a custom function. The child matches if the function passed
7135      * returns true.
7136      * @param {Function} fn
7137      * @param {Object} scope (optional)
7138      * @return {Node} The found child or null if none was found
7139      */
7140     findChildBy : function(fn, scope){
7141         var cs = this.childNodes;
7142         for(var i = 0, len = cs.length; i < len; i++) {
7143                 if(fn.call(scope||cs[i], cs[i]) === true){
7144                     return cs[i];
7145                 }
7146         }
7147         return null;
7148     },
7149
7150     /**
7151      * Sorts this nodes children using the supplied sort function
7152      * @param {Function} fn
7153      * @param {Object} scope (optional)
7154      */
7155     sort : function(fn, scope){
7156         var cs = this.childNodes;
7157         var len = cs.length;
7158         if(len > 0){
7159             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7160             cs.sort(sortFn);
7161             for(var i = 0; i < len; i++){
7162                 var n = cs[i];
7163                 n.previousSibling = cs[i-1];
7164                 n.nextSibling = cs[i+1];
7165                 if(i == 0){
7166                     this.setFirstChild(n);
7167                 }
7168                 if(i == len-1){
7169                     this.setLastChild(n);
7170                 }
7171             }
7172         }
7173     },
7174
7175     /**
7176      * Returns true if this node is an ancestor (at any point) of the passed node.
7177      * @param {Node} node
7178      * @return {Boolean}
7179      */
7180     contains : function(node){
7181         return node.isAncestor(this);
7182     },
7183
7184     /**
7185      * Returns true if the passed node is an ancestor (at any point) of this node.
7186      * @param {Node} node
7187      * @return {Boolean}
7188      */
7189     isAncestor : function(node){
7190         var p = this.parentNode;
7191         while(p){
7192             if(p == node){
7193                 return true;
7194             }
7195             p = p.parentNode;
7196         }
7197         return false;
7198     },
7199
7200     toString : function(){
7201         return "[Node"+(this.id?" "+this.id:"")+"]";
7202     }
7203 });/*
7204  * Based on:
7205  * Ext JS Library 1.1.1
7206  * Copyright(c) 2006-2007, Ext JS, LLC.
7207  *
7208  * Originally Released Under LGPL - original licence link has changed is not relivant.
7209  *
7210  * Fork - LGPL
7211  * <script type="text/javascript">
7212  */
7213  
7214
7215 /**
7216  * @class Roo.ComponentMgr
7217  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7218  * @singleton
7219  */
7220 Roo.ComponentMgr = function(){
7221     var all = new Roo.util.MixedCollection();
7222
7223     return {
7224         /**
7225          * Registers a component.
7226          * @param {Roo.Component} c The component
7227          */
7228         register : function(c){
7229             all.add(c);
7230         },
7231
7232         /**
7233          * Unregisters a component.
7234          * @param {Roo.Component} c The component
7235          */
7236         unregister : function(c){
7237             all.remove(c);
7238         },
7239
7240         /**
7241          * Returns a component by id
7242          * @param {String} id The component id
7243          */
7244         get : function(id){
7245             return all.get(id);
7246         },
7247
7248         /**
7249          * Registers a function that will be called when a specified component is added to ComponentMgr
7250          * @param {String} id The component id
7251          * @param {Funtction} fn The callback function
7252          * @param {Object} scope The scope of the callback
7253          */
7254         onAvailable : function(id, fn, scope){
7255             all.on("add", function(index, o){
7256                 if(o.id == id){
7257                     fn.call(scope || o, o);
7258                     all.un("add", fn, scope);
7259                 }
7260             });
7261         }
7262     };
7263 }();/*
7264  * Based on:
7265  * Ext JS Library 1.1.1
7266  * Copyright(c) 2006-2007, Ext JS, LLC.
7267  *
7268  * Originally Released Under LGPL - original licence link has changed is not relivant.
7269  *
7270  * Fork - LGPL
7271  * <script type="text/javascript">
7272  */
7273  
7274 /**
7275  * @class Roo.Component
7276  * @extends Roo.util.Observable
7277  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7278  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7279  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7280  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7281  * All visual components (widgets) that require rendering into a layout should subclass Component.
7282  * @constructor
7283  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7284  * 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
7285  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7286  */
7287 Roo.Component = function(config){
7288     config = config || {};
7289     if(config.tagName || config.dom || typeof config == "string"){ // element object
7290         config = {el: config, id: config.id || config};
7291     }
7292     this.initialConfig = config;
7293
7294     Roo.apply(this, config);
7295     this.addEvents({
7296         /**
7297          * @event disable
7298          * Fires after the component is disabled.
7299              * @param {Roo.Component} this
7300              */
7301         disable : true,
7302         /**
7303          * @event enable
7304          * Fires after the component is enabled.
7305              * @param {Roo.Component} this
7306              */
7307         enable : true,
7308         /**
7309          * @event beforeshow
7310          * Fires before the component is shown.  Return false to stop the show.
7311              * @param {Roo.Component} this
7312              */
7313         beforeshow : true,
7314         /**
7315          * @event show
7316          * Fires after the component is shown.
7317              * @param {Roo.Component} this
7318              */
7319         show : true,
7320         /**
7321          * @event beforehide
7322          * Fires before the component is hidden. Return false to stop the hide.
7323              * @param {Roo.Component} this
7324              */
7325         beforehide : true,
7326         /**
7327          * @event hide
7328          * Fires after the component is hidden.
7329              * @param {Roo.Component} this
7330              */
7331         hide : true,
7332         /**
7333          * @event beforerender
7334          * Fires before the component is rendered. Return false to stop the render.
7335              * @param {Roo.Component} this
7336              */
7337         beforerender : true,
7338         /**
7339          * @event render
7340          * Fires after the component is rendered.
7341              * @param {Roo.Component} this
7342              */
7343         render : true,
7344         /**
7345          * @event beforedestroy
7346          * Fires before the component is destroyed. Return false to stop the destroy.
7347              * @param {Roo.Component} this
7348              */
7349         beforedestroy : true,
7350         /**
7351          * @event destroy
7352          * Fires after the component is destroyed.
7353              * @param {Roo.Component} this
7354              */
7355         destroy : true
7356     });
7357     if(!this.id){
7358         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7359     }
7360     Roo.ComponentMgr.register(this);
7361     Roo.Component.superclass.constructor.call(this);
7362     this.initComponent();
7363     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7364         this.render(this.renderTo);
7365         delete this.renderTo;
7366     }
7367 };
7368
7369 // private
7370 Roo.Component.AUTO_ID = 1000;
7371
7372 Roo.extend(Roo.Component, Roo.util.Observable, {
7373     /**
7374      * @property {Boolean} hidden
7375      * true if this component is hidden. Read-only.
7376      */
7377     hidden : false,
7378     /**
7379      * true if this component is disabled. Read-only.
7380      */
7381     disabled : false,
7382     /**
7383      * true if this component has been rendered. Read-only.
7384      */
7385     rendered : false,
7386     
7387     /** @cfg {String} disableClass
7388      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7389      */
7390     disabledClass : "x-item-disabled",
7391         /** @cfg {Boolean} allowDomMove
7392          * Whether the component can move the Dom node when rendering (defaults to true).
7393          */
7394     allowDomMove : true,
7395     /** @cfg {String} hideMode
7396      * How this component should hidden. Supported values are
7397      * "visibility" (css visibility), "offsets" (negative offset position) and
7398      * "display" (css display) - defaults to "display".
7399      */
7400     hideMode: 'display',
7401
7402     // private
7403     ctype : "Roo.Component",
7404
7405     /** @cfg {String} actionMode 
7406      * which property holds the element that used for  hide() / show() / disable() / enable()
7407      * default is 'el' 
7408      */
7409     actionMode : "el",
7410
7411     // private
7412     getActionEl : function(){
7413         return this[this.actionMode];
7414     },
7415
7416     initComponent : Roo.emptyFn,
7417     /**
7418      * If this is a lazy rendering component, render it to its container element.
7419      * @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.
7420      */
7421     render : function(container, position){
7422         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7423             if(!container && this.el){
7424                 this.el = Roo.get(this.el);
7425                 container = this.el.dom.parentNode;
7426                 this.allowDomMove = false;
7427             }
7428             this.container = Roo.get(container);
7429             this.rendered = true;
7430             if(position !== undefined){
7431                 if(typeof position == 'number'){
7432                     position = this.container.dom.childNodes[position];
7433                 }else{
7434                     position = Roo.getDom(position);
7435                 }
7436             }
7437             this.onRender(this.container, position || null);
7438             if(this.cls){
7439                 this.el.addClass(this.cls);
7440                 delete this.cls;
7441             }
7442             if(this.style){
7443                 this.el.applyStyles(this.style);
7444                 delete this.style;
7445             }
7446             this.fireEvent("render", this);
7447             this.afterRender(this.container);
7448             if(this.hidden){
7449                 this.hide();
7450             }
7451             if(this.disabled){
7452                 this.disable();
7453             }
7454         }
7455         return this;
7456     },
7457
7458     // private
7459     // default function is not really useful
7460     onRender : function(ct, position){
7461         if(this.el){
7462             this.el = Roo.get(this.el);
7463             if(this.allowDomMove !== false){
7464                 ct.dom.insertBefore(this.el.dom, position);
7465             }
7466         }
7467     },
7468
7469     // private
7470     getAutoCreate : function(){
7471         var cfg = typeof this.autoCreate == "object" ?
7472                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7473         if(this.id && !cfg.id){
7474             cfg.id = this.id;
7475         }
7476         return cfg;
7477     },
7478
7479     // private
7480     afterRender : Roo.emptyFn,
7481
7482     /**
7483      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7484      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7485      */
7486     destroy : function(){
7487         if(this.fireEvent("beforedestroy", this) !== false){
7488             this.purgeListeners();
7489             this.beforeDestroy();
7490             if(this.rendered){
7491                 this.el.removeAllListeners();
7492                 this.el.remove();
7493                 if(this.actionMode == "container"){
7494                     this.container.remove();
7495                 }
7496             }
7497             this.onDestroy();
7498             Roo.ComponentMgr.unregister(this);
7499             this.fireEvent("destroy", this);
7500         }
7501     },
7502
7503         // private
7504     beforeDestroy : function(){
7505
7506     },
7507
7508         // private
7509         onDestroy : function(){
7510
7511     },
7512
7513     /**
7514      * Returns the underlying {@link Roo.Element}.
7515      * @return {Roo.Element} The element
7516      */
7517     getEl : function(){
7518         return this.el;
7519     },
7520
7521     /**
7522      * Returns the id of this component.
7523      * @return {String}
7524      */
7525     getId : function(){
7526         return this.id;
7527     },
7528
7529     /**
7530      * Try to focus this component.
7531      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7532      * @return {Roo.Component} this
7533      */
7534     focus : function(selectText){
7535         if(this.rendered){
7536             this.el.focus();
7537             if(selectText === true){
7538                 this.el.dom.select();
7539             }
7540         }
7541         return this;
7542     },
7543
7544     // private
7545     blur : function(){
7546         if(this.rendered){
7547             this.el.blur();
7548         }
7549         return this;
7550     },
7551
7552     /**
7553      * Disable this component.
7554      * @return {Roo.Component} this
7555      */
7556     disable : function(){
7557         if(this.rendered){
7558             this.onDisable();
7559         }
7560         this.disabled = true;
7561         this.fireEvent("disable", this);
7562         return this;
7563     },
7564
7565         // private
7566     onDisable : function(){
7567         this.getActionEl().addClass(this.disabledClass);
7568         this.el.dom.disabled = true;
7569     },
7570
7571     /**
7572      * Enable this component.
7573      * @return {Roo.Component} this
7574      */
7575     enable : function(){
7576         if(this.rendered){
7577             this.onEnable();
7578         }
7579         this.disabled = false;
7580         this.fireEvent("enable", this);
7581         return this;
7582     },
7583
7584         // private
7585     onEnable : function(){
7586         this.getActionEl().removeClass(this.disabledClass);
7587         this.el.dom.disabled = false;
7588     },
7589
7590     /**
7591      * Convenience function for setting disabled/enabled by boolean.
7592      * @param {Boolean} disabled
7593      */
7594     setDisabled : function(disabled){
7595         this[disabled ? "disable" : "enable"]();
7596     },
7597
7598     /**
7599      * Show this component.
7600      * @return {Roo.Component} this
7601      */
7602     show: function(){
7603         if(this.fireEvent("beforeshow", this) !== false){
7604             this.hidden = false;
7605             if(this.rendered){
7606                 this.onShow();
7607             }
7608             this.fireEvent("show", this);
7609         }
7610         return this;
7611     },
7612
7613     // private
7614     onShow : function(){
7615         var ae = this.getActionEl();
7616         if(this.hideMode == 'visibility'){
7617             ae.dom.style.visibility = "visible";
7618         }else if(this.hideMode == 'offsets'){
7619             ae.removeClass('x-hidden');
7620         }else{
7621             ae.dom.style.display = "";
7622         }
7623     },
7624
7625     /**
7626      * Hide this component.
7627      * @return {Roo.Component} this
7628      */
7629     hide: function(){
7630         if(this.fireEvent("beforehide", this) !== false){
7631             this.hidden = true;
7632             if(this.rendered){
7633                 this.onHide();
7634             }
7635             this.fireEvent("hide", this);
7636         }
7637         return this;
7638     },
7639
7640     // private
7641     onHide : function(){
7642         var ae = this.getActionEl();
7643         if(this.hideMode == 'visibility'){
7644             ae.dom.style.visibility = "hidden";
7645         }else if(this.hideMode == 'offsets'){
7646             ae.addClass('x-hidden');
7647         }else{
7648             ae.dom.style.display = "none";
7649         }
7650     },
7651
7652     /**
7653      * Convenience function to hide or show this component by boolean.
7654      * @param {Boolean} visible True to show, false to hide
7655      * @return {Roo.Component} this
7656      */
7657     setVisible: function(visible){
7658         if(visible) {
7659             this.show();
7660         }else{
7661             this.hide();
7662         }
7663         return this;
7664     },
7665
7666     /**
7667      * Returns true if this component is visible.
7668      */
7669     isVisible : function(){
7670         return this.getActionEl().isVisible();
7671     },
7672
7673     cloneConfig : function(overrides){
7674         overrides = overrides || {};
7675         var id = overrides.id || Roo.id();
7676         var cfg = Roo.applyIf(overrides, this.initialConfig);
7677         cfg.id = id; // prevent dup id
7678         return new this.constructor(cfg);
7679     }
7680 });/*
7681  * Based on:
7682  * Ext JS Library 1.1.1
7683  * Copyright(c) 2006-2007, Ext JS, LLC.
7684  *
7685  * Originally Released Under LGPL - original licence link has changed is not relivant.
7686  *
7687  * Fork - LGPL
7688  * <script type="text/javascript">
7689  */
7690  (function(){ 
7691 /**
7692  * @class Roo.Layer
7693  * @extends Roo.Element
7694  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7695  * automatic maintaining of shadow/shim positions.
7696  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7697  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7698  * you can pass a string with a CSS class name. False turns off the shadow.
7699  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7700  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7701  * @cfg {String} cls CSS class to add to the element
7702  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7703  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7704  * @constructor
7705  * @param {Object} config An object with config options.
7706  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7707  */
7708
7709 Roo.Layer = function(config, existingEl){
7710     config = config || {};
7711     var dh = Roo.DomHelper;
7712     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7713     if(existingEl){
7714         this.dom = Roo.getDom(existingEl);
7715     }
7716     if(!this.dom){
7717         var o = config.dh || {tag: "div", cls: "x-layer"};
7718         this.dom = dh.append(pel, o);
7719     }
7720     if(config.cls){
7721         this.addClass(config.cls);
7722     }
7723     this.constrain = config.constrain !== false;
7724     this.visibilityMode = Roo.Element.VISIBILITY;
7725     if(config.id){
7726         this.id = this.dom.id = config.id;
7727     }else{
7728         this.id = Roo.id(this.dom);
7729     }
7730     this.zindex = config.zindex || this.getZIndex();
7731     this.position("absolute", this.zindex);
7732     if(config.shadow){
7733         this.shadowOffset = config.shadowOffset || 4;
7734         this.shadow = new Roo.Shadow({
7735             offset : this.shadowOffset,
7736             mode : config.shadow
7737         });
7738     }else{
7739         this.shadowOffset = 0;
7740     }
7741     this.useShim = config.shim !== false && Roo.useShims;
7742     this.useDisplay = config.useDisplay;
7743     this.hide();
7744 };
7745
7746 var supr = Roo.Element.prototype;
7747
7748 // shims are shared among layer to keep from having 100 iframes
7749 var shims = [];
7750
7751 Roo.extend(Roo.Layer, Roo.Element, {
7752
7753     getZIndex : function(){
7754         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7755     },
7756
7757     getShim : function(){
7758         if(!this.useShim){
7759             return null;
7760         }
7761         if(this.shim){
7762             return this.shim;
7763         }
7764         var shim = shims.shift();
7765         if(!shim){
7766             shim = this.createShim();
7767             shim.enableDisplayMode('block');
7768             shim.dom.style.display = 'none';
7769             shim.dom.style.visibility = 'visible';
7770         }
7771         var pn = this.dom.parentNode;
7772         if(shim.dom.parentNode != pn){
7773             pn.insertBefore(shim.dom, this.dom);
7774         }
7775         shim.setStyle('z-index', this.getZIndex()-2);
7776         this.shim = shim;
7777         return shim;
7778     },
7779
7780     hideShim : function(){
7781         if(this.shim){
7782             this.shim.setDisplayed(false);
7783             shims.push(this.shim);
7784             delete this.shim;
7785         }
7786     },
7787
7788     disableShadow : function(){
7789         if(this.shadow){
7790             this.shadowDisabled = true;
7791             this.shadow.hide();
7792             this.lastShadowOffset = this.shadowOffset;
7793             this.shadowOffset = 0;
7794         }
7795     },
7796
7797     enableShadow : function(show){
7798         if(this.shadow){
7799             this.shadowDisabled = false;
7800             this.shadowOffset = this.lastShadowOffset;
7801             delete this.lastShadowOffset;
7802             if(show){
7803                 this.sync(true);
7804             }
7805         }
7806     },
7807
7808     // private
7809     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7810     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7811     sync : function(doShow){
7812         var sw = this.shadow;
7813         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7814             var sh = this.getShim();
7815
7816             var w = this.getWidth(),
7817                 h = this.getHeight();
7818
7819             var l = this.getLeft(true),
7820                 t = this.getTop(true);
7821
7822             if(sw && !this.shadowDisabled){
7823                 if(doShow && !sw.isVisible()){
7824                     sw.show(this);
7825                 }else{
7826                     sw.realign(l, t, w, h);
7827                 }
7828                 if(sh){
7829                     if(doShow){
7830                        sh.show();
7831                     }
7832                     // fit the shim behind the shadow, so it is shimmed too
7833                     var a = sw.adjusts, s = sh.dom.style;
7834                     s.left = (Math.min(l, l+a.l))+"px";
7835                     s.top = (Math.min(t, t+a.t))+"px";
7836                     s.width = (w+a.w)+"px";
7837                     s.height = (h+a.h)+"px";
7838                 }
7839             }else if(sh){
7840                 if(doShow){
7841                    sh.show();
7842                 }
7843                 sh.setSize(w, h);
7844                 sh.setLeftTop(l, t);
7845             }
7846             
7847         }
7848     },
7849
7850     // private
7851     destroy : function(){
7852         this.hideShim();
7853         if(this.shadow){
7854             this.shadow.hide();
7855         }
7856         this.removeAllListeners();
7857         var pn = this.dom.parentNode;
7858         if(pn){
7859             pn.removeChild(this.dom);
7860         }
7861         Roo.Element.uncache(this.id);
7862     },
7863
7864     remove : function(){
7865         this.destroy();
7866     },
7867
7868     // private
7869     beginUpdate : function(){
7870         this.updating = true;
7871     },
7872
7873     // private
7874     endUpdate : function(){
7875         this.updating = false;
7876         this.sync(true);
7877     },
7878
7879     // private
7880     hideUnders : function(negOffset){
7881         if(this.shadow){
7882             this.shadow.hide();
7883         }
7884         this.hideShim();
7885     },
7886
7887     // private
7888     constrainXY : function(){
7889         if(this.constrain){
7890             var vw = Roo.lib.Dom.getViewWidth(),
7891                 vh = Roo.lib.Dom.getViewHeight();
7892             var s = Roo.get(document).getScroll();
7893
7894             var xy = this.getXY();
7895             var x = xy[0], y = xy[1];   
7896             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7897             // only move it if it needs it
7898             var moved = false;
7899             // first validate right/bottom
7900             if((x + w) > vw+s.left){
7901                 x = vw - w - this.shadowOffset;
7902                 moved = true;
7903             }
7904             if((y + h) > vh+s.top){
7905                 y = vh - h - this.shadowOffset;
7906                 moved = true;
7907             }
7908             // then make sure top/left isn't negative
7909             if(x < s.left){
7910                 x = s.left;
7911                 moved = true;
7912             }
7913             if(y < s.top){
7914                 y = s.top;
7915                 moved = true;
7916             }
7917             if(moved){
7918                 if(this.avoidY){
7919                     var ay = this.avoidY;
7920                     if(y <= ay && (y+h) >= ay){
7921                         y = ay-h-5;   
7922                     }
7923                 }
7924                 xy = [x, y];
7925                 this.storeXY(xy);
7926                 supr.setXY.call(this, xy);
7927                 this.sync();
7928             }
7929         }
7930     },
7931
7932     isVisible : function(){
7933         return this.visible;    
7934     },
7935
7936     // private
7937     showAction : function(){
7938         this.visible = true; // track visibility to prevent getStyle calls
7939         if(this.useDisplay === true){
7940             this.setDisplayed("");
7941         }else if(this.lastXY){
7942             supr.setXY.call(this, this.lastXY);
7943         }else if(this.lastLT){
7944             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7945         }
7946     },
7947
7948     // private
7949     hideAction : function(){
7950         this.visible = false;
7951         if(this.useDisplay === true){
7952             this.setDisplayed(false);
7953         }else{
7954             this.setLeftTop(-10000,-10000);
7955         }
7956     },
7957
7958     // overridden Element method
7959     setVisible : function(v, a, d, c, e){
7960         if(v){
7961             this.showAction();
7962         }
7963         if(a && v){
7964             var cb = function(){
7965                 this.sync(true);
7966                 if(c){
7967                     c();
7968                 }
7969             }.createDelegate(this);
7970             supr.setVisible.call(this, true, true, d, cb, e);
7971         }else{
7972             if(!v){
7973                 this.hideUnders(true);
7974             }
7975             var cb = c;
7976             if(a){
7977                 cb = function(){
7978                     this.hideAction();
7979                     if(c){
7980                         c();
7981                     }
7982                 }.createDelegate(this);
7983             }
7984             supr.setVisible.call(this, v, a, d, cb, e);
7985             if(v){
7986                 this.sync(true);
7987             }else if(!a){
7988                 this.hideAction();
7989             }
7990         }
7991     },
7992
7993     storeXY : function(xy){
7994         delete this.lastLT;
7995         this.lastXY = xy;
7996     },
7997
7998     storeLeftTop : function(left, top){
7999         delete this.lastXY;
8000         this.lastLT = [left, top];
8001     },
8002
8003     // private
8004     beforeFx : function(){
8005         this.beforeAction();
8006         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8007     },
8008
8009     // private
8010     afterFx : function(){
8011         Roo.Layer.superclass.afterFx.apply(this, arguments);
8012         this.sync(this.isVisible());
8013     },
8014
8015     // private
8016     beforeAction : function(){
8017         if(!this.updating && this.shadow){
8018             this.shadow.hide();
8019         }
8020     },
8021
8022     // overridden Element method
8023     setLeft : function(left){
8024         this.storeLeftTop(left, this.getTop(true));
8025         supr.setLeft.apply(this, arguments);
8026         this.sync();
8027     },
8028
8029     setTop : function(top){
8030         this.storeLeftTop(this.getLeft(true), top);
8031         supr.setTop.apply(this, arguments);
8032         this.sync();
8033     },
8034
8035     setLeftTop : function(left, top){
8036         this.storeLeftTop(left, top);
8037         supr.setLeftTop.apply(this, arguments);
8038         this.sync();
8039     },
8040
8041     setXY : function(xy, a, d, c, e){
8042         this.fixDisplay();
8043         this.beforeAction();
8044         this.storeXY(xy);
8045         var cb = this.createCB(c);
8046         supr.setXY.call(this, xy, a, d, cb, e);
8047         if(!a){
8048             cb();
8049         }
8050     },
8051
8052     // private
8053     createCB : function(c){
8054         var el = this;
8055         return function(){
8056             el.constrainXY();
8057             el.sync(true);
8058             if(c){
8059                 c();
8060             }
8061         };
8062     },
8063
8064     // overridden Element method
8065     setX : function(x, a, d, c, e){
8066         this.setXY([x, this.getY()], a, d, c, e);
8067     },
8068
8069     // overridden Element method
8070     setY : function(y, a, d, c, e){
8071         this.setXY([this.getX(), y], a, d, c, e);
8072     },
8073
8074     // overridden Element method
8075     setSize : function(w, h, a, d, c, e){
8076         this.beforeAction();
8077         var cb = this.createCB(c);
8078         supr.setSize.call(this, w, h, a, d, cb, e);
8079         if(!a){
8080             cb();
8081         }
8082     },
8083
8084     // overridden Element method
8085     setWidth : function(w, a, d, c, e){
8086         this.beforeAction();
8087         var cb = this.createCB(c);
8088         supr.setWidth.call(this, w, a, d, cb, e);
8089         if(!a){
8090             cb();
8091         }
8092     },
8093
8094     // overridden Element method
8095     setHeight : function(h, a, d, c, e){
8096         this.beforeAction();
8097         var cb = this.createCB(c);
8098         supr.setHeight.call(this, h, a, d, cb, e);
8099         if(!a){
8100             cb();
8101         }
8102     },
8103
8104     // overridden Element method
8105     setBounds : function(x, y, w, h, a, d, c, e){
8106         this.beforeAction();
8107         var cb = this.createCB(c);
8108         if(!a){
8109             this.storeXY([x, y]);
8110             supr.setXY.call(this, [x, y]);
8111             supr.setSize.call(this, w, h, a, d, cb, e);
8112             cb();
8113         }else{
8114             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8115         }
8116         return this;
8117     },
8118     
8119     /**
8120      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8121      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8122      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8123      * @param {Number} zindex The new z-index to set
8124      * @return {this} The Layer
8125      */
8126     setZIndex : function(zindex){
8127         this.zindex = zindex;
8128         this.setStyle("z-index", zindex + 2);
8129         if(this.shadow){
8130             this.shadow.setZIndex(zindex + 1);
8131         }
8132         if(this.shim){
8133             this.shim.setStyle("z-index", zindex);
8134         }
8135     }
8136 });
8137 })();/*
8138  * Based on:
8139  * Ext JS Library 1.1.1
8140  * Copyright(c) 2006-2007, Ext JS, LLC.
8141  *
8142  * Originally Released Under LGPL - original licence link has changed is not relivant.
8143  *
8144  * Fork - LGPL
8145  * <script type="text/javascript">
8146  */
8147
8148
8149 /**
8150  * @class Roo.Shadow
8151  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8152  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8153  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8154  * @constructor
8155  * Create a new Shadow
8156  * @param {Object} config The config object
8157  */
8158 Roo.Shadow = function(config){
8159     Roo.apply(this, config);
8160     if(typeof this.mode != "string"){
8161         this.mode = this.defaultMode;
8162     }
8163     var o = this.offset, a = {h: 0};
8164     var rad = Math.floor(this.offset/2);
8165     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8166         case "drop":
8167             a.w = 0;
8168             a.l = a.t = o;
8169             a.t -= 1;
8170             if(Roo.isIE){
8171                 a.l -= this.offset + rad;
8172                 a.t -= this.offset + rad;
8173                 a.w -= rad;
8174                 a.h -= rad;
8175                 a.t += 1;
8176             }
8177         break;
8178         case "sides":
8179             a.w = (o*2);
8180             a.l = -o;
8181             a.t = o-1;
8182             if(Roo.isIE){
8183                 a.l -= (this.offset - rad);
8184                 a.t -= this.offset + rad;
8185                 a.l += 1;
8186                 a.w -= (this.offset - rad)*2;
8187                 a.w -= rad + 1;
8188                 a.h -= 1;
8189             }
8190         break;
8191         case "frame":
8192             a.w = a.h = (o*2);
8193             a.l = a.t = -o;
8194             a.t += 1;
8195             a.h -= 2;
8196             if(Roo.isIE){
8197                 a.l -= (this.offset - rad);
8198                 a.t -= (this.offset - rad);
8199                 a.l += 1;
8200                 a.w -= (this.offset + rad + 1);
8201                 a.h -= (this.offset + rad);
8202                 a.h += 1;
8203             }
8204         break;
8205     };
8206
8207     this.adjusts = a;
8208 };
8209
8210 Roo.Shadow.prototype = {
8211     /**
8212      * @cfg {String} mode
8213      * The shadow display mode.  Supports the following options:<br />
8214      * sides: Shadow displays on both sides and bottom only<br />
8215      * frame: Shadow displays equally on all four sides<br />
8216      * drop: Traditional bottom-right drop shadow (default)
8217      */
8218     /**
8219      * @cfg {String} offset
8220      * The number of pixels to offset the shadow from the element (defaults to 4)
8221      */
8222     offset: 4,
8223
8224     // private
8225     defaultMode: "drop",
8226
8227     /**
8228      * Displays the shadow under the target element
8229      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8230      */
8231     show : function(target){
8232         target = Roo.get(target);
8233         if(!this.el){
8234             this.el = Roo.Shadow.Pool.pull();
8235             if(this.el.dom.nextSibling != target.dom){
8236                 this.el.insertBefore(target);
8237             }
8238         }
8239         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8240         if(Roo.isIE){
8241             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8242         }
8243         this.realign(
8244             target.getLeft(true),
8245             target.getTop(true),
8246             target.getWidth(),
8247             target.getHeight()
8248         );
8249         this.el.dom.style.display = "block";
8250     },
8251
8252     /**
8253      * Returns true if the shadow is visible, else false
8254      */
8255     isVisible : function(){
8256         return this.el ? true : false;  
8257     },
8258
8259     /**
8260      * Direct alignment when values are already available. Show must be called at least once before
8261      * calling this method to ensure it is initialized.
8262      * @param {Number} left The target element left position
8263      * @param {Number} top The target element top position
8264      * @param {Number} width The target element width
8265      * @param {Number} height The target element height
8266      */
8267     realign : function(l, t, w, h){
8268         if(!this.el){
8269             return;
8270         }
8271         var a = this.adjusts, d = this.el.dom, s = d.style;
8272         var iea = 0;
8273         s.left = (l+a.l)+"px";
8274         s.top = (t+a.t)+"px";
8275         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8276  
8277         if(s.width != sws || s.height != shs){
8278             s.width = sws;
8279             s.height = shs;
8280             if(!Roo.isIE){
8281                 var cn = d.childNodes;
8282                 var sww = Math.max(0, (sw-12))+"px";
8283                 cn[0].childNodes[1].style.width = sww;
8284                 cn[1].childNodes[1].style.width = sww;
8285                 cn[2].childNodes[1].style.width = sww;
8286                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8287             }
8288         }
8289     },
8290
8291     /**
8292      * Hides this shadow
8293      */
8294     hide : function(){
8295         if(this.el){
8296             this.el.dom.style.display = "none";
8297             Roo.Shadow.Pool.push(this.el);
8298             delete this.el;
8299         }
8300     },
8301
8302     /**
8303      * Adjust the z-index of this shadow
8304      * @param {Number} zindex The new z-index
8305      */
8306     setZIndex : function(z){
8307         this.zIndex = z;
8308         if(this.el){
8309             this.el.setStyle("z-index", z);
8310         }
8311     }
8312 };
8313
8314 // Private utility class that manages the internal Shadow cache
8315 Roo.Shadow.Pool = function(){
8316     var p = [];
8317     var markup = Roo.isIE ?
8318                  '<div class="x-ie-shadow"></div>' :
8319                  '<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>';
8320     return {
8321         pull : function(){
8322             var sh = p.shift();
8323             if(!sh){
8324                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8325                 sh.autoBoxAdjust = false;
8326             }
8327             return sh;
8328         },
8329
8330         push : function(sh){
8331             p.push(sh);
8332         }
8333     };
8334 }();/*
8335  * Based on:
8336  * Ext JS Library 1.1.1
8337  * Copyright(c) 2006-2007, Ext JS, LLC.
8338  *
8339  * Originally Released Under LGPL - original licence link has changed is not relivant.
8340  *
8341  * Fork - LGPL
8342  * <script type="text/javascript">
8343  */
8344
8345 /**
8346  * @class Roo.BoxComponent
8347  * @extends Roo.Component
8348  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8349  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8350  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8351  * layout containers.
8352  * @constructor
8353  * @param {Roo.Element/String/Object} config The configuration options.
8354  */
8355 Roo.BoxComponent = function(config){
8356     Roo.Component.call(this, config);
8357     this.addEvents({
8358         /**
8359          * @event resize
8360          * Fires after the component is resized.
8361              * @param {Roo.Component} this
8362              * @param {Number} adjWidth The box-adjusted width that was set
8363              * @param {Number} adjHeight The box-adjusted height that was set
8364              * @param {Number} rawWidth The width that was originally specified
8365              * @param {Number} rawHeight The height that was originally specified
8366              */
8367         resize : true,
8368         /**
8369          * @event move
8370          * Fires after the component is moved.
8371              * @param {Roo.Component} this
8372              * @param {Number} x The new x position
8373              * @param {Number} y The new y position
8374              */
8375         move : true
8376     });
8377 };
8378
8379 Roo.extend(Roo.BoxComponent, Roo.Component, {
8380     // private, set in afterRender to signify that the component has been rendered
8381     boxReady : false,
8382     // private, used to defer height settings to subclasses
8383     deferHeight: false,
8384     /** @cfg {Number} width
8385      * width (optional) size of component
8386      */
8387      /** @cfg {Number} height
8388      * height (optional) size of component
8389      */
8390      
8391     /**
8392      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8393      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8394      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8395      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8396      * @return {Roo.BoxComponent} this
8397      */
8398     setSize : function(w, h){
8399         // support for standard size objects
8400         if(typeof w == 'object'){
8401             h = w.height;
8402             w = w.width;
8403         }
8404         // not rendered
8405         if(!this.boxReady){
8406             this.width = w;
8407             this.height = h;
8408             return this;
8409         }
8410
8411         // prevent recalcs when not needed
8412         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8413             return this;
8414         }
8415         this.lastSize = {width: w, height: h};
8416
8417         var adj = this.adjustSize(w, h);
8418         var aw = adj.width, ah = adj.height;
8419         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8420             var rz = this.getResizeEl();
8421             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8422                 rz.setSize(aw, ah);
8423             }else if(!this.deferHeight && ah !== undefined){
8424                 rz.setHeight(ah);
8425             }else if(aw !== undefined){
8426                 rz.setWidth(aw);
8427             }
8428             this.onResize(aw, ah, w, h);
8429             this.fireEvent('resize', this, aw, ah, w, h);
8430         }
8431         return this;
8432     },
8433
8434     /**
8435      * Gets the current size of the component's underlying element.
8436      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8437      */
8438     getSize : function(){
8439         return this.el.getSize();
8440     },
8441
8442     /**
8443      * Gets the current XY position of the component's underlying element.
8444      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8445      * @return {Array} The XY position of the element (e.g., [100, 200])
8446      */
8447     getPosition : function(local){
8448         if(local === true){
8449             return [this.el.getLeft(true), this.el.getTop(true)];
8450         }
8451         return this.xy || this.el.getXY();
8452     },
8453
8454     /**
8455      * Gets the current box measurements of the component's underlying element.
8456      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8457      * @returns {Object} box An object in the format {x, y, width, height}
8458      */
8459     getBox : function(local){
8460         var s = this.el.getSize();
8461         if(local){
8462             s.x = this.el.getLeft(true);
8463             s.y = this.el.getTop(true);
8464         }else{
8465             var xy = this.xy || this.el.getXY();
8466             s.x = xy[0];
8467             s.y = xy[1];
8468         }
8469         return s;
8470     },
8471
8472     /**
8473      * Sets the current box measurements of the component's underlying element.
8474      * @param {Object} box An object in the format {x, y, width, height}
8475      * @returns {Roo.BoxComponent} this
8476      */
8477     updateBox : function(box){
8478         this.setSize(box.width, box.height);
8479         this.setPagePosition(box.x, box.y);
8480         return this;
8481     },
8482
8483     // protected
8484     getResizeEl : function(){
8485         return this.resizeEl || this.el;
8486     },
8487
8488     // protected
8489     getPositionEl : function(){
8490         return this.positionEl || this.el;
8491     },
8492
8493     /**
8494      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8495      * This method fires the move event.
8496      * @param {Number} left The new left
8497      * @param {Number} top The new top
8498      * @returns {Roo.BoxComponent} this
8499      */
8500     setPosition : function(x, y){
8501         this.x = x;
8502         this.y = y;
8503         if(!this.boxReady){
8504             return this;
8505         }
8506         var adj = this.adjustPosition(x, y);
8507         var ax = adj.x, ay = adj.y;
8508
8509         var el = this.getPositionEl();
8510         if(ax !== undefined || ay !== undefined){
8511             if(ax !== undefined && ay !== undefined){
8512                 el.setLeftTop(ax, ay);
8513             }else if(ax !== undefined){
8514                 el.setLeft(ax);
8515             }else if(ay !== undefined){
8516                 el.setTop(ay);
8517             }
8518             this.onPosition(ax, ay);
8519             this.fireEvent('move', this, ax, ay);
8520         }
8521         return this;
8522     },
8523
8524     /**
8525      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8526      * This method fires the move event.
8527      * @param {Number} x The new x position
8528      * @param {Number} y The new y position
8529      * @returns {Roo.BoxComponent} this
8530      */
8531     setPagePosition : function(x, y){
8532         this.pageX = x;
8533         this.pageY = y;
8534         if(!this.boxReady){
8535             return;
8536         }
8537         if(x === undefined || y === undefined){ // cannot translate undefined points
8538             return;
8539         }
8540         var p = this.el.translatePoints(x, y);
8541         this.setPosition(p.left, p.top);
8542         return this;
8543     },
8544
8545     // private
8546     onRender : function(ct, position){
8547         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8548         if(this.resizeEl){
8549             this.resizeEl = Roo.get(this.resizeEl);
8550         }
8551         if(this.positionEl){
8552             this.positionEl = Roo.get(this.positionEl);
8553         }
8554     },
8555
8556     // private
8557     afterRender : function(){
8558         Roo.BoxComponent.superclass.afterRender.call(this);
8559         this.boxReady = true;
8560         this.setSize(this.width, this.height);
8561         if(this.x || this.y){
8562             this.setPosition(this.x, this.y);
8563         }
8564         if(this.pageX || this.pageY){
8565             this.setPagePosition(this.pageX, this.pageY);
8566         }
8567     },
8568
8569     /**
8570      * Force the component's size to recalculate based on the underlying element's current height and width.
8571      * @returns {Roo.BoxComponent} this
8572      */
8573     syncSize : function(){
8574         delete this.lastSize;
8575         this.setSize(this.el.getWidth(), this.el.getHeight());
8576         return this;
8577     },
8578
8579     /**
8580      * Called after the component is resized, this method is empty by default but can be implemented by any
8581      * subclass that needs to perform custom logic after a resize occurs.
8582      * @param {Number} adjWidth The box-adjusted width that was set
8583      * @param {Number} adjHeight The box-adjusted height that was set
8584      * @param {Number} rawWidth The width that was originally specified
8585      * @param {Number} rawHeight The height that was originally specified
8586      */
8587     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8588
8589     },
8590
8591     /**
8592      * Called after the component is moved, this method is empty by default but can be implemented by any
8593      * subclass that needs to perform custom logic after a move occurs.
8594      * @param {Number} x The new x position
8595      * @param {Number} y The new y position
8596      */
8597     onPosition : function(x, y){
8598
8599     },
8600
8601     // private
8602     adjustSize : function(w, h){
8603         if(this.autoWidth){
8604             w = 'auto';
8605         }
8606         if(this.autoHeight){
8607             h = 'auto';
8608         }
8609         return {width : w, height: h};
8610     },
8611
8612     // private
8613     adjustPosition : function(x, y){
8614         return {x : x, y: y};
8615     }
8616 });/*
8617  * Based on:
8618  * Ext JS Library 1.1.1
8619  * Copyright(c) 2006-2007, Ext JS, LLC.
8620  *
8621  * Originally Released Under LGPL - original licence link has changed is not relivant.
8622  *
8623  * Fork - LGPL
8624  * <script type="text/javascript">
8625  */
8626
8627
8628 /**
8629  * @class Roo.SplitBar
8630  * @extends Roo.util.Observable
8631  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8632  * <br><br>
8633  * Usage:
8634  * <pre><code>
8635 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8636                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8637 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8638 split.minSize = 100;
8639 split.maxSize = 600;
8640 split.animate = true;
8641 split.on('moved', splitterMoved);
8642 </code></pre>
8643  * @constructor
8644  * Create a new SplitBar
8645  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8646  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8647  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8648  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8649                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8650                         position of the SplitBar).
8651  */
8652 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8653     
8654     /** @private */
8655     this.el = Roo.get(dragElement, true);
8656     this.el.dom.unselectable = "on";
8657     /** @private */
8658     this.resizingEl = Roo.get(resizingElement, true);
8659
8660     /**
8661      * @private
8662      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8663      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8664      * @type Number
8665      */
8666     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8667     
8668     /**
8669      * The minimum size of the resizing element. (Defaults to 0)
8670      * @type Number
8671      */
8672     this.minSize = 0;
8673     
8674     /**
8675      * The maximum size of the resizing element. (Defaults to 2000)
8676      * @type Number
8677      */
8678     this.maxSize = 2000;
8679     
8680     /**
8681      * Whether to animate the transition to the new size
8682      * @type Boolean
8683      */
8684     this.animate = false;
8685     
8686     /**
8687      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8688      * @type Boolean
8689      */
8690     this.useShim = false;
8691     
8692     /** @private */
8693     this.shim = null;
8694     
8695     if(!existingProxy){
8696         /** @private */
8697         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8698     }else{
8699         this.proxy = Roo.get(existingProxy).dom;
8700     }
8701     /** @private */
8702     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8703     
8704     /** @private */
8705     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8706     
8707     /** @private */
8708     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8709     
8710     /** @private */
8711     this.dragSpecs = {};
8712     
8713     /**
8714      * @private The adapter to use to positon and resize elements
8715      */
8716     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8717     this.adapter.init(this);
8718     
8719     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8720         /** @private */
8721         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8722         this.el.addClass("x-splitbar-h");
8723     }else{
8724         /** @private */
8725         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8726         this.el.addClass("x-splitbar-v");
8727     }
8728     
8729     this.addEvents({
8730         /**
8731          * @event resize
8732          * Fires when the splitter is moved (alias for {@link #event-moved})
8733          * @param {Roo.SplitBar} this
8734          * @param {Number} newSize the new width or height
8735          */
8736         "resize" : true,
8737         /**
8738          * @event moved
8739          * Fires when the splitter is moved
8740          * @param {Roo.SplitBar} this
8741          * @param {Number} newSize the new width or height
8742          */
8743         "moved" : true,
8744         /**
8745          * @event beforeresize
8746          * Fires before the splitter is dragged
8747          * @param {Roo.SplitBar} this
8748          */
8749         "beforeresize" : true,
8750
8751         "beforeapply" : true
8752     });
8753
8754     Roo.util.Observable.call(this);
8755 };
8756
8757 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8758     onStartProxyDrag : function(x, y){
8759         this.fireEvent("beforeresize", this);
8760         if(!this.overlay){
8761             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8762             o.unselectable();
8763             o.enableDisplayMode("block");
8764             // all splitbars share the same overlay
8765             Roo.SplitBar.prototype.overlay = o;
8766         }
8767         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8768         this.overlay.show();
8769         Roo.get(this.proxy).setDisplayed("block");
8770         var size = this.adapter.getElementSize(this);
8771         this.activeMinSize = this.getMinimumSize();;
8772         this.activeMaxSize = this.getMaximumSize();;
8773         var c1 = size - this.activeMinSize;
8774         var c2 = Math.max(this.activeMaxSize - size, 0);
8775         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8776             this.dd.resetConstraints();
8777             this.dd.setXConstraint(
8778                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8779                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8780             );
8781             this.dd.setYConstraint(0, 0);
8782         }else{
8783             this.dd.resetConstraints();
8784             this.dd.setXConstraint(0, 0);
8785             this.dd.setYConstraint(
8786                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8787                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8788             );
8789          }
8790         this.dragSpecs.startSize = size;
8791         this.dragSpecs.startPoint = [x, y];
8792         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8793     },
8794     
8795     /** 
8796      * @private Called after the drag operation by the DDProxy
8797      */
8798     onEndProxyDrag : function(e){
8799         Roo.get(this.proxy).setDisplayed(false);
8800         var endPoint = Roo.lib.Event.getXY(e);
8801         if(this.overlay){
8802             this.overlay.hide();
8803         }
8804         var newSize;
8805         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8806             newSize = this.dragSpecs.startSize + 
8807                 (this.placement == Roo.SplitBar.LEFT ?
8808                     endPoint[0] - this.dragSpecs.startPoint[0] :
8809                     this.dragSpecs.startPoint[0] - endPoint[0]
8810                 );
8811         }else{
8812             newSize = this.dragSpecs.startSize + 
8813                 (this.placement == Roo.SplitBar.TOP ?
8814                     endPoint[1] - this.dragSpecs.startPoint[1] :
8815                     this.dragSpecs.startPoint[1] - endPoint[1]
8816                 );
8817         }
8818         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8819         if(newSize != this.dragSpecs.startSize){
8820             if(this.fireEvent('beforeapply', this, newSize) !== false){
8821                 this.adapter.setElementSize(this, newSize);
8822                 this.fireEvent("moved", this, newSize);
8823                 this.fireEvent("resize", this, newSize);
8824             }
8825         }
8826     },
8827     
8828     /**
8829      * Get the adapter this SplitBar uses
8830      * @return The adapter object
8831      */
8832     getAdapter : function(){
8833         return this.adapter;
8834     },
8835     
8836     /**
8837      * Set the adapter this SplitBar uses
8838      * @param {Object} adapter A SplitBar adapter object
8839      */
8840     setAdapter : function(adapter){
8841         this.adapter = adapter;
8842         this.adapter.init(this);
8843     },
8844     
8845     /**
8846      * Gets the minimum size for the resizing element
8847      * @return {Number} The minimum size
8848      */
8849     getMinimumSize : function(){
8850         return this.minSize;
8851     },
8852     
8853     /**
8854      * Sets the minimum size for the resizing element
8855      * @param {Number} minSize The minimum size
8856      */
8857     setMinimumSize : function(minSize){
8858         this.minSize = minSize;
8859     },
8860     
8861     /**
8862      * Gets the maximum size for the resizing element
8863      * @return {Number} The maximum size
8864      */
8865     getMaximumSize : function(){
8866         return this.maxSize;
8867     },
8868     
8869     /**
8870      * Sets the maximum size for the resizing element
8871      * @param {Number} maxSize The maximum size
8872      */
8873     setMaximumSize : function(maxSize){
8874         this.maxSize = maxSize;
8875     },
8876     
8877     /**
8878      * Sets the initialize size for the resizing element
8879      * @param {Number} size The initial size
8880      */
8881     setCurrentSize : function(size){
8882         var oldAnimate = this.animate;
8883         this.animate = false;
8884         this.adapter.setElementSize(this, size);
8885         this.animate = oldAnimate;
8886     },
8887     
8888     /**
8889      * Destroy this splitbar. 
8890      * @param {Boolean} removeEl True to remove the element
8891      */
8892     destroy : function(removeEl){
8893         if(this.shim){
8894             this.shim.remove();
8895         }
8896         this.dd.unreg();
8897         this.proxy.parentNode.removeChild(this.proxy);
8898         if(removeEl){
8899             this.el.remove();
8900         }
8901     }
8902 });
8903
8904 /**
8905  * @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.
8906  */
8907 Roo.SplitBar.createProxy = function(dir){
8908     var proxy = new Roo.Element(document.createElement("div"));
8909     proxy.unselectable();
8910     var cls = 'x-splitbar-proxy';
8911     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8912     document.body.appendChild(proxy.dom);
8913     return proxy.dom;
8914 };
8915
8916 /** 
8917  * @class Roo.SplitBar.BasicLayoutAdapter
8918  * Default Adapter. It assumes the splitter and resizing element are not positioned
8919  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8920  */
8921 Roo.SplitBar.BasicLayoutAdapter = function(){
8922 };
8923
8924 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8925     // do nothing for now
8926     init : function(s){
8927     
8928     },
8929     /**
8930      * Called before drag operations to get the current size of the resizing element. 
8931      * @param {Roo.SplitBar} s The SplitBar using this adapter
8932      */
8933      getElementSize : function(s){
8934         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8935             return s.resizingEl.getWidth();
8936         }else{
8937             return s.resizingEl.getHeight();
8938         }
8939     },
8940     
8941     /**
8942      * Called after drag operations to set the size of the resizing element.
8943      * @param {Roo.SplitBar} s The SplitBar using this adapter
8944      * @param {Number} newSize The new size to set
8945      * @param {Function} onComplete A function to be invoked when resizing is complete
8946      */
8947     setElementSize : function(s, newSize, onComplete){
8948         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8949             if(!s.animate){
8950                 s.resizingEl.setWidth(newSize);
8951                 if(onComplete){
8952                     onComplete(s, newSize);
8953                 }
8954             }else{
8955                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8956             }
8957         }else{
8958             
8959             if(!s.animate){
8960                 s.resizingEl.setHeight(newSize);
8961                 if(onComplete){
8962                     onComplete(s, newSize);
8963                 }
8964             }else{
8965                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8966             }
8967         }
8968     }
8969 };
8970
8971 /** 
8972  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8973  * @extends Roo.SplitBar.BasicLayoutAdapter
8974  * Adapter that  moves the splitter element to align with the resized sizing element. 
8975  * Used with an absolute positioned SplitBar.
8976  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8977  * document.body, make sure you assign an id to the body element.
8978  */
8979 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8980     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8981     this.container = Roo.get(container);
8982 };
8983
8984 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8985     init : function(s){
8986         this.basic.init(s);
8987     },
8988     
8989     getElementSize : function(s){
8990         return this.basic.getElementSize(s);
8991     },
8992     
8993     setElementSize : function(s, newSize, onComplete){
8994         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
8995     },
8996     
8997     moveSplitter : function(s){
8998         var yes = Roo.SplitBar;
8999         switch(s.placement){
9000             case yes.LEFT:
9001                 s.el.setX(s.resizingEl.getRight());
9002                 break;
9003             case yes.RIGHT:
9004                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9005                 break;
9006             case yes.TOP:
9007                 s.el.setY(s.resizingEl.getBottom());
9008                 break;
9009             case yes.BOTTOM:
9010                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9011                 break;
9012         }
9013     }
9014 };
9015
9016 /**
9017  * Orientation constant - Create a vertical SplitBar
9018  * @static
9019  * @type Number
9020  */
9021 Roo.SplitBar.VERTICAL = 1;
9022
9023 /**
9024  * Orientation constant - Create a horizontal SplitBar
9025  * @static
9026  * @type Number
9027  */
9028 Roo.SplitBar.HORIZONTAL = 2;
9029
9030 /**
9031  * Placement constant - The resizing element is to the left of the splitter element
9032  * @static
9033  * @type Number
9034  */
9035 Roo.SplitBar.LEFT = 1;
9036
9037 /**
9038  * Placement constant - The resizing element is to the right of the splitter element
9039  * @static
9040  * @type Number
9041  */
9042 Roo.SplitBar.RIGHT = 2;
9043
9044 /**
9045  * Placement constant - The resizing element is positioned above the splitter element
9046  * @static
9047  * @type Number
9048  */
9049 Roo.SplitBar.TOP = 3;
9050
9051 /**
9052  * Placement constant - The resizing element is positioned under splitter element
9053  * @static
9054  * @type Number
9055  */
9056 Roo.SplitBar.BOTTOM = 4;
9057 /*
9058  * Based on:
9059  * Ext JS Library 1.1.1
9060  * Copyright(c) 2006-2007, Ext JS, LLC.
9061  *
9062  * Originally Released Under LGPL - original licence link has changed is not relivant.
9063  *
9064  * Fork - LGPL
9065  * <script type="text/javascript">
9066  */
9067
9068 /**
9069  * @class Roo.View
9070  * @extends Roo.util.Observable
9071  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9072  * This class also supports single and multi selection modes. <br>
9073  * Create a data model bound view:
9074  <pre><code>
9075  var store = new Roo.data.Store(...);
9076
9077  var view = new Roo.View({
9078     el : "my-element",
9079     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9080  
9081     singleSelect: true,
9082     selectedClass: "ydataview-selected",
9083     store: store
9084  });
9085
9086  // listen for node click?
9087  view.on("click", function(vw, index, node, e){
9088  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9089  });
9090
9091  // load XML data
9092  dataModel.load("foobar.xml");
9093  </code></pre>
9094  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9095  * <br><br>
9096  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9097  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9098  * 
9099  * Note: old style constructor is still suported (container, template, config)
9100  * 
9101  * @constructor
9102  * Create a new View
9103  * @param {Object} config The config object
9104  * 
9105  */
9106 Roo.View = function(config, depreciated_tpl, depreciated_config){
9107     
9108     if (typeof(depreciated_tpl) == 'undefined') {
9109         // new way.. - universal constructor.
9110         Roo.apply(this, config);
9111         this.el  = Roo.get(this.el);
9112     } else {
9113         // old format..
9114         this.el  = Roo.get(config);
9115         this.tpl = depreciated_tpl;
9116         Roo.apply(this, depreciated_config);
9117     }
9118      
9119     
9120     if(typeof(this.tpl) == "string"){
9121         this.tpl = new Roo.Template(this.tpl);
9122     } else {
9123         // support xtype ctors..
9124         this.tpl = new Roo.factory(this.tpl, Roo);
9125     }
9126     
9127     
9128     this.tpl.compile();
9129    
9130
9131      
9132     /** @private */
9133     this.addEvents({
9134     /**
9135      * @event beforeclick
9136      * Fires before a click is processed. Returns false to cancel the default action.
9137      * @param {Roo.View} this
9138      * @param {Number} index The index of the target node
9139      * @param {HTMLElement} node The target node
9140      * @param {Roo.EventObject} e The raw event object
9141      */
9142         "beforeclick" : true,
9143     /**
9144      * @event click
9145      * Fires when a template node is clicked.
9146      * @param {Roo.View} this
9147      * @param {Number} index The index of the target node
9148      * @param {HTMLElement} node The target node
9149      * @param {Roo.EventObject} e The raw event object
9150      */
9151         "click" : true,
9152     /**
9153      * @event dblclick
9154      * Fires when a template node is double clicked.
9155      * @param {Roo.View} this
9156      * @param {Number} index The index of the target node
9157      * @param {HTMLElement} node The target node
9158      * @param {Roo.EventObject} e The raw event object
9159      */
9160         "dblclick" : true,
9161     /**
9162      * @event contextmenu
9163      * Fires when a template node is right clicked.
9164      * @param {Roo.View} this
9165      * @param {Number} index The index of the target node
9166      * @param {HTMLElement} node The target node
9167      * @param {Roo.EventObject} e The raw event object
9168      */
9169         "contextmenu" : true,
9170     /**
9171      * @event selectionchange
9172      * Fires when the selected nodes change.
9173      * @param {Roo.View} this
9174      * @param {Array} selections Array of the selected nodes
9175      */
9176         "selectionchange" : true,
9177
9178     /**
9179      * @event beforeselect
9180      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9181      * @param {Roo.View} this
9182      * @param {HTMLElement} node The node to be selected
9183      * @param {Array} selections Array of currently selected nodes
9184      */
9185         "beforeselect" : true
9186     });
9187
9188     this.el.on({
9189         "click": this.onClick,
9190         "dblclick": this.onDblClick,
9191         "contextmenu": this.onContextMenu,
9192         scope:this
9193     });
9194
9195     this.selections = [];
9196     this.nodes = [];
9197     this.cmp = new Roo.CompositeElementLite([]);
9198     if(this.store){
9199         this.store = Roo.factory(this.store, Roo.data);
9200         this.setStore(this.store, true);
9201     }
9202     Roo.View.superclass.constructor.call(this);
9203 };
9204
9205 Roo.extend(Roo.View, Roo.util.Observable, {
9206     
9207      /**
9208      * @cfg {Roo.data.Store} store Data store to load data from.
9209      */
9210     store : false,
9211     
9212     /**
9213      * @cfg {String|Roo.Element} el The container element.
9214      */
9215     el : '',
9216     
9217     /**
9218      * @cfg {String|Roo.Template} tpl The template used by this View 
9219      */
9220     tpl : false,
9221     
9222     /**
9223      * @cfg {String} selectedClass The css class to add to selected nodes
9224      */
9225     selectedClass : "x-view-selected",
9226      /**
9227      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9228      */
9229     emptyText : "",
9230     /**
9231      * @cfg {Boolean} multiSelect Allow multiple selection
9232      */
9233     
9234     multiSelect : false,
9235     /**
9236      * @cfg {Boolean} singleSelect Allow single selection
9237      */
9238     singleSelect:  false,
9239     
9240     /**
9241      * Returns the element this view is bound to.
9242      * @return {Roo.Element}
9243      */
9244     getEl : function(){
9245         return this.el;
9246     },
9247
9248     /**
9249      * Refreshes the view.
9250      */
9251     refresh : function(){
9252         var t = this.tpl;
9253         this.clearSelections();
9254         this.el.update("");
9255         var html = [];
9256         var records = this.store.getRange();
9257         if(records.length < 1){
9258             this.el.update(this.emptyText);
9259             return;
9260         }
9261         for(var i = 0, len = records.length; i < len; i++){
9262             var data = this.prepareData(records[i].data, i, records[i]);
9263             html[html.length] = t.apply(data);
9264         }
9265         this.el.update(html.join(""));
9266         this.nodes = this.el.dom.childNodes;
9267         this.updateIndexes(0);
9268     },
9269
9270     /**
9271      * Function to override to reformat the data that is sent to
9272      * the template for each node.
9273      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9274      * a JSON object for an UpdateManager bound view).
9275      */
9276     prepareData : function(data){
9277         return data;
9278     },
9279
9280     onUpdate : function(ds, record){
9281         this.clearSelections();
9282         var index = this.store.indexOf(record);
9283         var n = this.nodes[index];
9284         this.tpl.insertBefore(n, this.prepareData(record.data));
9285         n.parentNode.removeChild(n);
9286         this.updateIndexes(index, index);
9287     },
9288
9289     onAdd : function(ds, records, index){
9290         this.clearSelections();
9291         if(this.nodes.length == 0){
9292             this.refresh();
9293             return;
9294         }
9295         var n = this.nodes[index];
9296         for(var i = 0, len = records.length; i < len; i++){
9297             var d = this.prepareData(records[i].data);
9298             if(n){
9299                 this.tpl.insertBefore(n, d);
9300             }else{
9301                 this.tpl.append(this.el, d);
9302             }
9303         }
9304         this.updateIndexes(index);
9305     },
9306
9307     onRemove : function(ds, record, index){
9308         this.clearSelections();
9309         this.el.dom.removeChild(this.nodes[index]);
9310         this.updateIndexes(index);
9311     },
9312
9313     /**
9314      * Refresh an individual node.
9315      * @param {Number} index
9316      */
9317     refreshNode : function(index){
9318         this.onUpdate(this.store, this.store.getAt(index));
9319     },
9320
9321     updateIndexes : function(startIndex, endIndex){
9322         var ns = this.nodes;
9323         startIndex = startIndex || 0;
9324         endIndex = endIndex || ns.length - 1;
9325         for(var i = startIndex; i <= endIndex; i++){
9326             ns[i].nodeIndex = i;
9327         }
9328     },
9329
9330     /**
9331      * Changes the data store this view uses and refresh the view.
9332      * @param {Store} store
9333      */
9334     setStore : function(store, initial){
9335         if(!initial && this.store){
9336             this.store.un("datachanged", this.refresh);
9337             this.store.un("add", this.onAdd);
9338             this.store.un("remove", this.onRemove);
9339             this.store.un("update", this.onUpdate);
9340             this.store.un("clear", this.refresh);
9341         }
9342         if(store){
9343           
9344             store.on("datachanged", this.refresh, this);
9345             store.on("add", this.onAdd, this);
9346             store.on("remove", this.onRemove, this);
9347             store.on("update", this.onUpdate, this);
9348             store.on("clear", this.refresh, this);
9349         }
9350         
9351         if(store){
9352             this.refresh();
9353         }
9354     },
9355
9356     /**
9357      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9358      * @param {HTMLElement} node
9359      * @return {HTMLElement} The template node
9360      */
9361     findItemFromChild : function(node){
9362         var el = this.el.dom;
9363         if(!node || node.parentNode == el){
9364                     return node;
9365             }
9366             var p = node.parentNode;
9367             while(p && p != el){
9368             if(p.parentNode == el){
9369                 return p;
9370             }
9371             p = p.parentNode;
9372         }
9373             return null;
9374     },
9375
9376     /** @ignore */
9377     onClick : function(e){
9378         var item = this.findItemFromChild(e.getTarget());
9379         if(item){
9380             var index = this.indexOf(item);
9381             if(this.onItemClick(item, index, e) !== false){
9382                 this.fireEvent("click", this, index, item, e);
9383             }
9384         }else{
9385             this.clearSelections();
9386         }
9387     },
9388
9389     /** @ignore */
9390     onContextMenu : function(e){
9391         var item = this.findItemFromChild(e.getTarget());
9392         if(item){
9393             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9394         }
9395     },
9396
9397     /** @ignore */
9398     onDblClick : function(e){
9399         var item = this.findItemFromChild(e.getTarget());
9400         if(item){
9401             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9402         }
9403     },
9404
9405     onItemClick : function(item, index, e){
9406         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9407             return false;
9408         }
9409         if(this.multiSelect || this.singleSelect){
9410             if(this.multiSelect && e.shiftKey && this.lastSelection){
9411                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9412             }else{
9413                 this.select(item, this.multiSelect && e.ctrlKey);
9414                 this.lastSelection = item;
9415             }
9416             e.preventDefault();
9417         }
9418         return true;
9419     },
9420
9421     /**
9422      * Get the number of selected nodes.
9423      * @return {Number}
9424      */
9425     getSelectionCount : function(){
9426         return this.selections.length;
9427     },
9428
9429     /**
9430      * Get the currently selected nodes.
9431      * @return {Array} An array of HTMLElements
9432      */
9433     getSelectedNodes : function(){
9434         return this.selections;
9435     },
9436
9437     /**
9438      * Get the indexes of the selected nodes.
9439      * @return {Array}
9440      */
9441     getSelectedIndexes : function(){
9442         var indexes = [], s = this.selections;
9443         for(var i = 0, len = s.length; i < len; i++){
9444             indexes.push(s[i].nodeIndex);
9445         }
9446         return indexes;
9447     },
9448
9449     /**
9450      * Clear all selections
9451      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9452      */
9453     clearSelections : function(suppressEvent){
9454         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9455             this.cmp.elements = this.selections;
9456             this.cmp.removeClass(this.selectedClass);
9457             this.selections = [];
9458             if(!suppressEvent){
9459                 this.fireEvent("selectionchange", this, this.selections);
9460             }
9461         }
9462     },
9463
9464     /**
9465      * Returns true if the passed node is selected
9466      * @param {HTMLElement/Number} node The node or node index
9467      * @return {Boolean}
9468      */
9469     isSelected : function(node){
9470         var s = this.selections;
9471         if(s.length < 1){
9472             return false;
9473         }
9474         node = this.getNode(node);
9475         return s.indexOf(node) !== -1;
9476     },
9477
9478     /**
9479      * Selects nodes.
9480      * @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
9481      * @param {Boolean} keepExisting (optional) true to keep existing selections
9482      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9483      */
9484     select : function(nodeInfo, keepExisting, suppressEvent){
9485         if(nodeInfo instanceof Array){
9486             if(!keepExisting){
9487                 this.clearSelections(true);
9488             }
9489             for(var i = 0, len = nodeInfo.length; i < len; i++){
9490                 this.select(nodeInfo[i], true, true);
9491             }
9492         } else{
9493             var node = this.getNode(nodeInfo);
9494             if(node && !this.isSelected(node)){
9495                 if(!keepExisting){
9496                     this.clearSelections(true);
9497                 }
9498                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9499                     Roo.fly(node).addClass(this.selectedClass);
9500                     this.selections.push(node);
9501                     if(!suppressEvent){
9502                         this.fireEvent("selectionchange", this, this.selections);
9503                     }
9504                 }
9505             }
9506         }
9507     },
9508
9509     /**
9510      * Gets a template node.
9511      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9512      * @return {HTMLElement} The node or null if it wasn't found
9513      */
9514     getNode : function(nodeInfo){
9515         if(typeof nodeInfo == "string"){
9516             return document.getElementById(nodeInfo);
9517         }else if(typeof nodeInfo == "number"){
9518             return this.nodes[nodeInfo];
9519         }
9520         return nodeInfo;
9521     },
9522
9523     /**
9524      * Gets a range template nodes.
9525      * @param {Number} startIndex
9526      * @param {Number} endIndex
9527      * @return {Array} An array of nodes
9528      */
9529     getNodes : function(start, end){
9530         var ns = this.nodes;
9531         start = start || 0;
9532         end = typeof end == "undefined" ? ns.length - 1 : end;
9533         var nodes = [];
9534         if(start <= end){
9535             for(var i = start; i <= end; i++){
9536                 nodes.push(ns[i]);
9537             }
9538         } else{
9539             for(var i = start; i >= end; i--){
9540                 nodes.push(ns[i]);
9541             }
9542         }
9543         return nodes;
9544     },
9545
9546     /**
9547      * Finds the index of the passed node
9548      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9549      * @return {Number} The index of the node or -1
9550      */
9551     indexOf : function(node){
9552         node = this.getNode(node);
9553         if(typeof node.nodeIndex == "number"){
9554             return node.nodeIndex;
9555         }
9556         var ns = this.nodes;
9557         for(var i = 0, len = ns.length; i < len; i++){
9558             if(ns[i] == node){
9559                 return i;
9560             }
9561         }
9562         return -1;
9563     }
9564 });
9565 /*
9566  * Based on:
9567  * Ext JS Library 1.1.1
9568  * Copyright(c) 2006-2007, Ext JS, LLC.
9569  *
9570  * Originally Released Under LGPL - original licence link has changed is not relivant.
9571  *
9572  * Fork - LGPL
9573  * <script type="text/javascript">
9574  */
9575
9576 /**
9577  * @class Roo.JsonView
9578  * @extends Roo.View
9579  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9580 <pre><code>
9581 var view = new Roo.JsonView({
9582     container: "my-element",
9583     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9584     multiSelect: true, 
9585     jsonRoot: "data" 
9586 });
9587
9588 // listen for node click?
9589 view.on("click", function(vw, index, node, e){
9590     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9591 });
9592
9593 // direct load of JSON data
9594 view.load("foobar.php");
9595
9596 // Example from my blog list
9597 var tpl = new Roo.Template(
9598     '&lt;div class="entry"&gt;' +
9599     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9600     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9601     "&lt;/div&gt;&lt;hr /&gt;"
9602 );
9603
9604 var moreView = new Roo.JsonView({
9605     container :  "entry-list", 
9606     template : tpl,
9607     jsonRoot: "posts"
9608 });
9609 moreView.on("beforerender", this.sortEntries, this);
9610 moreView.load({
9611     url: "/blog/get-posts.php",
9612     params: "allposts=true",
9613     text: "Loading Blog Entries..."
9614 });
9615 </code></pre>
9616
9617 * Note: old code is supported with arguments : (container, template, config)
9618
9619
9620  * @constructor
9621  * Create a new JsonView
9622  * 
9623  * @param {Object} config The config object
9624  * 
9625  */
9626 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9627     
9628     
9629     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9630
9631     var um = this.el.getUpdateManager();
9632     um.setRenderer(this);
9633     um.on("update", this.onLoad, this);
9634     um.on("failure", this.onLoadException, this);
9635
9636     /**
9637      * @event beforerender
9638      * Fires before rendering of the downloaded JSON data.
9639      * @param {Roo.JsonView} this
9640      * @param {Object} data The JSON data loaded
9641      */
9642     /**
9643      * @event load
9644      * Fires when data is loaded.
9645      * @param {Roo.JsonView} this
9646      * @param {Object} data The JSON data loaded
9647      * @param {Object} response The raw Connect response object
9648      */
9649     /**
9650      * @event loadexception
9651      * Fires when loading fails.
9652      * @param {Roo.JsonView} this
9653      * @param {Object} response The raw Connect response object
9654      */
9655     this.addEvents({
9656         'beforerender' : true,
9657         'load' : true,
9658         'loadexception' : true
9659     });
9660 };
9661 Roo.extend(Roo.JsonView, Roo.View, {
9662     /**
9663      * @type {String} The root property in the loaded JSON object that contains the data
9664      */
9665     jsonRoot : "",
9666
9667     /**
9668      * Refreshes the view.
9669      */
9670     refresh : function(){
9671         this.clearSelections();
9672         this.el.update("");
9673         var html = [];
9674         var o = this.jsonData;
9675         if(o && o.length > 0){
9676             for(var i = 0, len = o.length; i < len; i++){
9677                 var data = this.prepareData(o[i], i, o);
9678                 html[html.length] = this.tpl.apply(data);
9679             }
9680         }else{
9681             html.push(this.emptyText);
9682         }
9683         this.el.update(html.join(""));
9684         this.nodes = this.el.dom.childNodes;
9685         this.updateIndexes(0);
9686     },
9687
9688     /**
9689      * 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.
9690      * @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:
9691      <pre><code>
9692      view.load({
9693          url: "your-url.php",
9694          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9695          callback: yourFunction,
9696          scope: yourObject, //(optional scope)
9697          discardUrl: false,
9698          nocache: false,
9699          text: "Loading...",
9700          timeout: 30,
9701          scripts: false
9702      });
9703      </code></pre>
9704      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9705      * 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.
9706      * @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}
9707      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9708      * @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.
9709      */
9710     load : function(){
9711         var um = this.el.getUpdateManager();
9712         um.update.apply(um, arguments);
9713     },
9714
9715     render : function(el, response){
9716         this.clearSelections();
9717         this.el.update("");
9718         var o;
9719         try{
9720             o = Roo.util.JSON.decode(response.responseText);
9721             if(this.jsonRoot){
9722                 
9723                 o = o[this.jsonRoot];
9724             }
9725         } catch(e){
9726         }
9727         /**
9728          * The current JSON data or null
9729          */
9730         this.jsonData = o;
9731         this.beforeRender();
9732         this.refresh();
9733     },
9734
9735 /**
9736  * Get the number of records in the current JSON dataset
9737  * @return {Number}
9738  */
9739     getCount : function(){
9740         return this.jsonData ? this.jsonData.length : 0;
9741     },
9742
9743 /**
9744  * Returns the JSON object for the specified node(s)
9745  * @param {HTMLElement/Array} node The node or an array of nodes
9746  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9747  * you get the JSON object for the node
9748  */
9749     getNodeData : function(node){
9750         if(node instanceof Array){
9751             var data = [];
9752             for(var i = 0, len = node.length; i < len; i++){
9753                 data.push(this.getNodeData(node[i]));
9754             }
9755             return data;
9756         }
9757         return this.jsonData[this.indexOf(node)] || null;
9758     },
9759
9760     beforeRender : function(){
9761         this.snapshot = this.jsonData;
9762         if(this.sortInfo){
9763             this.sort.apply(this, this.sortInfo);
9764         }
9765         this.fireEvent("beforerender", this, this.jsonData);
9766     },
9767
9768     onLoad : function(el, o){
9769         this.fireEvent("load", this, this.jsonData, o);
9770     },
9771
9772     onLoadException : function(el, o){
9773         this.fireEvent("loadexception", this, o);
9774     },
9775
9776 /**
9777  * Filter the data by a specific property.
9778  * @param {String} property A property on your JSON objects
9779  * @param {String/RegExp} value Either string that the property values
9780  * should start with, or a RegExp to test against the property
9781  */
9782     filter : function(property, value){
9783         if(this.jsonData){
9784             var data = [];
9785             var ss = this.snapshot;
9786             if(typeof value == "string"){
9787                 var vlen = value.length;
9788                 if(vlen == 0){
9789                     this.clearFilter();
9790                     return;
9791                 }
9792                 value = value.toLowerCase();
9793                 for(var i = 0, len = ss.length; i < len; i++){
9794                     var o = ss[i];
9795                     if(o[property].substr(0, vlen).toLowerCase() == value){
9796                         data.push(o);
9797                     }
9798                 }
9799             } else if(value.exec){ // regex?
9800                 for(var i = 0, len = ss.length; i < len; i++){
9801                     var o = ss[i];
9802                     if(value.test(o[property])){
9803                         data.push(o);
9804                     }
9805                 }
9806             } else{
9807                 return;
9808             }
9809             this.jsonData = data;
9810             this.refresh();
9811         }
9812     },
9813
9814 /**
9815  * Filter by a function. The passed function will be called with each
9816  * object in the current dataset. If the function returns true the value is kept,
9817  * otherwise it is filtered.
9818  * @param {Function} fn
9819  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9820  */
9821     filterBy : function(fn, scope){
9822         if(this.jsonData){
9823             var data = [];
9824             var ss = this.snapshot;
9825             for(var i = 0, len = ss.length; i < len; i++){
9826                 var o = ss[i];
9827                 if(fn.call(scope || this, o)){
9828                     data.push(o);
9829                 }
9830             }
9831             this.jsonData = data;
9832             this.refresh();
9833         }
9834     },
9835
9836 /**
9837  * Clears the current filter.
9838  */
9839     clearFilter : function(){
9840         if(this.snapshot && this.jsonData != this.snapshot){
9841             this.jsonData = this.snapshot;
9842             this.refresh();
9843         }
9844     },
9845
9846
9847 /**
9848  * Sorts the data for this view and refreshes it.
9849  * @param {String} property A property on your JSON objects to sort on
9850  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9851  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9852  */
9853     sort : function(property, dir, sortType){
9854         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9855         if(this.jsonData){
9856             var p = property;
9857             var dsc = dir && dir.toLowerCase() == "desc";
9858             var f = function(o1, o2){
9859                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9860                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9861                 ;
9862                 if(v1 < v2){
9863                     return dsc ? +1 : -1;
9864                 } else if(v1 > v2){
9865                     return dsc ? -1 : +1;
9866                 } else{
9867                     return 0;
9868                 }
9869             };
9870             this.jsonData.sort(f);
9871             this.refresh();
9872             if(this.jsonData != this.snapshot){
9873                 this.snapshot.sort(f);
9874             }
9875         }
9876     }
9877 });/*
9878  * Based on:
9879  * Ext JS Library 1.1.1
9880  * Copyright(c) 2006-2007, Ext JS, LLC.
9881  *
9882  * Originally Released Under LGPL - original licence link has changed is not relivant.
9883  *
9884  * Fork - LGPL
9885  * <script type="text/javascript">
9886  */
9887  
9888
9889 /**
9890  * @class Roo.ColorPalette
9891  * @extends Roo.Component
9892  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9893  * Here's an example of typical usage:
9894  * <pre><code>
9895 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9896 cp.render('my-div');
9897
9898 cp.on('select', function(palette, selColor){
9899     // do something with selColor
9900 });
9901 </code></pre>
9902  * @constructor
9903  * Create a new ColorPalette
9904  * @param {Object} config The config object
9905  */
9906 Roo.ColorPalette = function(config){
9907     Roo.ColorPalette.superclass.constructor.call(this, config);
9908     this.addEvents({
9909         /**
9910              * @event select
9911              * Fires when a color is selected
9912              * @param {ColorPalette} this
9913              * @param {String} color The 6-digit color hex code (without the # symbol)
9914              */
9915         select: true
9916     });
9917
9918     if(this.handler){
9919         this.on("select", this.handler, this.scope, true);
9920     }
9921 };
9922 Roo.extend(Roo.ColorPalette, Roo.Component, {
9923     /**
9924      * @cfg {String} itemCls
9925      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9926      */
9927     itemCls : "x-color-palette",
9928     /**
9929      * @cfg {String} value
9930      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9931      * the hex codes are case-sensitive.
9932      */
9933     value : null,
9934     clickEvent:'click',
9935     // private
9936     ctype: "Roo.ColorPalette",
9937
9938     /**
9939      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9940      */
9941     allowReselect : false,
9942
9943     /**
9944      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9945      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9946      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9947      * of colors with the width setting until the box is symmetrical.</p>
9948      * <p>You can override individual colors if needed:</p>
9949      * <pre><code>
9950 var cp = new Roo.ColorPalette();
9951 cp.colors[0] = "FF0000";  // change the first box to red
9952 </code></pre>
9953
9954 Or you can provide a custom array of your own for complete control:
9955 <pre><code>
9956 var cp = new Roo.ColorPalette();
9957 cp.colors = ["000000", "993300", "333300"];
9958 </code></pre>
9959      * @type Array
9960      */
9961     colors : [
9962         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9963         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9964         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9965         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9966         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9967     ],
9968
9969     // private
9970     onRender : function(container, position){
9971         var t = new Roo.MasterTemplate(
9972             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9973         );
9974         var c = this.colors;
9975         for(var i = 0, len = c.length; i < len; i++){
9976             t.add([c[i]]);
9977         }
9978         var el = document.createElement("div");
9979         el.className = this.itemCls;
9980         t.overwrite(el);
9981         container.dom.insertBefore(el, position);
9982         this.el = Roo.get(el);
9983         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9984         if(this.clickEvent != 'click'){
9985             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9986         }
9987     },
9988
9989     // private
9990     afterRender : function(){
9991         Roo.ColorPalette.superclass.afterRender.call(this);
9992         if(this.value){
9993             var s = this.value;
9994             this.value = null;
9995             this.select(s);
9996         }
9997     },
9998
9999     // private
10000     handleClick : function(e, t){
10001         e.preventDefault();
10002         if(!this.disabled){
10003             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10004             this.select(c.toUpperCase());
10005         }
10006     },
10007
10008     /**
10009      * Selects the specified color in the palette (fires the select event)
10010      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10011      */
10012     select : function(color){
10013         color = color.replace("#", "");
10014         if(color != this.value || this.allowReselect){
10015             var el = this.el;
10016             if(this.value){
10017                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10018             }
10019             el.child("a.color-"+color).addClass("x-color-palette-sel");
10020             this.value = color;
10021             this.fireEvent("select", this, color);
10022         }
10023     }
10024 });/*
10025  * Based on:
10026  * Ext JS Library 1.1.1
10027  * Copyright(c) 2006-2007, Ext JS, LLC.
10028  *
10029  * Originally Released Under LGPL - original licence link has changed is not relivant.
10030  *
10031  * Fork - LGPL
10032  * <script type="text/javascript">
10033  */
10034  
10035 /**
10036  * @class Roo.DatePicker
10037  * @extends Roo.Component
10038  * Simple date picker class.
10039  * @constructor
10040  * Create a new DatePicker
10041  * @param {Object} config The config object
10042  */
10043 Roo.DatePicker = function(config){
10044     Roo.DatePicker.superclass.constructor.call(this, config);
10045
10046     this.value = config && config.value ?
10047                  config.value.clearTime() : new Date().clearTime();
10048
10049     this.addEvents({
10050         /**
10051              * @event select
10052              * Fires when a date is selected
10053              * @param {DatePicker} this
10054              * @param {Date} date The selected date
10055              */
10056         select: true
10057     });
10058
10059     if(this.handler){
10060         this.on("select", this.handler,  this.scope || this);
10061     }
10062     // build the disabledDatesRE
10063     if(!this.disabledDatesRE && this.disabledDates){
10064         var dd = this.disabledDates;
10065         var re = "(?:";
10066         for(var i = 0; i < dd.length; i++){
10067             re += dd[i];
10068             if(i != dd.length-1) re += "|";
10069         }
10070         this.disabledDatesRE = new RegExp(re + ")");
10071     }
10072 };
10073
10074 Roo.extend(Roo.DatePicker, Roo.Component, {
10075     /**
10076      * @cfg {String} todayText
10077      * The text to display on the button that selects the current date (defaults to "Today")
10078      */
10079     todayText : "Today",
10080     /**
10081      * @cfg {String} okText
10082      * The text to display on the ok button
10083      */
10084     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10085     /**
10086      * @cfg {String} cancelText
10087      * The text to display on the cancel button
10088      */
10089     cancelText : "Cancel",
10090     /**
10091      * @cfg {String} todayTip
10092      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10093      */
10094     todayTip : "{0} (Spacebar)",
10095     /**
10096      * @cfg {Date} minDate
10097      * Minimum allowable date (JavaScript date object, defaults to null)
10098      */
10099     minDate : null,
10100     /**
10101      * @cfg {Date} maxDate
10102      * Maximum allowable date (JavaScript date object, defaults to null)
10103      */
10104     maxDate : null,
10105     /**
10106      * @cfg {String} minText
10107      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10108      */
10109     minText : "This date is before the minimum date",
10110     /**
10111      * @cfg {String} maxText
10112      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10113      */
10114     maxText : "This date is after the maximum date",
10115     /**
10116      * @cfg {String} format
10117      * The default date format string which can be overriden for localization support.  The format must be
10118      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10119      */
10120     format : "m/d/y",
10121     /**
10122      * @cfg {Array} disabledDays
10123      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10124      */
10125     disabledDays : null,
10126     /**
10127      * @cfg {String} disabledDaysText
10128      * The tooltip to display when the date falls on a disabled day (defaults to "")
10129      */
10130     disabledDaysText : "",
10131     /**
10132      * @cfg {RegExp} disabledDatesRE
10133      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10134      */
10135     disabledDatesRE : null,
10136     /**
10137      * @cfg {String} disabledDatesText
10138      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10139      */
10140     disabledDatesText : "",
10141     /**
10142      * @cfg {Boolean} constrainToViewport
10143      * True to constrain the date picker to the viewport (defaults to true)
10144      */
10145     constrainToViewport : true,
10146     /**
10147      * @cfg {Array} monthNames
10148      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10149      */
10150     monthNames : Date.monthNames,
10151     /**
10152      * @cfg {Array} dayNames
10153      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10154      */
10155     dayNames : Date.dayNames,
10156     /**
10157      * @cfg {String} nextText
10158      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10159      */
10160     nextText: 'Next Month (Control+Right)',
10161     /**
10162      * @cfg {String} prevText
10163      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10164      */
10165     prevText: 'Previous Month (Control+Left)',
10166     /**
10167      * @cfg {String} monthYearText
10168      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10169      */
10170     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10171     /**
10172      * @cfg {Number} startDay
10173      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10174      */
10175     startDay : 0,
10176     /**
10177      * @cfg {Bool} showClear
10178      * Show a clear button (usefull for date form elements that can be blank.)
10179      */
10180     
10181     showClear: false,
10182     
10183     /**
10184      * Sets the value of the date field
10185      * @param {Date} value The date to set
10186      */
10187     setValue : function(value){
10188         var old = this.value;
10189         this.value = value.clearTime(true);
10190         if(this.el){
10191             this.update(this.value);
10192         }
10193     },
10194
10195     /**
10196      * Gets the current selected value of the date field
10197      * @return {Date} The selected date
10198      */
10199     getValue : function(){
10200         return this.value;
10201     },
10202
10203     // private
10204     focus : function(){
10205         if(this.el){
10206             this.update(this.activeDate);
10207         }
10208     },
10209
10210     // private
10211     onRender : function(container, position){
10212         var m = [
10213              '<table cellspacing="0">',
10214                 '<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>',
10215                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10216         var dn = this.dayNames;
10217         for(var i = 0; i < 7; i++){
10218             var d = this.startDay+i;
10219             if(d > 6){
10220                 d = d-7;
10221             }
10222             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10223         }
10224         m[m.length] = "</tr></thead><tbody><tr>";
10225         for(var i = 0; i < 42; i++) {
10226             if(i % 7 == 0 && i != 0){
10227                 m[m.length] = "</tr><tr>";
10228             }
10229             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10230         }
10231         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10232             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10233
10234         var el = document.createElement("div");
10235         el.className = "x-date-picker";
10236         el.innerHTML = m.join("");
10237
10238         container.dom.insertBefore(el, position);
10239
10240         this.el = Roo.get(el);
10241         this.eventEl = Roo.get(el.firstChild);
10242
10243         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10244             handler: this.showPrevMonth,
10245             scope: this,
10246             preventDefault:true,
10247             stopDefault:true
10248         });
10249
10250         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10251             handler: this.showNextMonth,
10252             scope: this,
10253             preventDefault:true,
10254             stopDefault:true
10255         });
10256
10257         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10258
10259         this.monthPicker = this.el.down('div.x-date-mp');
10260         this.monthPicker.enableDisplayMode('block');
10261         
10262         var kn = new Roo.KeyNav(this.eventEl, {
10263             "left" : function(e){
10264                 e.ctrlKey ?
10265                     this.showPrevMonth() :
10266                     this.update(this.activeDate.add("d", -1));
10267             },
10268
10269             "right" : function(e){
10270                 e.ctrlKey ?
10271                     this.showNextMonth() :
10272                     this.update(this.activeDate.add("d", 1));
10273             },
10274
10275             "up" : function(e){
10276                 e.ctrlKey ?
10277                     this.showNextYear() :
10278                     this.update(this.activeDate.add("d", -7));
10279             },
10280
10281             "down" : function(e){
10282                 e.ctrlKey ?
10283                     this.showPrevYear() :
10284                     this.update(this.activeDate.add("d", 7));
10285             },
10286
10287             "pageUp" : function(e){
10288                 this.showNextMonth();
10289             },
10290
10291             "pageDown" : function(e){
10292                 this.showPrevMonth();
10293             },
10294
10295             "enter" : function(e){
10296                 e.stopPropagation();
10297                 return true;
10298             },
10299
10300             scope : this
10301         });
10302
10303         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10304
10305         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10306
10307         this.el.unselectable();
10308         
10309         this.cells = this.el.select("table.x-date-inner tbody td");
10310         this.textNodes = this.el.query("table.x-date-inner tbody span");
10311
10312         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10313             text: "&#160;",
10314             tooltip: this.monthYearText
10315         });
10316
10317         this.mbtn.on('click', this.showMonthPicker, this);
10318         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10319
10320
10321         var today = (new Date()).dateFormat(this.format);
10322         
10323         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10324         if (this.showClear) {
10325             baseTb.add( new Roo.Toolbar.Fill());
10326         }
10327         baseTb.add({
10328             text: String.format(this.todayText, today),
10329             tooltip: String.format(this.todayTip, today),
10330             handler: this.selectToday,
10331             scope: this
10332         });
10333         
10334         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10335             
10336         //});
10337         if (this.showClear) {
10338             
10339             baseTb.add( new Roo.Toolbar.Fill());
10340             baseTb.add({
10341                 text: '&#160;',
10342                 cls: 'x-btn-icon x-btn-clear',
10343                 handler: function() {
10344                     //this.value = '';
10345                     this.fireEvent("select", this, '');
10346                 },
10347                 scope: this
10348             });
10349         }
10350         
10351         
10352         if(Roo.isIE){
10353             this.el.repaint();
10354         }
10355         this.update(this.value);
10356     },
10357
10358     createMonthPicker : function(){
10359         if(!this.monthPicker.dom.firstChild){
10360             var buf = ['<table border="0" cellspacing="0">'];
10361             for(var i = 0; i < 6; i++){
10362                 buf.push(
10363                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10364                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10365                     i == 0 ?
10366                     '<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>' :
10367                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10368                 );
10369             }
10370             buf.push(
10371                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10372                     this.okText,
10373                     '</button><button type="button" class="x-date-mp-cancel">',
10374                     this.cancelText,
10375                     '</button></td></tr>',
10376                 '</table>'
10377             );
10378             this.monthPicker.update(buf.join(''));
10379             this.monthPicker.on('click', this.onMonthClick, this);
10380             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10381
10382             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10383             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10384
10385             this.mpMonths.each(function(m, a, i){
10386                 i += 1;
10387                 if((i%2) == 0){
10388                     m.dom.xmonth = 5 + Math.round(i * .5);
10389                 }else{
10390                     m.dom.xmonth = Math.round((i-1) * .5);
10391                 }
10392             });
10393         }
10394     },
10395
10396     showMonthPicker : function(){
10397         this.createMonthPicker();
10398         var size = this.el.getSize();
10399         this.monthPicker.setSize(size);
10400         this.monthPicker.child('table').setSize(size);
10401
10402         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10403         this.updateMPMonth(this.mpSelMonth);
10404         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10405         this.updateMPYear(this.mpSelYear);
10406
10407         this.monthPicker.slideIn('t', {duration:.2});
10408     },
10409
10410     updateMPYear : function(y){
10411         this.mpyear = y;
10412         var ys = this.mpYears.elements;
10413         for(var i = 1; i <= 10; i++){
10414             var td = ys[i-1], y2;
10415             if((i%2) == 0){
10416                 y2 = y + Math.round(i * .5);
10417                 td.firstChild.innerHTML = y2;
10418                 td.xyear = y2;
10419             }else{
10420                 y2 = y - (5-Math.round(i * .5));
10421                 td.firstChild.innerHTML = y2;
10422                 td.xyear = y2;
10423             }
10424             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10425         }
10426     },
10427
10428     updateMPMonth : function(sm){
10429         this.mpMonths.each(function(m, a, i){
10430             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10431         });
10432     },
10433
10434     selectMPMonth: function(m){
10435         
10436     },
10437
10438     onMonthClick : function(e, t){
10439         e.stopEvent();
10440         var el = new Roo.Element(t), pn;
10441         if(el.is('button.x-date-mp-cancel')){
10442             this.hideMonthPicker();
10443         }
10444         else if(el.is('button.x-date-mp-ok')){
10445             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10446             this.hideMonthPicker();
10447         }
10448         else if(pn = el.up('td.x-date-mp-month', 2)){
10449             this.mpMonths.removeClass('x-date-mp-sel');
10450             pn.addClass('x-date-mp-sel');
10451             this.mpSelMonth = pn.dom.xmonth;
10452         }
10453         else if(pn = el.up('td.x-date-mp-year', 2)){
10454             this.mpYears.removeClass('x-date-mp-sel');
10455             pn.addClass('x-date-mp-sel');
10456             this.mpSelYear = pn.dom.xyear;
10457         }
10458         else if(el.is('a.x-date-mp-prev')){
10459             this.updateMPYear(this.mpyear-10);
10460         }
10461         else if(el.is('a.x-date-mp-next')){
10462             this.updateMPYear(this.mpyear+10);
10463         }
10464     },
10465
10466     onMonthDblClick : function(e, t){
10467         e.stopEvent();
10468         var el = new Roo.Element(t), pn;
10469         if(pn = el.up('td.x-date-mp-month', 2)){
10470             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10471             this.hideMonthPicker();
10472         }
10473         else if(pn = el.up('td.x-date-mp-year', 2)){
10474             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10475             this.hideMonthPicker();
10476         }
10477     },
10478
10479     hideMonthPicker : function(disableAnim){
10480         if(this.monthPicker){
10481             if(disableAnim === true){
10482                 this.monthPicker.hide();
10483             }else{
10484                 this.monthPicker.slideOut('t', {duration:.2});
10485             }
10486         }
10487     },
10488
10489     // private
10490     showPrevMonth : function(e){
10491         this.update(this.activeDate.add("mo", -1));
10492     },
10493
10494     // private
10495     showNextMonth : function(e){
10496         this.update(this.activeDate.add("mo", 1));
10497     },
10498
10499     // private
10500     showPrevYear : function(){
10501         this.update(this.activeDate.add("y", -1));
10502     },
10503
10504     // private
10505     showNextYear : function(){
10506         this.update(this.activeDate.add("y", 1));
10507     },
10508
10509     // private
10510     handleMouseWheel : function(e){
10511         var delta = e.getWheelDelta();
10512         if(delta > 0){
10513             this.showPrevMonth();
10514             e.stopEvent();
10515         } else if(delta < 0){
10516             this.showNextMonth();
10517             e.stopEvent();
10518         }
10519     },
10520
10521     // private
10522     handleDateClick : function(e, t){
10523         e.stopEvent();
10524         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10525             this.setValue(new Date(t.dateValue));
10526             this.fireEvent("select", this, this.value);
10527         }
10528     },
10529
10530     // private
10531     selectToday : function(){
10532         this.setValue(new Date().clearTime());
10533         this.fireEvent("select", this, this.value);
10534     },
10535
10536     // private
10537     update : function(date){
10538         var vd = this.activeDate;
10539         this.activeDate = date;
10540         if(vd && this.el){
10541             var t = date.getTime();
10542             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10543                 this.cells.removeClass("x-date-selected");
10544                 this.cells.each(function(c){
10545                    if(c.dom.firstChild.dateValue == t){
10546                        c.addClass("x-date-selected");
10547                        setTimeout(function(){
10548                             try{c.dom.firstChild.focus();}catch(e){}
10549                        }, 50);
10550                        return false;
10551                    }
10552                 });
10553                 return;
10554             }
10555         }
10556         var days = date.getDaysInMonth();
10557         var firstOfMonth = date.getFirstDateOfMonth();
10558         var startingPos = firstOfMonth.getDay()-this.startDay;
10559
10560         if(startingPos <= this.startDay){
10561             startingPos += 7;
10562         }
10563
10564         var pm = date.add("mo", -1);
10565         var prevStart = pm.getDaysInMonth()-startingPos;
10566
10567         var cells = this.cells.elements;
10568         var textEls = this.textNodes;
10569         days += startingPos;
10570
10571         // convert everything to numbers so it's fast
10572         var day = 86400000;
10573         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10574         var today = new Date().clearTime().getTime();
10575         var sel = date.clearTime().getTime();
10576         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10577         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10578         var ddMatch = this.disabledDatesRE;
10579         var ddText = this.disabledDatesText;
10580         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10581         var ddaysText = this.disabledDaysText;
10582         var format = this.format;
10583
10584         var setCellClass = function(cal, cell){
10585             cell.title = "";
10586             var t = d.getTime();
10587             cell.firstChild.dateValue = t;
10588             if(t == today){
10589                 cell.className += " x-date-today";
10590                 cell.title = cal.todayText;
10591             }
10592             if(t == sel){
10593                 cell.className += " x-date-selected";
10594                 setTimeout(function(){
10595                     try{cell.firstChild.focus();}catch(e){}
10596                 }, 50);
10597             }
10598             // disabling
10599             if(t < min) {
10600                 cell.className = " x-date-disabled";
10601                 cell.title = cal.minText;
10602                 return;
10603             }
10604             if(t > max) {
10605                 cell.className = " x-date-disabled";
10606                 cell.title = cal.maxText;
10607                 return;
10608             }
10609             if(ddays){
10610                 if(ddays.indexOf(d.getDay()) != -1){
10611                     cell.title = ddaysText;
10612                     cell.className = " x-date-disabled";
10613                 }
10614             }
10615             if(ddMatch && format){
10616                 var fvalue = d.dateFormat(format);
10617                 if(ddMatch.test(fvalue)){
10618                     cell.title = ddText.replace("%0", fvalue);
10619                     cell.className = " x-date-disabled";
10620                 }
10621             }
10622         };
10623
10624         var i = 0;
10625         for(; i < startingPos; i++) {
10626             textEls[i].innerHTML = (++prevStart);
10627             d.setDate(d.getDate()+1);
10628             cells[i].className = "x-date-prevday";
10629             setCellClass(this, cells[i]);
10630         }
10631         for(; i < days; i++){
10632             intDay = i - startingPos + 1;
10633             textEls[i].innerHTML = (intDay);
10634             d.setDate(d.getDate()+1);
10635             cells[i].className = "x-date-active";
10636             setCellClass(this, cells[i]);
10637         }
10638         var extraDays = 0;
10639         for(; i < 42; i++) {
10640              textEls[i].innerHTML = (++extraDays);
10641              d.setDate(d.getDate()+1);
10642              cells[i].className = "x-date-nextday";
10643              setCellClass(this, cells[i]);
10644         }
10645
10646         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10647
10648         if(!this.internalRender){
10649             var main = this.el.dom.firstChild;
10650             var w = main.offsetWidth;
10651             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10652             Roo.fly(main).setWidth(w);
10653             this.internalRender = true;
10654             // opera does not respect the auto grow header center column
10655             // then, after it gets a width opera refuses to recalculate
10656             // without a second pass
10657             if(Roo.isOpera && !this.secondPass){
10658                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10659                 this.secondPass = true;
10660                 this.update.defer(10, this, [date]);
10661             }
10662         }
10663     }
10664 });/*
10665  * Based on:
10666  * Ext JS Library 1.1.1
10667  * Copyright(c) 2006-2007, Ext JS, LLC.
10668  *
10669  * Originally Released Under LGPL - original licence link has changed is not relivant.
10670  *
10671  * Fork - LGPL
10672  * <script type="text/javascript">
10673  */
10674 /**
10675  * @class Roo.TabPanel
10676  * @extends Roo.util.Observable
10677  * A lightweight tab container.
10678  * <br><br>
10679  * Usage:
10680  * <pre><code>
10681 // basic tabs 1, built from existing content
10682 var tabs = new Roo.TabPanel("tabs1");
10683 tabs.addTab("script", "View Script");
10684 tabs.addTab("markup", "View Markup");
10685 tabs.activate("script");
10686
10687 // more advanced tabs, built from javascript
10688 var jtabs = new Roo.TabPanel("jtabs");
10689 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10690
10691 // set up the UpdateManager
10692 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10693 var updater = tab2.getUpdateManager();
10694 updater.setDefaultUrl("ajax1.htm");
10695 tab2.on('activate', updater.refresh, updater, true);
10696
10697 // Use setUrl for Ajax loading
10698 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10699 tab3.setUrl("ajax2.htm", null, true);
10700
10701 // Disabled tab
10702 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10703 tab4.disable();
10704
10705 jtabs.activate("jtabs-1");
10706  * </code></pre>
10707  * @constructor
10708  * Create a new TabPanel.
10709  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10710  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10711  */
10712 Roo.TabPanel = function(container, config){
10713     /**
10714     * The container element for this TabPanel.
10715     * @type Roo.Element
10716     */
10717     this.el = Roo.get(container, true);
10718     if(config){
10719         if(typeof config == "boolean"){
10720             this.tabPosition = config ? "bottom" : "top";
10721         }else{
10722             Roo.apply(this, config);
10723         }
10724     }
10725     if(this.tabPosition == "bottom"){
10726         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10727         this.el.addClass("x-tabs-bottom");
10728     }
10729     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10730     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10731     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10732     if(Roo.isIE){
10733         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10734     }
10735     if(this.tabPosition != "bottom"){
10736     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10737      * @type Roo.Element
10738      */
10739       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10740       this.el.addClass("x-tabs-top");
10741     }
10742     this.items = [];
10743
10744     this.bodyEl.setStyle("position", "relative");
10745
10746     this.active = null;
10747     this.activateDelegate = this.activate.createDelegate(this);
10748
10749     this.addEvents({
10750         /**
10751          * @event tabchange
10752          * Fires when the active tab changes
10753          * @param {Roo.TabPanel} this
10754          * @param {Roo.TabPanelItem} activePanel The new active tab
10755          */
10756         "tabchange": true,
10757         /**
10758          * @event beforetabchange
10759          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10760          * @param {Roo.TabPanel} this
10761          * @param {Object} e Set cancel to true on this object to cancel the tab change
10762          * @param {Roo.TabPanelItem} tab The tab being changed to
10763          */
10764         "beforetabchange" : true
10765     });
10766
10767     Roo.EventManager.onWindowResize(this.onResize, this);
10768     this.cpad = this.el.getPadding("lr");
10769     this.hiddenCount = 0;
10770
10771     Roo.TabPanel.superclass.constructor.call(this);
10772 };
10773
10774 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10775         /*
10776          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10777          */
10778     tabPosition : "top",
10779         /*
10780          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10781          */
10782     currentTabWidth : 0,
10783         /*
10784          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10785          */
10786     minTabWidth : 40,
10787         /*
10788          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10789          */
10790     maxTabWidth : 250,
10791         /*
10792          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10793          */
10794     preferredTabWidth : 175,
10795         /*
10796          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10797          */
10798     resizeTabs : false,
10799         /*
10800          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10801          */
10802     monitorResize : true,
10803
10804     /**
10805      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10806      * @param {String} id The id of the div to use <b>or create</b>
10807      * @param {String} text The text for the tab
10808      * @param {String} content (optional) Content to put in the TabPanelItem body
10809      * @param {Boolean} closable (optional) True to create a close icon on the tab
10810      * @return {Roo.TabPanelItem} The created TabPanelItem
10811      */
10812     addTab : function(id, text, content, closable){
10813         var item = new Roo.TabPanelItem(this, id, text, closable);
10814         this.addTabItem(item);
10815         if(content){
10816             item.setContent(content);
10817         }
10818         return item;
10819     },
10820
10821     /**
10822      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10823      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10824      * @return {Roo.TabPanelItem}
10825      */
10826     getTab : function(id){
10827         return this.items[id];
10828     },
10829
10830     /**
10831      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10832      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10833      */
10834     hideTab : function(id){
10835         var t = this.items[id];
10836         if(!t.isHidden()){
10837            t.setHidden(true);
10838            this.hiddenCount++;
10839            this.autoSizeTabs();
10840         }
10841     },
10842
10843     /**
10844      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10845      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10846      */
10847     unhideTab : function(id){
10848         var t = this.items[id];
10849         if(t.isHidden()){
10850            t.setHidden(false);
10851            this.hiddenCount--;
10852            this.autoSizeTabs();
10853         }
10854     },
10855
10856     /**
10857      * Adds an existing {@link Roo.TabPanelItem}.
10858      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10859      */
10860     addTabItem : function(item){
10861         this.items[item.id] = item;
10862         this.items.push(item);
10863         if(this.resizeTabs){
10864            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10865            this.autoSizeTabs();
10866         }else{
10867             item.autoSize();
10868         }
10869     },
10870
10871     /**
10872      * Removes a {@link Roo.TabPanelItem}.
10873      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10874      */
10875     removeTab : function(id){
10876         var items = this.items;
10877         var tab = items[id];
10878         if(!tab) { return; }
10879         var index = items.indexOf(tab);
10880         if(this.active == tab && items.length > 1){
10881             var newTab = this.getNextAvailable(index);
10882             if(newTab) {
10883                 newTab.activate();
10884             }
10885         }
10886         this.stripEl.dom.removeChild(tab.pnode.dom);
10887         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10888             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10889         }
10890         items.splice(index, 1);
10891         delete this.items[tab.id];
10892         tab.fireEvent("close", tab);
10893         tab.purgeListeners();
10894         this.autoSizeTabs();
10895     },
10896
10897     getNextAvailable : function(start){
10898         var items = this.items;
10899         var index = start;
10900         // look for a next tab that will slide over to
10901         // replace the one being removed
10902         while(index < items.length){
10903             var item = items[++index];
10904             if(item && !item.isHidden()){
10905                 return item;
10906             }
10907         }
10908         // if one isn't found select the previous tab (on the left)
10909         index = start;
10910         while(index >= 0){
10911             var item = items[--index];
10912             if(item && !item.isHidden()){
10913                 return item;
10914             }
10915         }
10916         return null;
10917     },
10918
10919     /**
10920      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10921      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10922      */
10923     disableTab : function(id){
10924         var tab = this.items[id];
10925         if(tab && this.active != tab){
10926             tab.disable();
10927         }
10928     },
10929
10930     /**
10931      * Enables a {@link Roo.TabPanelItem} that is disabled.
10932      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10933      */
10934     enableTab : function(id){
10935         var tab = this.items[id];
10936         tab.enable();
10937     },
10938
10939     /**
10940      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10941      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10942      * @return {Roo.TabPanelItem} The TabPanelItem.
10943      */
10944     activate : function(id){
10945         var tab = this.items[id];
10946         if(!tab){
10947             return null;
10948         }
10949         if(tab == this.active || tab.disabled){
10950             return tab;
10951         }
10952         var e = {};
10953         this.fireEvent("beforetabchange", this, e, tab);
10954         if(e.cancel !== true && !tab.disabled){
10955             if(this.active){
10956                 this.active.hide();
10957             }
10958             this.active = this.items[id];
10959             this.active.show();
10960             this.fireEvent("tabchange", this, this.active);
10961         }
10962         return tab;
10963     },
10964
10965     /**
10966      * Gets the active {@link Roo.TabPanelItem}.
10967      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10968      */
10969     getActiveTab : function(){
10970         return this.active;
10971     },
10972
10973     /**
10974      * Updates the tab body element to fit the height of the container element
10975      * for overflow scrolling
10976      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10977      */
10978     syncHeight : function(targetHeight){
10979         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10980         var bm = this.bodyEl.getMargins();
10981         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10982         this.bodyEl.setHeight(newHeight);
10983         return newHeight;
10984     },
10985
10986     onResize : function(){
10987         if(this.monitorResize){
10988             this.autoSizeTabs();
10989         }
10990     },
10991
10992     /**
10993      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
10994      */
10995     beginUpdate : function(){
10996         this.updating = true;
10997     },
10998
10999     /**
11000      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11001      */
11002     endUpdate : function(){
11003         this.updating = false;
11004         this.autoSizeTabs();
11005     },
11006
11007     /**
11008      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11009      */
11010     autoSizeTabs : function(){
11011         var count = this.items.length;
11012         var vcount = count - this.hiddenCount;
11013         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11014         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11015         var availWidth = Math.floor(w / vcount);
11016         var b = this.stripBody;
11017         if(b.getWidth() > w){
11018             var tabs = this.items;
11019             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11020             if(availWidth < this.minTabWidth){
11021                 /*if(!this.sleft){    // incomplete scrolling code
11022                     this.createScrollButtons();
11023                 }
11024                 this.showScroll();
11025                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11026             }
11027         }else{
11028             if(this.currentTabWidth < this.preferredTabWidth){
11029                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11030             }
11031         }
11032     },
11033
11034     /**
11035      * Returns the number of tabs in this TabPanel.
11036      * @return {Number}
11037      */
11038      getCount : function(){
11039          return this.items.length;
11040      },
11041
11042     /**
11043      * Resizes all the tabs to the passed width
11044      * @param {Number} The new width
11045      */
11046     setTabWidth : function(width){
11047         this.currentTabWidth = width;
11048         for(var i = 0, len = this.items.length; i < len; i++) {
11049                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11050         }
11051     },
11052
11053     /**
11054      * Destroys this TabPanel
11055      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11056      */
11057     destroy : function(removeEl){
11058         Roo.EventManager.removeResizeListener(this.onResize, this);
11059         for(var i = 0, len = this.items.length; i < len; i++){
11060             this.items[i].purgeListeners();
11061         }
11062         if(removeEl === true){
11063             this.el.update("");
11064             this.el.remove();
11065         }
11066     }
11067 });
11068
11069 /**
11070  * @class Roo.TabPanelItem
11071  * @extends Roo.util.Observable
11072  * Represents an individual item (tab plus body) in a TabPanel.
11073  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11074  * @param {String} id The id of this TabPanelItem
11075  * @param {String} text The text for the tab of this TabPanelItem
11076  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11077  */
11078 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11079     /**
11080      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11081      * @type Roo.TabPanel
11082      */
11083     this.tabPanel = tabPanel;
11084     /**
11085      * The id for this TabPanelItem
11086      * @type String
11087      */
11088     this.id = id;
11089     /** @private */
11090     this.disabled = false;
11091     /** @private */
11092     this.text = text;
11093     /** @private */
11094     this.loaded = false;
11095     this.closable = closable;
11096
11097     /**
11098      * The body element for this TabPanelItem.
11099      * @type Roo.Element
11100      */
11101     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11102     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11103     this.bodyEl.setStyle("display", "block");
11104     this.bodyEl.setStyle("zoom", "1");
11105     this.hideAction();
11106
11107     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11108     /** @private */
11109     this.el = Roo.get(els.el, true);
11110     this.inner = Roo.get(els.inner, true);
11111     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11112     this.pnode = Roo.get(els.el.parentNode, true);
11113     this.el.on("mousedown", this.onTabMouseDown, this);
11114     this.el.on("click", this.onTabClick, this);
11115     /** @private */
11116     if(closable){
11117         var c = Roo.get(els.close, true);
11118         c.dom.title = this.closeText;
11119         c.addClassOnOver("close-over");
11120         c.on("click", this.closeClick, this);
11121      }
11122
11123     this.addEvents({
11124          /**
11125          * @event activate
11126          * Fires when this tab becomes the active tab.
11127          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11128          * @param {Roo.TabPanelItem} this
11129          */
11130         "activate": true,
11131         /**
11132          * @event beforeclose
11133          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11134          * @param {Roo.TabPanelItem} this
11135          * @param {Object} e Set cancel to true on this object to cancel the close.
11136          */
11137         "beforeclose": true,
11138         /**
11139          * @event close
11140          * Fires when this tab is closed.
11141          * @param {Roo.TabPanelItem} this
11142          */
11143          "close": true,
11144         /**
11145          * @event deactivate
11146          * Fires when this tab is no longer the active tab.
11147          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11148          * @param {Roo.TabPanelItem} this
11149          */
11150          "deactivate" : true
11151     });
11152     this.hidden = false;
11153
11154     Roo.TabPanelItem.superclass.constructor.call(this);
11155 };
11156
11157 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11158     purgeListeners : function(){
11159        Roo.util.Observable.prototype.purgeListeners.call(this);
11160        this.el.removeAllListeners();
11161     },
11162     /**
11163      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11164      */
11165     show : function(){
11166         this.pnode.addClass("on");
11167         this.showAction();
11168         if(Roo.isOpera){
11169             this.tabPanel.stripWrap.repaint();
11170         }
11171         this.fireEvent("activate", this.tabPanel, this);
11172     },
11173
11174     /**
11175      * Returns true if this tab is the active tab.
11176      * @return {Boolean}
11177      */
11178     isActive : function(){
11179         return this.tabPanel.getActiveTab() == this;
11180     },
11181
11182     /**
11183      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11184      */
11185     hide : function(){
11186         this.pnode.removeClass("on");
11187         this.hideAction();
11188         this.fireEvent("deactivate", this.tabPanel, this);
11189     },
11190
11191     hideAction : function(){
11192         this.bodyEl.hide();
11193         this.bodyEl.setStyle("position", "absolute");
11194         this.bodyEl.setLeft("-20000px");
11195         this.bodyEl.setTop("-20000px");
11196     },
11197
11198     showAction : function(){
11199         this.bodyEl.setStyle("position", "relative");
11200         this.bodyEl.setTop("");
11201         this.bodyEl.setLeft("");
11202         this.bodyEl.show();
11203     },
11204
11205     /**
11206      * Set the tooltip for the tab.
11207      * @param {String} tooltip The tab's tooltip
11208      */
11209     setTooltip : function(text){
11210         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11211             this.textEl.dom.qtip = text;
11212             this.textEl.dom.removeAttribute('title');
11213         }else{
11214             this.textEl.dom.title = text;
11215         }
11216     },
11217
11218     onTabClick : function(e){
11219         e.preventDefault();
11220         this.tabPanel.activate(this.id);
11221     },
11222
11223     onTabMouseDown : function(e){
11224         e.preventDefault();
11225         this.tabPanel.activate(this.id);
11226     },
11227
11228     getWidth : function(){
11229         return this.inner.getWidth();
11230     },
11231
11232     setWidth : function(width){
11233         var iwidth = width - this.pnode.getPadding("lr");
11234         this.inner.setWidth(iwidth);
11235         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11236         this.pnode.setWidth(width);
11237     },
11238
11239     /**
11240      * Show or hide the tab
11241      * @param {Boolean} hidden True to hide or false to show.
11242      */
11243     setHidden : function(hidden){
11244         this.hidden = hidden;
11245         this.pnode.setStyle("display", hidden ? "none" : "");
11246     },
11247
11248     /**
11249      * Returns true if this tab is "hidden"
11250      * @return {Boolean}
11251      */
11252     isHidden : function(){
11253         return this.hidden;
11254     },
11255
11256     /**
11257      * Returns the text for this tab
11258      * @return {String}
11259      */
11260     getText : function(){
11261         return this.text;
11262     },
11263
11264     autoSize : function(){
11265         //this.el.beginMeasure();
11266         this.textEl.setWidth(1);
11267         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11268         //this.el.endMeasure();
11269     },
11270
11271     /**
11272      * Sets the text for the tab (Note: this also sets the tooltip text)
11273      * @param {String} text The tab's text and tooltip
11274      */
11275     setText : function(text){
11276         this.text = text;
11277         this.textEl.update(text);
11278         this.setTooltip(text);
11279         if(!this.tabPanel.resizeTabs){
11280             this.autoSize();
11281         }
11282     },
11283     /**
11284      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11285      */
11286     activate : function(){
11287         this.tabPanel.activate(this.id);
11288     },
11289
11290     /**
11291      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11292      */
11293     disable : function(){
11294         if(this.tabPanel.active != this){
11295             this.disabled = true;
11296             this.pnode.addClass("disabled");
11297         }
11298     },
11299
11300     /**
11301      * Enables this TabPanelItem if it was previously disabled.
11302      */
11303     enable : function(){
11304         this.disabled = false;
11305         this.pnode.removeClass("disabled");
11306     },
11307
11308     /**
11309      * Sets the content for this TabPanelItem.
11310      * @param {String} content The content
11311      * @param {Boolean} loadScripts true to look for and load scripts
11312      */
11313     setContent : function(content, loadScripts){
11314         this.bodyEl.update(content, loadScripts);
11315     },
11316
11317     /**
11318      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11319      * @return {Roo.UpdateManager} The UpdateManager
11320      */
11321     getUpdateManager : function(){
11322         return this.bodyEl.getUpdateManager();
11323     },
11324
11325     /**
11326      * Set a URL to be used to load the content for this TabPanelItem.
11327      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11328      * @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)
11329      * @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)
11330      * @return {Roo.UpdateManager} The UpdateManager
11331      */
11332     setUrl : function(url, params, loadOnce){
11333         if(this.refreshDelegate){
11334             this.un('activate', this.refreshDelegate);
11335         }
11336         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11337         this.on("activate", this.refreshDelegate);
11338         return this.bodyEl.getUpdateManager();
11339     },
11340
11341     /** @private */
11342     _handleRefresh : function(url, params, loadOnce){
11343         if(!loadOnce || !this.loaded){
11344             var updater = this.bodyEl.getUpdateManager();
11345             updater.update(url, params, this._setLoaded.createDelegate(this));
11346         }
11347     },
11348
11349     /**
11350      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11351      *   Will fail silently if the setUrl method has not been called.
11352      *   This does not activate the panel, just updates its content.
11353      */
11354     refresh : function(){
11355         if(this.refreshDelegate){
11356            this.loaded = false;
11357            this.refreshDelegate();
11358         }
11359     },
11360
11361     /** @private */
11362     _setLoaded : function(){
11363         this.loaded = true;
11364     },
11365
11366     /** @private */
11367     closeClick : function(e){
11368         var o = {};
11369         e.stopEvent();
11370         this.fireEvent("beforeclose", this, o);
11371         if(o.cancel !== true){
11372             this.tabPanel.removeTab(this.id);
11373         }
11374     },
11375     /**
11376      * The text displayed in the tooltip for the close icon.
11377      * @type String
11378      */
11379     closeText : "Close this tab"
11380 });
11381
11382 /** @private */
11383 Roo.TabPanel.prototype.createStrip = function(container){
11384     var strip = document.createElement("div");
11385     strip.className = "x-tabs-wrap";
11386     container.appendChild(strip);
11387     return strip;
11388 };
11389 /** @private */
11390 Roo.TabPanel.prototype.createStripList = function(strip){
11391     // div wrapper for retard IE
11392     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>';
11393     return strip.firstChild.firstChild.firstChild.firstChild;
11394 };
11395 /** @private */
11396 Roo.TabPanel.prototype.createBody = function(container){
11397     var body = document.createElement("div");
11398     Roo.id(body, "tab-body");
11399     Roo.fly(body).addClass("x-tabs-body");
11400     container.appendChild(body);
11401     return body;
11402 };
11403 /** @private */
11404 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11405     var body = Roo.getDom(id);
11406     if(!body){
11407         body = document.createElement("div");
11408         body.id = id;
11409     }
11410     Roo.fly(body).addClass("x-tabs-item-body");
11411     bodyEl.insertBefore(body, bodyEl.firstChild);
11412     return body;
11413 };
11414 /** @private */
11415 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11416     var td = document.createElement("td");
11417     stripEl.appendChild(td);
11418     if(closable){
11419         td.className = "x-tabs-closable";
11420         if(!this.closeTpl){
11421             this.closeTpl = new Roo.Template(
11422                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11423                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11424                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11425             );
11426         }
11427         var el = this.closeTpl.overwrite(td, {"text": text});
11428         var close = el.getElementsByTagName("div")[0];
11429         var inner = el.getElementsByTagName("em")[0];
11430         return {"el": el, "close": close, "inner": inner};
11431     } else {
11432         if(!this.tabTpl){
11433             this.tabTpl = new Roo.Template(
11434                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11435                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11436             );
11437         }
11438         var el = this.tabTpl.overwrite(td, {"text": text});
11439         var inner = el.getElementsByTagName("em")[0];
11440         return {"el": el, "inner": inner};
11441     }
11442 };/*
11443  * Based on:
11444  * Ext JS Library 1.1.1
11445  * Copyright(c) 2006-2007, Ext JS, LLC.
11446  *
11447  * Originally Released Under LGPL - original licence link has changed is not relivant.
11448  *
11449  * Fork - LGPL
11450  * <script type="text/javascript">
11451  */
11452
11453 /**
11454  * @class Roo.Button
11455  * @extends Roo.util.Observable
11456  * Simple Button class
11457  * @cfg {String} text The button text
11458  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11459  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11460  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11461  * @cfg {Object} scope The scope of the handler
11462  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11463  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11464  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11465  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11466  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11467  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11468    applies if enableToggle = true)
11469  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11470  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11471   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11472  * @constructor
11473  * Create a new button
11474  * @param {Object} config The config object
11475  */
11476 Roo.Button = function(renderTo, config)
11477 {
11478     if (!config) {
11479         config = renderTo;
11480         renderTo = config.renderTo || false;
11481     }
11482     
11483     Roo.apply(this, config);
11484     this.addEvents({
11485         /**
11486              * @event click
11487              * Fires when this button is clicked
11488              * @param {Button} this
11489              * @param {EventObject} e The click event
11490              */
11491             "click" : true,
11492         /**
11493              * @event toggle
11494              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11495              * @param {Button} this
11496              * @param {Boolean} pressed
11497              */
11498             "toggle" : true,
11499         /**
11500              * @event mouseover
11501              * Fires when the mouse hovers over the button
11502              * @param {Button} this
11503              * @param {Event} e The event object
11504              */
11505         'mouseover' : true,
11506         /**
11507              * @event mouseout
11508              * Fires when the mouse exits the button
11509              * @param {Button} this
11510              * @param {Event} e The event object
11511              */
11512         'mouseout': true,
11513          /**
11514              * @event render
11515              * Fires when the button is rendered
11516              * @param {Button} this
11517              */
11518         'render': true
11519     });
11520     if(this.menu){
11521         this.menu = Roo.menu.MenuMgr.get(this.menu);
11522     }
11523     // register listeners first!!  - so render can be captured..
11524     Roo.util.Observable.call(this);
11525     if(renderTo){
11526         this.render(renderTo);
11527     }
11528     
11529   
11530 };
11531
11532 Roo.extend(Roo.Button, Roo.util.Observable, {
11533     /**
11534      * 
11535      */
11536     
11537     /**
11538      * Read-only. True if this button is hidden
11539      * @type Boolean
11540      */
11541     hidden : false,
11542     /**
11543      * Read-only. True if this button is disabled
11544      * @type Boolean
11545      */
11546     disabled : false,
11547     /**
11548      * Read-only. True if this button is pressed (only if enableToggle = true)
11549      * @type Boolean
11550      */
11551     pressed : false,
11552
11553     /**
11554      * @cfg {Number} tabIndex 
11555      * The DOM tabIndex for this button (defaults to undefined)
11556      */
11557     tabIndex : undefined,
11558
11559     /**
11560      * @cfg {Boolean} enableToggle
11561      * True to enable pressed/not pressed toggling (defaults to false)
11562      */
11563     enableToggle: false,
11564     /**
11565      * @cfg {Mixed} menu
11566      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11567      */
11568     menu : undefined,
11569     /**
11570      * @cfg {String} menuAlign
11571      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11572      */
11573     menuAlign : "tl-bl?",
11574
11575     /**
11576      * @cfg {String} iconCls
11577      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11578      */
11579     iconCls : undefined,
11580     /**
11581      * @cfg {String} type
11582      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11583      */
11584     type : 'button',
11585
11586     // private
11587     menuClassTarget: 'tr',
11588
11589     /**
11590      * @cfg {String} clickEvent
11591      * The type of event to map to the button's event handler (defaults to 'click')
11592      */
11593     clickEvent : 'click',
11594
11595     /**
11596      * @cfg {Boolean} handleMouseEvents
11597      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11598      */
11599     handleMouseEvents : true,
11600
11601     /**
11602      * @cfg {String} tooltipType
11603      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11604      */
11605     tooltipType : 'qtip',
11606
11607     /**
11608      * @cfg {String} cls
11609      * A CSS class to apply to the button's main element.
11610      */
11611     
11612     /**
11613      * @cfg {Roo.Template} template (Optional)
11614      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11615      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11616      * require code modifications if required elements (e.g. a button) aren't present.
11617      */
11618
11619     // private
11620     render : function(renderTo){
11621         var btn;
11622         if(this.hideParent){
11623             this.parentEl = Roo.get(renderTo);
11624         }
11625         if(!this.dhconfig){
11626             if(!this.template){
11627                 if(!Roo.Button.buttonTemplate){
11628                     // hideous table template
11629                     Roo.Button.buttonTemplate = new Roo.Template(
11630                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11631                         '<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>',
11632                         "</tr></tbody></table>");
11633                 }
11634                 this.template = Roo.Button.buttonTemplate;
11635             }
11636             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11637             var btnEl = btn.child("button:first");
11638             btnEl.on('focus', this.onFocus, this);
11639             btnEl.on('blur', this.onBlur, this);
11640             if(this.cls){
11641                 btn.addClass(this.cls);
11642             }
11643             if(this.icon){
11644                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11645             }
11646             if(this.iconCls){
11647                 btnEl.addClass(this.iconCls);
11648                 if(!this.cls){
11649                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11650                 }
11651             }
11652             if(this.tabIndex !== undefined){
11653                 btnEl.dom.tabIndex = this.tabIndex;
11654             }
11655             if(this.tooltip){
11656                 if(typeof this.tooltip == 'object'){
11657                     Roo.QuickTips.tips(Roo.apply({
11658                           target: btnEl.id
11659                     }, this.tooltip));
11660                 } else {
11661                     btnEl.dom[this.tooltipType] = this.tooltip;
11662                 }
11663             }
11664         }else{
11665             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11666         }
11667         this.el = btn;
11668         if(this.id){
11669             this.el.dom.id = this.el.id = this.id;
11670         }
11671         if(this.menu){
11672             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11673             this.menu.on("show", this.onMenuShow, this);
11674             this.menu.on("hide", this.onMenuHide, this);
11675         }
11676         btn.addClass("x-btn");
11677         if(Roo.isIE && !Roo.isIE7){
11678             this.autoWidth.defer(1, this);
11679         }else{
11680             this.autoWidth();
11681         }
11682         if(this.handleMouseEvents){
11683             btn.on("mouseover", this.onMouseOver, this);
11684             btn.on("mouseout", this.onMouseOut, this);
11685             btn.on("mousedown", this.onMouseDown, this);
11686         }
11687         btn.on(this.clickEvent, this.onClick, this);
11688         //btn.on("mouseup", this.onMouseUp, this);
11689         if(this.hidden){
11690             this.hide();
11691         }
11692         if(this.disabled){
11693             this.disable();
11694         }
11695         Roo.ButtonToggleMgr.register(this);
11696         if(this.pressed){
11697             this.el.addClass("x-btn-pressed");
11698         }
11699         if(this.repeat){
11700             var repeater = new Roo.util.ClickRepeater(btn,
11701                 typeof this.repeat == "object" ? this.repeat : {}
11702             );
11703             repeater.on("click", this.onClick,  this);
11704         }
11705         
11706         this.fireEvent('render', this);
11707         
11708     },
11709     /**
11710      * Returns the button's underlying element
11711      * @return {Roo.Element} The element
11712      */
11713     getEl : function(){
11714         return this.el;  
11715     },
11716     
11717     /**
11718      * Destroys this Button and removes any listeners.
11719      */
11720     destroy : function(){
11721         Roo.ButtonToggleMgr.unregister(this);
11722         this.el.removeAllListeners();
11723         this.purgeListeners();
11724         this.el.remove();
11725     },
11726
11727     // private
11728     autoWidth : function(){
11729         if(this.el){
11730             this.el.setWidth("auto");
11731             if(Roo.isIE7 && Roo.isStrict){
11732                 var ib = this.el.child('button');
11733                 if(ib && ib.getWidth() > 20){
11734                     ib.clip();
11735                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11736                 }
11737             }
11738             if(this.minWidth){
11739                 if(this.hidden){
11740                     this.el.beginMeasure();
11741                 }
11742                 if(this.el.getWidth() < this.minWidth){
11743                     this.el.setWidth(this.minWidth);
11744                 }
11745                 if(this.hidden){
11746                     this.el.endMeasure();
11747                 }
11748             }
11749         }
11750     },
11751
11752     /**
11753      * Assigns this button's click handler
11754      * @param {Function} handler The function to call when the button is clicked
11755      * @param {Object} scope (optional) Scope for the function passed in
11756      */
11757     setHandler : function(handler, scope){
11758         this.handler = handler;
11759         this.scope = scope;  
11760     },
11761     
11762     /**
11763      * Sets this button's text
11764      * @param {String} text The button text
11765      */
11766     setText : function(text){
11767         this.text = text;
11768         if(this.el){
11769             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11770         }
11771         this.autoWidth();
11772     },
11773     
11774     /**
11775      * Gets the text for this button
11776      * @return {String} The button text
11777      */
11778     getText : function(){
11779         return this.text;  
11780     },
11781     
11782     /**
11783      * Show this button
11784      */
11785     show: function(){
11786         this.hidden = false;
11787         if(this.el){
11788             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11789         }
11790     },
11791     
11792     /**
11793      * Hide this button
11794      */
11795     hide: function(){
11796         this.hidden = true;
11797         if(this.el){
11798             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11799         }
11800     },
11801     
11802     /**
11803      * Convenience function for boolean show/hide
11804      * @param {Boolean} visible True to show, false to hide
11805      */
11806     setVisible: function(visible){
11807         if(visible) {
11808             this.show();
11809         }else{
11810             this.hide();
11811         }
11812     },
11813     
11814     /**
11815      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11816      * @param {Boolean} state (optional) Force a particular state
11817      */
11818     toggle : function(state){
11819         state = state === undefined ? !this.pressed : state;
11820         if(state != this.pressed){
11821             if(state){
11822                 this.el.addClass("x-btn-pressed");
11823                 this.pressed = true;
11824                 this.fireEvent("toggle", this, true);
11825             }else{
11826                 this.el.removeClass("x-btn-pressed");
11827                 this.pressed = false;
11828                 this.fireEvent("toggle", this, false);
11829             }
11830             if(this.toggleHandler){
11831                 this.toggleHandler.call(this.scope || this, this, state);
11832             }
11833         }
11834     },
11835     
11836     /**
11837      * Focus the button
11838      */
11839     focus : function(){
11840         this.el.child('button:first').focus();
11841     },
11842     
11843     /**
11844      * Disable this button
11845      */
11846     disable : function(){
11847         if(this.el){
11848             this.el.addClass("x-btn-disabled");
11849         }
11850         this.disabled = true;
11851     },
11852     
11853     /**
11854      * Enable this button
11855      */
11856     enable : function(){
11857         if(this.el){
11858             this.el.removeClass("x-btn-disabled");
11859         }
11860         this.disabled = false;
11861     },
11862
11863     /**
11864      * Convenience function for boolean enable/disable
11865      * @param {Boolean} enabled True to enable, false to disable
11866      */
11867     setDisabled : function(v){
11868         this[v !== true ? "enable" : "disable"]();
11869     },
11870
11871     // private
11872     onClick : function(e){
11873         if(e){
11874             e.preventDefault();
11875         }
11876         if(e.button != 0){
11877             return;
11878         }
11879         if(!this.disabled){
11880             if(this.enableToggle){
11881                 this.toggle();
11882             }
11883             if(this.menu && !this.menu.isVisible()){
11884                 this.menu.show(this.el, this.menuAlign);
11885             }
11886             this.fireEvent("click", this, e);
11887             if(this.handler){
11888                 this.el.removeClass("x-btn-over");
11889                 this.handler.call(this.scope || this, this, e);
11890             }
11891         }
11892     },
11893     // private
11894     onMouseOver : function(e){
11895         if(!this.disabled){
11896             this.el.addClass("x-btn-over");
11897             this.fireEvent('mouseover', this, e);
11898         }
11899     },
11900     // private
11901     onMouseOut : function(e){
11902         if(!e.within(this.el,  true)){
11903             this.el.removeClass("x-btn-over");
11904             this.fireEvent('mouseout', this, e);
11905         }
11906     },
11907     // private
11908     onFocus : function(e){
11909         if(!this.disabled){
11910             this.el.addClass("x-btn-focus");
11911         }
11912     },
11913     // private
11914     onBlur : function(e){
11915         this.el.removeClass("x-btn-focus");
11916     },
11917     // private
11918     onMouseDown : function(e){
11919         if(!this.disabled && e.button == 0){
11920             this.el.addClass("x-btn-click");
11921             Roo.get(document).on('mouseup', this.onMouseUp, this);
11922         }
11923     },
11924     // private
11925     onMouseUp : function(e){
11926         if(e.button == 0){
11927             this.el.removeClass("x-btn-click");
11928             Roo.get(document).un('mouseup', this.onMouseUp, this);
11929         }
11930     },
11931     // private
11932     onMenuShow : function(e){
11933         this.el.addClass("x-btn-menu-active");
11934     },
11935     // private
11936     onMenuHide : function(e){
11937         this.el.removeClass("x-btn-menu-active");
11938     }   
11939 });
11940
11941 // Private utility class used by Button
11942 Roo.ButtonToggleMgr = function(){
11943    var groups = {};
11944    
11945    function toggleGroup(btn, state){
11946        if(state){
11947            var g = groups[btn.toggleGroup];
11948            for(var i = 0, l = g.length; i < l; i++){
11949                if(g[i] != btn){
11950                    g[i].toggle(false);
11951                }
11952            }
11953        }
11954    }
11955    
11956    return {
11957        register : function(btn){
11958            if(!btn.toggleGroup){
11959                return;
11960            }
11961            var g = groups[btn.toggleGroup];
11962            if(!g){
11963                g = groups[btn.toggleGroup] = [];
11964            }
11965            g.push(btn);
11966            btn.on("toggle", toggleGroup);
11967        },
11968        
11969        unregister : function(btn){
11970            if(!btn.toggleGroup){
11971                return;
11972            }
11973            var g = groups[btn.toggleGroup];
11974            if(g){
11975                g.remove(btn);
11976                btn.un("toggle", toggleGroup);
11977            }
11978        }
11979    };
11980 }();/*
11981  * Based on:
11982  * Ext JS Library 1.1.1
11983  * Copyright(c) 2006-2007, Ext JS, LLC.
11984  *
11985  * Originally Released Under LGPL - original licence link has changed is not relivant.
11986  *
11987  * Fork - LGPL
11988  * <script type="text/javascript">
11989  */
11990  
11991 /**
11992  * @class Roo.SplitButton
11993  * @extends Roo.Button
11994  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
11995  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
11996  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
11997  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
11998  * @cfg {String} arrowTooltip The title attribute of the arrow
11999  * @constructor
12000  * Create a new menu button
12001  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12002  * @param {Object} config The config object
12003  */
12004 Roo.SplitButton = function(renderTo, config){
12005     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12006     /**
12007      * @event arrowclick
12008      * Fires when this button's arrow is clicked
12009      * @param {SplitButton} this
12010      * @param {EventObject} e The click event
12011      */
12012     this.addEvents({"arrowclick":true});
12013 };
12014
12015 Roo.extend(Roo.SplitButton, Roo.Button, {
12016     render : function(renderTo){
12017         // this is one sweet looking template!
12018         var tpl = new Roo.Template(
12019             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12020             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12021             '<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>',
12022             "</tbody></table></td><td>",
12023             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12024             '<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>',
12025             "</tbody></table></td></tr></table>"
12026         );
12027         var btn = tpl.append(renderTo, [this.text, this.type], true);
12028         var btnEl = btn.child("button");
12029         if(this.cls){
12030             btn.addClass(this.cls);
12031         }
12032         if(this.icon){
12033             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12034         }
12035         if(this.iconCls){
12036             btnEl.addClass(this.iconCls);
12037             if(!this.cls){
12038                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12039             }
12040         }
12041         this.el = btn;
12042         if(this.handleMouseEvents){
12043             btn.on("mouseover", this.onMouseOver, this);
12044             btn.on("mouseout", this.onMouseOut, this);
12045             btn.on("mousedown", this.onMouseDown, this);
12046             btn.on("mouseup", this.onMouseUp, this);
12047         }
12048         btn.on(this.clickEvent, this.onClick, this);
12049         if(this.tooltip){
12050             if(typeof this.tooltip == 'object'){
12051                 Roo.QuickTips.tips(Roo.apply({
12052                       target: btnEl.id
12053                 }, this.tooltip));
12054             } else {
12055                 btnEl.dom[this.tooltipType] = this.tooltip;
12056             }
12057         }
12058         if(this.arrowTooltip){
12059             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12060         }
12061         if(this.hidden){
12062             this.hide();
12063         }
12064         if(this.disabled){
12065             this.disable();
12066         }
12067         if(this.pressed){
12068             this.el.addClass("x-btn-pressed");
12069         }
12070         if(Roo.isIE && !Roo.isIE7){
12071             this.autoWidth.defer(1, this);
12072         }else{
12073             this.autoWidth();
12074         }
12075         if(this.menu){
12076             this.menu.on("show", this.onMenuShow, this);
12077             this.menu.on("hide", this.onMenuHide, this);
12078         }
12079         this.fireEvent('render', this);
12080     },
12081
12082     // private
12083     autoWidth : function(){
12084         if(this.el){
12085             var tbl = this.el.child("table:first");
12086             var tbl2 = this.el.child("table:last");
12087             this.el.setWidth("auto");
12088             tbl.setWidth("auto");
12089             if(Roo.isIE7 && Roo.isStrict){
12090                 var ib = this.el.child('button:first');
12091                 if(ib && ib.getWidth() > 20){
12092                     ib.clip();
12093                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12094                 }
12095             }
12096             if(this.minWidth){
12097                 if(this.hidden){
12098                     this.el.beginMeasure();
12099                 }
12100                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12101                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12102                 }
12103                 if(this.hidden){
12104                     this.el.endMeasure();
12105                 }
12106             }
12107             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12108         } 
12109     },
12110     /**
12111      * Sets this button's click handler
12112      * @param {Function} handler The function to call when the button is clicked
12113      * @param {Object} scope (optional) Scope for the function passed above
12114      */
12115     setHandler : function(handler, scope){
12116         this.handler = handler;
12117         this.scope = scope;  
12118     },
12119     
12120     /**
12121      * Sets this button's arrow click handler
12122      * @param {Function} handler The function to call when the arrow is clicked
12123      * @param {Object} scope (optional) Scope for the function passed above
12124      */
12125     setArrowHandler : function(handler, scope){
12126         this.arrowHandler = handler;
12127         this.scope = scope;  
12128     },
12129     
12130     /**
12131      * Focus the button
12132      */
12133     focus : function(){
12134         if(this.el){
12135             this.el.child("button:first").focus();
12136         }
12137     },
12138
12139     // private
12140     onClick : function(e){
12141         e.preventDefault();
12142         if(!this.disabled){
12143             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12144                 if(this.menu && !this.menu.isVisible()){
12145                     this.menu.show(this.el, this.menuAlign);
12146                 }
12147                 this.fireEvent("arrowclick", this, e);
12148                 if(this.arrowHandler){
12149                     this.arrowHandler.call(this.scope || this, this, e);
12150                 }
12151             }else{
12152                 this.fireEvent("click", this, e);
12153                 if(this.handler){
12154                     this.handler.call(this.scope || this, this, e);
12155                 }
12156             }
12157         }
12158     },
12159     // private
12160     onMouseDown : function(e){
12161         if(!this.disabled){
12162             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12163         }
12164     },
12165     // private
12166     onMouseUp : function(e){
12167         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12168     }   
12169 });
12170
12171
12172 // backwards compat
12173 Roo.MenuButton = Roo.SplitButton;/*
12174  * Based on:
12175  * Ext JS Library 1.1.1
12176  * Copyright(c) 2006-2007, Ext JS, LLC.
12177  *
12178  * Originally Released Under LGPL - original licence link has changed is not relivant.
12179  *
12180  * Fork - LGPL
12181  * <script type="text/javascript">
12182  */
12183
12184 /**
12185  * @class Roo.Toolbar
12186  * Basic Toolbar class.
12187  * @constructor
12188  * Creates a new Toolbar
12189  * @param {Object} config The config object
12190  */ 
12191 Roo.Toolbar = function(container, buttons, config)
12192 {
12193     /// old consturctor format still supported..
12194     if(container instanceof Array){ // omit the container for later rendering
12195         buttons = container;
12196         config = buttons;
12197         container = null;
12198     }
12199     if (typeof(container) == 'object' && container.xtype) {
12200         config = container;
12201         container = config.container;
12202         buttons = config.buttons; // not really - use items!!
12203     }
12204     var xitems = [];
12205     if (config && config.items) {
12206         xitems = config.items;
12207         delete config.items;
12208     }
12209     Roo.apply(this, config);
12210     this.buttons = buttons;
12211     
12212     if(container){
12213         this.render(container);
12214     }
12215     Roo.each(xitems, function(b) {
12216         this.add(b);
12217     }, this);
12218     
12219 };
12220
12221 Roo.Toolbar.prototype = {
12222     /**
12223      * @cfg {Roo.data.Store} items
12224      * array of button configs or elements to add
12225      */
12226     
12227     /**
12228      * @cfg {String/HTMLElement/Element} container
12229      * The id or element that will contain the toolbar
12230      */
12231     // private
12232     render : function(ct){
12233         this.el = Roo.get(ct);
12234         if(this.cls){
12235             this.el.addClass(this.cls);
12236         }
12237         // using a table allows for vertical alignment
12238         // 100% width is needed by Safari...
12239         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12240         this.tr = this.el.child("tr", true);
12241         var autoId = 0;
12242         this.items = new Roo.util.MixedCollection(false, function(o){
12243             return o.id || ("item" + (++autoId));
12244         });
12245         if(this.buttons){
12246             this.add.apply(this, this.buttons);
12247             delete this.buttons;
12248         }
12249     },
12250
12251     /**
12252      * Adds element(s) to the toolbar -- this function takes a variable number of 
12253      * arguments of mixed type and adds them to the toolbar.
12254      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12255      * <ul>
12256      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12257      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12258      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12259      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12260      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12261      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12262      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12263      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12264      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12265      * </ul>
12266      * @param {Mixed} arg2
12267      * @param {Mixed} etc.
12268      */
12269     add : function(){
12270         var a = arguments, l = a.length;
12271         for(var i = 0; i < l; i++){
12272             this._add(a[i]);
12273         }
12274     },
12275     // private..
12276     _add : function(el) {
12277         
12278         if (el.xtype) {
12279             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12280         }
12281         
12282         if (el.applyTo){ // some kind of form field
12283             return this.addField(el);
12284         } 
12285         if (el.render){ // some kind of Toolbar.Item
12286             return this.addItem(el);
12287         }
12288         if (typeof el == "string"){ // string
12289             if(el == "separator" || el == "-"){
12290                 return this.addSeparator();
12291             }
12292             if (el == " "){
12293                 return this.addSpacer();
12294             }
12295             if(el == "->"){
12296                 return this.addFill();
12297             }
12298             return this.addText(el);
12299             
12300         }
12301         if(el.tagName){ // element
12302             return this.addElement(el);
12303         }
12304         if(typeof el == "object"){ // must be button config?
12305             return this.addButton(el);
12306         }
12307         // and now what?!?!
12308         return false;
12309         
12310     },
12311     
12312     /**
12313      * Add an Xtype element
12314      * @param {Object} xtype Xtype Object
12315      * @return {Object} created Object
12316      */
12317     addxtype : function(e){
12318         return this.add(e);  
12319     },
12320     
12321     /**
12322      * Returns the Element for this toolbar.
12323      * @return {Roo.Element}
12324      */
12325     getEl : function(){
12326         return this.el;  
12327     },
12328     
12329     /**
12330      * Adds a separator
12331      * @return {Roo.Toolbar.Item} The separator item
12332      */
12333     addSeparator : function(){
12334         return this.addItem(new Roo.Toolbar.Separator());
12335     },
12336
12337     /**
12338      * Adds a spacer element
12339      * @return {Roo.Toolbar.Spacer} The spacer item
12340      */
12341     addSpacer : function(){
12342         return this.addItem(new Roo.Toolbar.Spacer());
12343     },
12344
12345     /**
12346      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12347      * @return {Roo.Toolbar.Fill} The fill item
12348      */
12349     addFill : function(){
12350         return this.addItem(new Roo.Toolbar.Fill());
12351     },
12352
12353     /**
12354      * Adds any standard HTML element to the toolbar
12355      * @param {String/HTMLElement/Element} el The element or id of the element to add
12356      * @return {Roo.Toolbar.Item} The element's item
12357      */
12358     addElement : function(el){
12359         return this.addItem(new Roo.Toolbar.Item(el));
12360     },
12361     /**
12362      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12363      * @type Roo.util.MixedCollection  
12364      */
12365     items : false,
12366      
12367     /**
12368      * Adds any Toolbar.Item or subclass
12369      * @param {Roo.Toolbar.Item} item
12370      * @return {Roo.Toolbar.Item} The item
12371      */
12372     addItem : function(item){
12373         var td = this.nextBlock();
12374         item.render(td);
12375         this.items.add(item);
12376         return item;
12377     },
12378     
12379     /**
12380      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12381      * @param {Object/Array} config A button config or array of configs
12382      * @return {Roo.Toolbar.Button/Array}
12383      */
12384     addButton : function(config){
12385         if(config instanceof Array){
12386             var buttons = [];
12387             for(var i = 0, len = config.length; i < len; i++) {
12388                 buttons.push(this.addButton(config[i]));
12389             }
12390             return buttons;
12391         }
12392         var b = config;
12393         if(!(config instanceof Roo.Toolbar.Button)){
12394             b = config.split ?
12395                 new Roo.Toolbar.SplitButton(config) :
12396                 new Roo.Toolbar.Button(config);
12397         }
12398         var td = this.nextBlock();
12399         b.render(td);
12400         this.items.add(b);
12401         return b;
12402     },
12403     
12404     /**
12405      * Adds text to the toolbar
12406      * @param {String} text The text to add
12407      * @return {Roo.Toolbar.Item} The element's item
12408      */
12409     addText : function(text){
12410         return this.addItem(new Roo.Toolbar.TextItem(text));
12411     },
12412     
12413     /**
12414      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12415      * @param {Number} index The index where the item is to be inserted
12416      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12417      * @return {Roo.Toolbar.Button/Item}
12418      */
12419     insertButton : function(index, item){
12420         if(item instanceof Array){
12421             var buttons = [];
12422             for(var i = 0, len = item.length; i < len; i++) {
12423                buttons.push(this.insertButton(index + i, item[i]));
12424             }
12425             return buttons;
12426         }
12427         if (!(item instanceof Roo.Toolbar.Button)){
12428            item = new Roo.Toolbar.Button(item);
12429         }
12430         var td = document.createElement("td");
12431         this.tr.insertBefore(td, this.tr.childNodes[index]);
12432         item.render(td);
12433         this.items.insert(index, item);
12434         return item;
12435     },
12436     
12437     /**
12438      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12439      * @param {Object} config
12440      * @return {Roo.Toolbar.Item} The element's item
12441      */
12442     addDom : function(config, returnEl){
12443         var td = this.nextBlock();
12444         Roo.DomHelper.overwrite(td, config);
12445         var ti = new Roo.Toolbar.Item(td.firstChild);
12446         ti.render(td);
12447         this.items.add(ti);
12448         return ti;
12449     },
12450
12451     /**
12452      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12453      * @type Roo.util.MixedCollection  
12454      */
12455     fields : false,
12456     
12457     /**
12458      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12459      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12460      * @param {Roo.form.Field} field
12461      * @return {Roo.ToolbarItem}
12462      */
12463      
12464       
12465     addField : function(field) {
12466         if (!this.fields) {
12467             var autoId = 0;
12468             this.fields = new Roo.util.MixedCollection(false, function(o){
12469                 return o.id || ("item" + (++autoId));
12470             });
12471
12472         }
12473         
12474         var td = this.nextBlock();
12475         field.render(td);
12476         var ti = new Roo.Toolbar.Item(td.firstChild);
12477         ti.render(td);
12478         this.items.add(ti);
12479         this.fields.add(field);
12480         return ti;
12481     },
12482     /**
12483      * Hide the toolbar
12484      * @method hide
12485      */
12486      
12487       
12488     hide : function()
12489     {
12490         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12491         this.el.child('div').hide();
12492     },
12493     /**
12494      * Show the toolbar
12495      * @method show
12496      */
12497     show : function()
12498     {
12499         this.el.child('div').show();
12500     },
12501       
12502     // private
12503     nextBlock : function(){
12504         var td = document.createElement("td");
12505         this.tr.appendChild(td);
12506         return td;
12507     },
12508
12509     // private
12510     destroy : function(){
12511         if(this.items){ // rendered?
12512             Roo.destroy.apply(Roo, this.items.items);
12513         }
12514         if(this.fields){ // rendered?
12515             Roo.destroy.apply(Roo, this.fields.items);
12516         }
12517         Roo.Element.uncache(this.el, this.tr);
12518     }
12519 };
12520
12521 /**
12522  * @class Roo.Toolbar.Item
12523  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12524  * @constructor
12525  * Creates a new Item
12526  * @param {HTMLElement} el 
12527  */
12528 Roo.Toolbar.Item = function(el){
12529     this.el = Roo.getDom(el);
12530     this.id = Roo.id(this.el);
12531     this.hidden = false;
12532 };
12533
12534 Roo.Toolbar.Item.prototype = {
12535     
12536     /**
12537      * Get this item's HTML Element
12538      * @return {HTMLElement}
12539      */
12540     getEl : function(){
12541        return this.el;  
12542     },
12543
12544     // private
12545     render : function(td){
12546         this.td = td;
12547         td.appendChild(this.el);
12548     },
12549     
12550     /**
12551      * Removes and destroys this item.
12552      */
12553     destroy : function(){
12554         this.td.parentNode.removeChild(this.td);
12555     },
12556     
12557     /**
12558      * Shows this item.
12559      */
12560     show: function(){
12561         this.hidden = false;
12562         this.td.style.display = "";
12563     },
12564     
12565     /**
12566      * Hides this item.
12567      */
12568     hide: function(){
12569         this.hidden = true;
12570         this.td.style.display = "none";
12571     },
12572     
12573     /**
12574      * Convenience function for boolean show/hide.
12575      * @param {Boolean} visible true to show/false to hide
12576      */
12577     setVisible: function(visible){
12578         if(visible) {
12579             this.show();
12580         }else{
12581             this.hide();
12582         }
12583     },
12584     
12585     /**
12586      * Try to focus this item.
12587      */
12588     focus : function(){
12589         Roo.fly(this.el).focus();
12590     },
12591     
12592     /**
12593      * Disables this item.
12594      */
12595     disable : function(){
12596         Roo.fly(this.td).addClass("x-item-disabled");
12597         this.disabled = true;
12598         this.el.disabled = true;
12599     },
12600     
12601     /**
12602      * Enables this item.
12603      */
12604     enable : function(){
12605         Roo.fly(this.td).removeClass("x-item-disabled");
12606         this.disabled = false;
12607         this.el.disabled = false;
12608     }
12609 };
12610
12611
12612 /**
12613  * @class Roo.Toolbar.Separator
12614  * @extends Roo.Toolbar.Item
12615  * A simple toolbar separator class
12616  * @constructor
12617  * Creates a new Separator
12618  */
12619 Roo.Toolbar.Separator = function(){
12620     var s = document.createElement("span");
12621     s.className = "ytb-sep";
12622     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12623 };
12624 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12625     enable:Roo.emptyFn,
12626     disable:Roo.emptyFn,
12627     focus:Roo.emptyFn
12628 });
12629
12630 /**
12631  * @class Roo.Toolbar.Spacer
12632  * @extends Roo.Toolbar.Item
12633  * A simple element that adds extra horizontal space to a toolbar.
12634  * @constructor
12635  * Creates a new Spacer
12636  */
12637 Roo.Toolbar.Spacer = function(){
12638     var s = document.createElement("div");
12639     s.className = "ytb-spacer";
12640     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12641 };
12642 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12643     enable:Roo.emptyFn,
12644     disable:Roo.emptyFn,
12645     focus:Roo.emptyFn
12646 });
12647
12648 /**
12649  * @class Roo.Toolbar.Fill
12650  * @extends Roo.Toolbar.Spacer
12651  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12652  * @constructor
12653  * Creates a new Spacer
12654  */
12655 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12656     // private
12657     render : function(td){
12658         td.style.width = '100%';
12659         Roo.Toolbar.Fill.superclass.render.call(this, td);
12660     }
12661 });
12662
12663 /**
12664  * @class Roo.Toolbar.TextItem
12665  * @extends Roo.Toolbar.Item
12666  * A simple class that renders text directly into a toolbar.
12667  * @constructor
12668  * Creates a new TextItem
12669  * @param {String} text
12670  */
12671 Roo.Toolbar.TextItem = function(text){
12672     if (typeof(text) == 'object') {
12673         text = text.text;
12674     }
12675     var s = document.createElement("span");
12676     s.className = "ytb-text";
12677     s.innerHTML = text;
12678     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12679 };
12680 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12681     enable:Roo.emptyFn,
12682     disable:Roo.emptyFn,
12683     focus:Roo.emptyFn
12684 });
12685
12686 /**
12687  * @class Roo.Toolbar.Button
12688  * @extends Roo.Button
12689  * A button that renders into a toolbar.
12690  * @constructor
12691  * Creates a new Button
12692  * @param {Object} config A standard {@link Roo.Button} config object
12693  */
12694 Roo.Toolbar.Button = function(config){
12695     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12696 };
12697 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12698     render : function(td){
12699         this.td = td;
12700         Roo.Toolbar.Button.superclass.render.call(this, td);
12701     },
12702     
12703     /**
12704      * Removes and destroys this button
12705      */
12706     destroy : function(){
12707         Roo.Toolbar.Button.superclass.destroy.call(this);
12708         this.td.parentNode.removeChild(this.td);
12709     },
12710     
12711     /**
12712      * Shows this button
12713      */
12714     show: function(){
12715         this.hidden = false;
12716         this.td.style.display = "";
12717     },
12718     
12719     /**
12720      * Hides this button
12721      */
12722     hide: function(){
12723         this.hidden = true;
12724         this.td.style.display = "none";
12725     },
12726
12727     /**
12728      * Disables this item
12729      */
12730     disable : function(){
12731         Roo.fly(this.td).addClass("x-item-disabled");
12732         this.disabled = true;
12733     },
12734
12735     /**
12736      * Enables this item
12737      */
12738     enable : function(){
12739         Roo.fly(this.td).removeClass("x-item-disabled");
12740         this.disabled = false;
12741     }
12742 });
12743 // backwards compat
12744 Roo.ToolbarButton = Roo.Toolbar.Button;
12745
12746 /**
12747  * @class Roo.Toolbar.SplitButton
12748  * @extends Roo.SplitButton
12749  * A menu button that renders into a toolbar.
12750  * @constructor
12751  * Creates a new SplitButton
12752  * @param {Object} config A standard {@link Roo.SplitButton} config object
12753  */
12754 Roo.Toolbar.SplitButton = function(config){
12755     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12756 };
12757 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12758     render : function(td){
12759         this.td = td;
12760         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12761     },
12762     
12763     /**
12764      * Removes and destroys this button
12765      */
12766     destroy : function(){
12767         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12768         this.td.parentNode.removeChild(this.td);
12769     },
12770     
12771     /**
12772      * Shows this button
12773      */
12774     show: function(){
12775         this.hidden = false;
12776         this.td.style.display = "";
12777     },
12778     
12779     /**
12780      * Hides this button
12781      */
12782     hide: function(){
12783         this.hidden = true;
12784         this.td.style.display = "none";
12785     }
12786 });
12787
12788 // backwards compat
12789 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12790  * Based on:
12791  * Ext JS Library 1.1.1
12792  * Copyright(c) 2006-2007, Ext JS, LLC.
12793  *
12794  * Originally Released Under LGPL - original licence link has changed is not relivant.
12795  *
12796  * Fork - LGPL
12797  * <script type="text/javascript">
12798  */
12799  
12800 /**
12801  * @class Roo.PagingToolbar
12802  * @extends Roo.Toolbar
12803  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12804  * @constructor
12805  * Create a new PagingToolbar
12806  * @param {Object} config The config object
12807  */
12808 Roo.PagingToolbar = function(el, ds, config)
12809 {
12810     // old args format still supported... - xtype is prefered..
12811     if (typeof(el) == 'object' && el.xtype) {
12812         // created from xtype...
12813         config = el;
12814         ds = el.dataSource;
12815         el = config.container;
12816     }
12817     var items = [];
12818     if (config.items) {
12819         items = config.items;
12820         config.items = [];
12821     }
12822     
12823     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12824     this.ds = ds;
12825     this.cursor = 0;
12826     this.renderButtons(this.el);
12827     this.bind(ds);
12828     
12829     // supprot items array.
12830    
12831     Roo.each(items, function(e) {
12832         this.add(Roo.factory(e));
12833     },this);
12834     
12835 };
12836
12837 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12838     /**
12839      * @cfg {Roo.data.Store} dataSource
12840      * The underlying data store providing the paged data
12841      */
12842     /**
12843      * @cfg {String/HTMLElement/Element} container
12844      * container The id or element that will contain the toolbar
12845      */
12846     /**
12847      * @cfg {Boolean} displayInfo
12848      * True to display the displayMsg (defaults to false)
12849      */
12850     /**
12851      * @cfg {Number} pageSize
12852      * The number of records to display per page (defaults to 20)
12853      */
12854     pageSize: 20,
12855     /**
12856      * @cfg {String} displayMsg
12857      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12858      */
12859     displayMsg : 'Displaying {0} - {1} of {2}',
12860     /**
12861      * @cfg {String} emptyMsg
12862      * The message to display when no records are found (defaults to "No data to display")
12863      */
12864     emptyMsg : 'No data to display',
12865     /**
12866      * Customizable piece of the default paging text (defaults to "Page")
12867      * @type String
12868      */
12869     beforePageText : "Page",
12870     /**
12871      * Customizable piece of the default paging text (defaults to "of %0")
12872      * @type String
12873      */
12874     afterPageText : "of {0}",
12875     /**
12876      * Customizable piece of the default paging text (defaults to "First Page")
12877      * @type String
12878      */
12879     firstText : "First Page",
12880     /**
12881      * Customizable piece of the default paging text (defaults to "Previous Page")
12882      * @type String
12883      */
12884     prevText : "Previous Page",
12885     /**
12886      * Customizable piece of the default paging text (defaults to "Next Page")
12887      * @type String
12888      */
12889     nextText : "Next Page",
12890     /**
12891      * Customizable piece of the default paging text (defaults to "Last Page")
12892      * @type String
12893      */
12894     lastText : "Last Page",
12895     /**
12896      * Customizable piece of the default paging text (defaults to "Refresh")
12897      * @type String
12898      */
12899     refreshText : "Refresh",
12900
12901     // private
12902     renderButtons : function(el){
12903         Roo.PagingToolbar.superclass.render.call(this, el);
12904         this.first = this.addButton({
12905             tooltip: this.firstText,
12906             cls: "x-btn-icon x-grid-page-first",
12907             disabled: true,
12908             handler: this.onClick.createDelegate(this, ["first"])
12909         });
12910         this.prev = this.addButton({
12911             tooltip: this.prevText,
12912             cls: "x-btn-icon x-grid-page-prev",
12913             disabled: true,
12914             handler: this.onClick.createDelegate(this, ["prev"])
12915         });
12916         //this.addSeparator();
12917         this.add(this.beforePageText);
12918         this.field = Roo.get(this.addDom({
12919            tag: "input",
12920            type: "text",
12921            size: "3",
12922            value: "1",
12923            cls: "x-grid-page-number"
12924         }).el);
12925         this.field.on("keydown", this.onPagingKeydown, this);
12926         this.field.on("focus", function(){this.dom.select();});
12927         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12928         this.field.setHeight(18);
12929         //this.addSeparator();
12930         this.next = this.addButton({
12931             tooltip: this.nextText,
12932             cls: "x-btn-icon x-grid-page-next",
12933             disabled: true,
12934             handler: this.onClick.createDelegate(this, ["next"])
12935         });
12936         this.last = this.addButton({
12937             tooltip: this.lastText,
12938             cls: "x-btn-icon x-grid-page-last",
12939             disabled: true,
12940             handler: this.onClick.createDelegate(this, ["last"])
12941         });
12942         //this.addSeparator();
12943         this.loading = this.addButton({
12944             tooltip: this.refreshText,
12945             cls: "x-btn-icon x-grid-loading",
12946             handler: this.onClick.createDelegate(this, ["refresh"])
12947         });
12948
12949         if(this.displayInfo){
12950             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12951         }
12952     },
12953
12954     // private
12955     updateInfo : function(){
12956         if(this.displayEl){
12957             var count = this.ds.getCount();
12958             var msg = count == 0 ?
12959                 this.emptyMsg :
12960                 String.format(
12961                     this.displayMsg,
12962                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12963                 );
12964             this.displayEl.update(msg);
12965         }
12966     },
12967
12968     // private
12969     onLoad : function(ds, r, o){
12970        this.cursor = o.params ? o.params.start : 0;
12971        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12972
12973        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12974        this.field.dom.value = ap;
12975        this.first.setDisabled(ap == 1);
12976        this.prev.setDisabled(ap == 1);
12977        this.next.setDisabled(ap == ps);
12978        this.last.setDisabled(ap == ps);
12979        this.loading.enable();
12980        this.updateInfo();
12981     },
12982
12983     // private
12984     getPageData : function(){
12985         var total = this.ds.getTotalCount();
12986         return {
12987             total : total,
12988             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12989             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12990         };
12991     },
12992
12993     // private
12994     onLoadError : function(){
12995         this.loading.enable();
12996     },
12997
12998     // private
12999     onPagingKeydown : function(e){
13000         var k = e.getKey();
13001         var d = this.getPageData();
13002         if(k == e.RETURN){
13003             var v = this.field.dom.value, pageNum;
13004             if(!v || isNaN(pageNum = parseInt(v, 10))){
13005                 this.field.dom.value = d.activePage;
13006                 return;
13007             }
13008             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13009             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13010             e.stopEvent();
13011         }
13012         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))
13013         {
13014           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13015           this.field.dom.value = pageNum;
13016           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13017           e.stopEvent();
13018         }
13019         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13020         {
13021           var v = this.field.dom.value, pageNum; 
13022           var increment = (e.shiftKey) ? 10 : 1;
13023           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13024             increment *= -1;
13025           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13026             this.field.dom.value = d.activePage;
13027             return;
13028           }
13029           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13030           {
13031             this.field.dom.value = parseInt(v, 10) + increment;
13032             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13033             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13034           }
13035           e.stopEvent();
13036         }
13037     },
13038
13039     // private
13040     beforeLoad : function(){
13041         if(this.loading){
13042             this.loading.disable();
13043         }
13044     },
13045
13046     // private
13047     onClick : function(which){
13048         var ds = this.ds;
13049         switch(which){
13050             case "first":
13051                 ds.load({params:{start: 0, limit: this.pageSize}});
13052             break;
13053             case "prev":
13054                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13055             break;
13056             case "next":
13057                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13058             break;
13059             case "last":
13060                 var total = ds.getTotalCount();
13061                 var extra = total % this.pageSize;
13062                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13063                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13064             break;
13065             case "refresh":
13066                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13067             break;
13068         }
13069     },
13070
13071     /**
13072      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13073      * @param {Roo.data.Store} store The data store to unbind
13074      */
13075     unbind : function(ds){
13076         ds.un("beforeload", this.beforeLoad, this);
13077         ds.un("load", this.onLoad, this);
13078         ds.un("loadexception", this.onLoadError, this);
13079         ds.un("remove", this.updateInfo, this);
13080         ds.un("add", this.updateInfo, this);
13081         this.ds = undefined;
13082     },
13083
13084     /**
13085      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13086      * @param {Roo.data.Store} store The data store to bind
13087      */
13088     bind : function(ds){
13089         ds.on("beforeload", this.beforeLoad, this);
13090         ds.on("load", this.onLoad, this);
13091         ds.on("loadexception", this.onLoadError, this);
13092         ds.on("remove", this.updateInfo, this);
13093         ds.on("add", this.updateInfo, this);
13094         this.ds = ds;
13095     }
13096 });/*
13097  * Based on:
13098  * Ext JS Library 1.1.1
13099  * Copyright(c) 2006-2007, Ext JS, LLC.
13100  *
13101  * Originally Released Under LGPL - original licence link has changed is not relivant.
13102  *
13103  * Fork - LGPL
13104  * <script type="text/javascript">
13105  */
13106
13107 /**
13108  * @class Roo.Resizable
13109  * @extends Roo.util.Observable
13110  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13111  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13112  * 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
13113  * the element will be wrapped for you automatically.</p>
13114  * <p>Here is the list of valid resize handles:</p>
13115  * <pre>
13116 Value   Description
13117 ------  -------------------
13118  'n'     north
13119  's'     south
13120  'e'     east
13121  'w'     west
13122  'nw'    northwest
13123  'sw'    southwest
13124  'se'    southeast
13125  'ne'    northeast
13126  'hd'    horizontal drag
13127  'all'   all
13128 </pre>
13129  * <p>Here's an example showing the creation of a typical Resizable:</p>
13130  * <pre><code>
13131 var resizer = new Roo.Resizable("element-id", {
13132     handles: 'all',
13133     minWidth: 200,
13134     minHeight: 100,
13135     maxWidth: 500,
13136     maxHeight: 400,
13137     pinned: true
13138 });
13139 resizer.on("resize", myHandler);
13140 </code></pre>
13141  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13142  * resizer.east.setDisplayed(false);</p>
13143  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13144  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13145  * resize operation's new size (defaults to [0, 0])
13146  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13147  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13148  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13149  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13150  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13151  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13152  * @cfg {Number} width The width of the element in pixels (defaults to null)
13153  * @cfg {Number} height The height of the element in pixels (defaults to null)
13154  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13155  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13156  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13157  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13158  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13159  * in favor of the handles config option (defaults to false)
13160  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13161  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13162  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13163  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13164  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13165  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13166  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13167  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13168  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13169  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13170  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13171  * @constructor
13172  * Create a new resizable component
13173  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13174  * @param {Object} config configuration options
13175   */
13176 Roo.Resizable = function(el, config)
13177 {
13178     this.el = Roo.get(el);
13179
13180     if(config && config.wrap){
13181         config.resizeChild = this.el;
13182         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13183         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13184         this.el.setStyle("overflow", "hidden");
13185         this.el.setPositioning(config.resizeChild.getPositioning());
13186         config.resizeChild.clearPositioning();
13187         if(!config.width || !config.height){
13188             var csize = config.resizeChild.getSize();
13189             this.el.setSize(csize.width, csize.height);
13190         }
13191         if(config.pinned && !config.adjustments){
13192             config.adjustments = "auto";
13193         }
13194     }
13195
13196     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13197     this.proxy.unselectable();
13198     this.proxy.enableDisplayMode('block');
13199
13200     Roo.apply(this, config);
13201
13202     if(this.pinned){
13203         this.disableTrackOver = true;
13204         this.el.addClass("x-resizable-pinned");
13205     }
13206     // if the element isn't positioned, make it relative
13207     var position = this.el.getStyle("position");
13208     if(position != "absolute" && position != "fixed"){
13209         this.el.setStyle("position", "relative");
13210     }
13211     if(!this.handles){ // no handles passed, must be legacy style
13212         this.handles = 's,e,se';
13213         if(this.multiDirectional){
13214             this.handles += ',n,w';
13215         }
13216     }
13217     if(this.handles == "all"){
13218         this.handles = "n s e w ne nw se sw";
13219     }
13220     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13221     var ps = Roo.Resizable.positions;
13222     for(var i = 0, len = hs.length; i < len; i++){
13223         if(hs[i] && ps[hs[i]]){
13224             var pos = ps[hs[i]];
13225             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13226         }
13227     }
13228     // legacy
13229     this.corner = this.southeast;
13230     
13231     // updateBox = the box can move..
13232     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13233         this.updateBox = true;
13234     }
13235
13236     this.activeHandle = null;
13237
13238     if(this.resizeChild){
13239         if(typeof this.resizeChild == "boolean"){
13240             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13241         }else{
13242             this.resizeChild = Roo.get(this.resizeChild, true);
13243         }
13244     }
13245     
13246     if(this.adjustments == "auto"){
13247         var rc = this.resizeChild;
13248         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13249         if(rc && (hw || hn)){
13250             rc.position("relative");
13251             rc.setLeft(hw ? hw.el.getWidth() : 0);
13252             rc.setTop(hn ? hn.el.getHeight() : 0);
13253         }
13254         this.adjustments = [
13255             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13256             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13257         ];
13258     }
13259
13260     if(this.draggable){
13261         this.dd = this.dynamic ?
13262             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13263         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13264     }
13265
13266     // public events
13267     this.addEvents({
13268         /**
13269          * @event beforeresize
13270          * Fired before resize is allowed. Set enabled to false to cancel resize.
13271          * @param {Roo.Resizable} this
13272          * @param {Roo.EventObject} e The mousedown event
13273          */
13274         "beforeresize" : true,
13275         /**
13276          * @event resize
13277          * Fired after a resize.
13278          * @param {Roo.Resizable} this
13279          * @param {Number} width The new width
13280          * @param {Number} height The new height
13281          * @param {Roo.EventObject} e The mouseup event
13282          */
13283         "resize" : true
13284     });
13285
13286     if(this.width !== null && this.height !== null){
13287         this.resizeTo(this.width, this.height);
13288     }else{
13289         this.updateChildSize();
13290     }
13291     if(Roo.isIE){
13292         this.el.dom.style.zoom = 1;
13293     }
13294     Roo.Resizable.superclass.constructor.call(this);
13295 };
13296
13297 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13298         resizeChild : false,
13299         adjustments : [0, 0],
13300         minWidth : 5,
13301         minHeight : 5,
13302         maxWidth : 10000,
13303         maxHeight : 10000,
13304         enabled : true,
13305         animate : false,
13306         duration : .35,
13307         dynamic : false,
13308         handles : false,
13309         multiDirectional : false,
13310         disableTrackOver : false,
13311         easing : 'easeOutStrong',
13312         widthIncrement : 0,
13313         heightIncrement : 0,
13314         pinned : false,
13315         width : null,
13316         height : null,
13317         preserveRatio : false,
13318         transparent: false,
13319         minX: 0,
13320         minY: 0,
13321         draggable: false,
13322
13323         /**
13324          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13325          */
13326         constrainTo: undefined,
13327         /**
13328          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13329          */
13330         resizeRegion: undefined,
13331
13332
13333     /**
13334      * Perform a manual resize
13335      * @param {Number} width
13336      * @param {Number} height
13337      */
13338     resizeTo : function(width, height){
13339         this.el.setSize(width, height);
13340         this.updateChildSize();
13341         this.fireEvent("resize", this, width, height, null);
13342     },
13343
13344     // private
13345     startSizing : function(e, handle){
13346         this.fireEvent("beforeresize", this, e);
13347         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13348
13349             if(!this.overlay){
13350                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13351                 this.overlay.unselectable();
13352                 this.overlay.enableDisplayMode("block");
13353                 this.overlay.on("mousemove", this.onMouseMove, this);
13354                 this.overlay.on("mouseup", this.onMouseUp, this);
13355             }
13356             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13357
13358             this.resizing = true;
13359             this.startBox = this.el.getBox();
13360             this.startPoint = e.getXY();
13361             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13362                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13363
13364             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13365             this.overlay.show();
13366
13367             if(this.constrainTo) {
13368                 var ct = Roo.get(this.constrainTo);
13369                 this.resizeRegion = ct.getRegion().adjust(
13370                     ct.getFrameWidth('t'),
13371                     ct.getFrameWidth('l'),
13372                     -ct.getFrameWidth('b'),
13373                     -ct.getFrameWidth('r')
13374                 );
13375             }
13376
13377             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13378             this.proxy.show();
13379             this.proxy.setBox(this.startBox);
13380             if(!this.dynamic){
13381                 this.proxy.setStyle('visibility', 'visible');
13382             }
13383         }
13384     },
13385
13386     // private
13387     onMouseDown : function(handle, e){
13388         if(this.enabled){
13389             e.stopEvent();
13390             this.activeHandle = handle;
13391             this.startSizing(e, handle);
13392         }
13393     },
13394
13395     // private
13396     onMouseUp : function(e){
13397         var size = this.resizeElement();
13398         this.resizing = false;
13399         this.handleOut();
13400         this.overlay.hide();
13401         this.proxy.hide();
13402         this.fireEvent("resize", this, size.width, size.height, e);
13403     },
13404
13405     // private
13406     updateChildSize : function(){
13407         if(this.resizeChild){
13408             var el = this.el;
13409             var child = this.resizeChild;
13410             var adj = this.adjustments;
13411             if(el.dom.offsetWidth){
13412                 var b = el.getSize(true);
13413                 child.setSize(b.width+adj[0], b.height+adj[1]);
13414             }
13415             // Second call here for IE
13416             // The first call enables instant resizing and
13417             // the second call corrects scroll bars if they
13418             // exist
13419             if(Roo.isIE){
13420                 setTimeout(function(){
13421                     if(el.dom.offsetWidth){
13422                         var b = el.getSize(true);
13423                         child.setSize(b.width+adj[0], b.height+adj[1]);
13424                     }
13425                 }, 10);
13426             }
13427         }
13428     },
13429
13430     // private
13431     snap : function(value, inc, min){
13432         if(!inc || !value) return value;
13433         var newValue = value;
13434         var m = value % inc;
13435         if(m > 0){
13436             if(m > (inc/2)){
13437                 newValue = value + (inc-m);
13438             }else{
13439                 newValue = value - m;
13440             }
13441         }
13442         return Math.max(min, newValue);
13443     },
13444
13445     // private
13446     resizeElement : function(){
13447         var box = this.proxy.getBox();
13448         if(this.updateBox){
13449             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13450         }else{
13451             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13452         }
13453         this.updateChildSize();
13454         if(!this.dynamic){
13455             this.proxy.hide();
13456         }
13457         return box;
13458     },
13459
13460     // private
13461     constrain : function(v, diff, m, mx){
13462         if(v - diff < m){
13463             diff = v - m;
13464         }else if(v - diff > mx){
13465             diff = mx - v;
13466         }
13467         return diff;
13468     },
13469
13470     // private
13471     onMouseMove : function(e){
13472         if(this.enabled){
13473             try{// try catch so if something goes wrong the user doesn't get hung
13474
13475             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13476                 return;
13477             }
13478
13479             //var curXY = this.startPoint;
13480             var curSize = this.curSize || this.startBox;
13481             var x = this.startBox.x, y = this.startBox.y;
13482             var ox = x, oy = y;
13483             var w = curSize.width, h = curSize.height;
13484             var ow = w, oh = h;
13485             var mw = this.minWidth, mh = this.minHeight;
13486             var mxw = this.maxWidth, mxh = this.maxHeight;
13487             var wi = this.widthIncrement;
13488             var hi = this.heightIncrement;
13489
13490             var eventXY = e.getXY();
13491             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13492             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13493
13494             var pos = this.activeHandle.position;
13495
13496             switch(pos){
13497                 case "east":
13498                     w += diffX;
13499                     w = Math.min(Math.max(mw, w), mxw);
13500                     break;
13501              
13502                 case "south":
13503                     h += diffY;
13504                     h = Math.min(Math.max(mh, h), mxh);
13505                     break;
13506                 case "southeast":
13507                     w += diffX;
13508                     h += diffY;
13509                     w = Math.min(Math.max(mw, w), mxw);
13510                     h = Math.min(Math.max(mh, h), mxh);
13511                     break;
13512                 case "north":
13513                     diffY = this.constrain(h, diffY, mh, mxh);
13514                     y += diffY;
13515                     h -= diffY;
13516                     break;
13517                 case "hdrag":
13518                     
13519                     if (wi) {
13520                         var adiffX = Math.abs(diffX);
13521                         var sub = (adiffX % wi); // how much 
13522                         if (sub > (wi/2)) { // far enough to snap
13523                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13524                         } else {
13525                             // remove difference.. 
13526                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13527                         }
13528                     }
13529                     x += diffX;
13530                     x = Math.max(this.minX, x);
13531                     break;
13532                 case "west":
13533                     diffX = this.constrain(w, diffX, mw, mxw);
13534                     x += diffX;
13535                     w -= diffX;
13536                     break;
13537                 case "northeast":
13538                     w += diffX;
13539                     w = Math.min(Math.max(mw, w), mxw);
13540                     diffY = this.constrain(h, diffY, mh, mxh);
13541                     y += diffY;
13542                     h -= diffY;
13543                     break;
13544                 case "northwest":
13545                     diffX = this.constrain(w, diffX, mw, mxw);
13546                     diffY = this.constrain(h, diffY, mh, mxh);
13547                     y += diffY;
13548                     h -= diffY;
13549                     x += diffX;
13550                     w -= diffX;
13551                     break;
13552                case "southwest":
13553                     diffX = this.constrain(w, diffX, mw, mxw);
13554                     h += diffY;
13555                     h = Math.min(Math.max(mh, h), mxh);
13556                     x += diffX;
13557                     w -= diffX;
13558                     break;
13559             }
13560
13561             var sw = this.snap(w, wi, mw);
13562             var sh = this.snap(h, hi, mh);
13563             if(sw != w || sh != h){
13564                 switch(pos){
13565                     case "northeast":
13566                         y -= sh - h;
13567                     break;
13568                     case "north":
13569                         y -= sh - h;
13570                         break;
13571                     case "southwest":
13572                         x -= sw - w;
13573                     break;
13574                     case "west":
13575                         x -= sw - w;
13576                         break;
13577                     case "northwest":
13578                         x -= sw - w;
13579                         y -= sh - h;
13580                     break;
13581                 }
13582                 w = sw;
13583                 h = sh;
13584             }
13585
13586             if(this.preserveRatio){
13587                 switch(pos){
13588                     case "southeast":
13589                     case "east":
13590                         h = oh * (w/ow);
13591                         h = Math.min(Math.max(mh, h), mxh);
13592                         w = ow * (h/oh);
13593                        break;
13594                     case "south":
13595                         w = ow * (h/oh);
13596                         w = Math.min(Math.max(mw, w), mxw);
13597                         h = oh * (w/ow);
13598                         break;
13599                     case "northeast":
13600                         w = ow * (h/oh);
13601                         w = Math.min(Math.max(mw, w), mxw);
13602                         h = oh * (w/ow);
13603                     break;
13604                     case "north":
13605                         var tw = w;
13606                         w = ow * (h/oh);
13607                         w = Math.min(Math.max(mw, w), mxw);
13608                         h = oh * (w/ow);
13609                         x += (tw - w) / 2;
13610                         break;
13611                     case "southwest":
13612                         h = oh * (w/ow);
13613                         h = Math.min(Math.max(mh, h), mxh);
13614                         var tw = w;
13615                         w = ow * (h/oh);
13616                         x += tw - w;
13617                         break;
13618                     case "west":
13619                         var th = h;
13620                         h = oh * (w/ow);
13621                         h = Math.min(Math.max(mh, h), mxh);
13622                         y += (th - h) / 2;
13623                         var tw = w;
13624                         w = ow * (h/oh);
13625                         x += tw - w;
13626                        break;
13627                     case "northwest":
13628                         var tw = w;
13629                         var th = h;
13630                         h = oh * (w/ow);
13631                         h = Math.min(Math.max(mh, h), mxh);
13632                         w = ow * (h/oh);
13633                         y += th - h;
13634                         x += tw - w;
13635                        break;
13636
13637                 }
13638             }
13639             if (pos == 'hdrag') {
13640                 w = ow;
13641             }
13642             this.proxy.setBounds(x, y, w, h);
13643             if(this.dynamic){
13644                 this.resizeElement();
13645             }
13646             }catch(e){}
13647         }
13648     },
13649
13650     // private
13651     handleOver : function(){
13652         if(this.enabled){
13653             this.el.addClass("x-resizable-over");
13654         }
13655     },
13656
13657     // private
13658     handleOut : function(){
13659         if(!this.resizing){
13660             this.el.removeClass("x-resizable-over");
13661         }
13662     },
13663
13664     /**
13665      * Returns the element this component is bound to.
13666      * @return {Roo.Element}
13667      */
13668     getEl : function(){
13669         return this.el;
13670     },
13671
13672     /**
13673      * Returns the resizeChild element (or null).
13674      * @return {Roo.Element}
13675      */
13676     getResizeChild : function(){
13677         return this.resizeChild;
13678     },
13679
13680     /**
13681      * Destroys this resizable. If the element was wrapped and
13682      * removeEl is not true then the element remains.
13683      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13684      */
13685     destroy : function(removeEl){
13686         this.proxy.remove();
13687         if(this.overlay){
13688             this.overlay.removeAllListeners();
13689             this.overlay.remove();
13690         }
13691         var ps = Roo.Resizable.positions;
13692         for(var k in ps){
13693             if(typeof ps[k] != "function" && this[ps[k]]){
13694                 var h = this[ps[k]];
13695                 h.el.removeAllListeners();
13696                 h.el.remove();
13697             }
13698         }
13699         if(removeEl){
13700             this.el.update("");
13701             this.el.remove();
13702         }
13703     }
13704 });
13705
13706 // private
13707 // hash to map config positions to true positions
13708 Roo.Resizable.positions = {
13709     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13710     hd: "hdrag"
13711 };
13712
13713 // private
13714 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13715     if(!this.tpl){
13716         // only initialize the template if resizable is used
13717         var tpl = Roo.DomHelper.createTemplate(
13718             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13719         );
13720         tpl.compile();
13721         Roo.Resizable.Handle.prototype.tpl = tpl;
13722     }
13723     this.position = pos;
13724     this.rz = rz;
13725     // show north drag fro topdra
13726     var handlepos = pos == 'hdrag' ? 'north' : pos;
13727     
13728     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13729     if (pos == 'hdrag') {
13730         this.el.setStyle('cursor', 'pointer');
13731     }
13732     this.el.unselectable();
13733     if(transparent){
13734         this.el.setOpacity(0);
13735     }
13736     this.el.on("mousedown", this.onMouseDown, this);
13737     if(!disableTrackOver){
13738         this.el.on("mouseover", this.onMouseOver, this);
13739         this.el.on("mouseout", this.onMouseOut, this);
13740     }
13741 };
13742
13743 // private
13744 Roo.Resizable.Handle.prototype = {
13745     afterResize : function(rz){
13746         // do nothing
13747     },
13748     // private
13749     onMouseDown : function(e){
13750         this.rz.onMouseDown(this, e);
13751     },
13752     // private
13753     onMouseOver : function(e){
13754         this.rz.handleOver(this, e);
13755     },
13756     // private
13757     onMouseOut : function(e){
13758         this.rz.handleOut(this, e);
13759     }
13760 };/*
13761  * Based on:
13762  * Ext JS Library 1.1.1
13763  * Copyright(c) 2006-2007, Ext JS, LLC.
13764  *
13765  * Originally Released Under LGPL - original licence link has changed is not relivant.
13766  *
13767  * Fork - LGPL
13768  * <script type="text/javascript">
13769  */
13770
13771 /**
13772  * @class Roo.Editor
13773  * @extends Roo.Component
13774  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13775  * @constructor
13776  * Create a new Editor
13777  * @param {Roo.form.Field} field The Field object (or descendant)
13778  * @param {Object} config The config object
13779  */
13780 Roo.Editor = function(field, config){
13781     Roo.Editor.superclass.constructor.call(this, config);
13782     this.field = field;
13783     this.addEvents({
13784         /**
13785              * @event beforestartedit
13786              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13787              * false from the handler of this event.
13788              * @param {Editor} this
13789              * @param {Roo.Element} boundEl The underlying element bound to this editor
13790              * @param {Mixed} value The field value being set
13791              */
13792         "beforestartedit" : true,
13793         /**
13794              * @event startedit
13795              * Fires when this editor is displayed
13796              * @param {Roo.Element} boundEl The underlying element bound to this editor
13797              * @param {Mixed} value The starting field value
13798              */
13799         "startedit" : true,
13800         /**
13801              * @event beforecomplete
13802              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13803              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13804              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13805              * event will not fire since no edit actually occurred.
13806              * @param {Editor} this
13807              * @param {Mixed} value The current field value
13808              * @param {Mixed} startValue The original field value
13809              */
13810         "beforecomplete" : true,
13811         /**
13812              * @event complete
13813              * Fires after editing is complete and any changed value has been written to the underlying field.
13814              * @param {Editor} this
13815              * @param {Mixed} value The current field value
13816              * @param {Mixed} startValue The original field value
13817              */
13818         "complete" : true,
13819         /**
13820          * @event specialkey
13821          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13822          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13823          * @param {Roo.form.Field} this
13824          * @param {Roo.EventObject} e The event object
13825          */
13826         "specialkey" : true
13827     });
13828 };
13829
13830 Roo.extend(Roo.Editor, Roo.Component, {
13831     /**
13832      * @cfg {Boolean/String} autosize
13833      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13834      * or "height" to adopt the height only (defaults to false)
13835      */
13836     /**
13837      * @cfg {Boolean} revertInvalid
13838      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13839      * validation fails (defaults to true)
13840      */
13841     /**
13842      * @cfg {Boolean} ignoreNoChange
13843      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13844      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13845      * will never be ignored.
13846      */
13847     /**
13848      * @cfg {Boolean} hideEl
13849      * False to keep the bound element visible while the editor is displayed (defaults to true)
13850      */
13851     /**
13852      * @cfg {Mixed} value
13853      * The data value of the underlying field (defaults to "")
13854      */
13855     value : "",
13856     /**
13857      * @cfg {String} alignment
13858      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13859      */
13860     alignment: "c-c?",
13861     /**
13862      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13863      * for bottom-right shadow (defaults to "frame")
13864      */
13865     shadow : "frame",
13866     /**
13867      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13868      */
13869     constrain : false,
13870     /**
13871      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13872      */
13873     completeOnEnter : false,
13874     /**
13875      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13876      */
13877     cancelOnEsc : false,
13878     /**
13879      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13880      */
13881     updateEl : false,
13882
13883     // private
13884     onRender : function(ct, position){
13885         this.el = new Roo.Layer({
13886             shadow: this.shadow,
13887             cls: "x-editor",
13888             parentEl : ct,
13889             shim : this.shim,
13890             shadowOffset:4,
13891             id: this.id,
13892             constrain: this.constrain
13893         });
13894         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13895         if(this.field.msgTarget != 'title'){
13896             this.field.msgTarget = 'qtip';
13897         }
13898         this.field.render(this.el);
13899         if(Roo.isGecko){
13900             this.field.el.dom.setAttribute('autocomplete', 'off');
13901         }
13902         this.field.on("specialkey", this.onSpecialKey, this);
13903         if(this.swallowKeys){
13904             this.field.el.swallowEvent(['keydown','keypress']);
13905         }
13906         this.field.show();
13907         this.field.on("blur", this.onBlur, this);
13908         if(this.field.grow){
13909             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13910         }
13911     },
13912
13913     onSpecialKey : function(field, e){
13914         //Roo.log('editor onSpecialKey');
13915         if(this.completeOnEnter && e.getKey() == e.ENTER){
13916             e.stopEvent();
13917             this.completeEdit();
13918         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13919             this.cancelEdit();
13920         }else{
13921             this.fireEvent('specialkey', field, e);
13922         }
13923     },
13924
13925     /**
13926      * Starts the editing process and shows the editor.
13927      * @param {String/HTMLElement/Element} el The element to edit
13928      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13929       * to the innerHTML of el.
13930      */
13931     startEdit : function(el, value){
13932         if(this.editing){
13933             this.completeEdit();
13934         }
13935         this.boundEl = Roo.get(el);
13936         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13937         if(!this.rendered){
13938             this.render(this.parentEl || document.body);
13939         }
13940         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13941             return;
13942         }
13943         this.startValue = v;
13944         this.field.setValue(v);
13945         if(this.autoSize){
13946             var sz = this.boundEl.getSize();
13947             switch(this.autoSize){
13948                 case "width":
13949                 this.setSize(sz.width,  "");
13950                 break;
13951                 case "height":
13952                 this.setSize("",  sz.height);
13953                 break;
13954                 default:
13955                 this.setSize(sz.width,  sz.height);
13956             }
13957         }
13958         this.el.alignTo(this.boundEl, this.alignment);
13959         this.editing = true;
13960         if(Roo.QuickTips){
13961             Roo.QuickTips.disable();
13962         }
13963         this.show();
13964     },
13965
13966     /**
13967      * Sets the height and width of this editor.
13968      * @param {Number} width The new width
13969      * @param {Number} height The new height
13970      */
13971     setSize : function(w, h){
13972         this.field.setSize(w, h);
13973         if(this.el){
13974             this.el.sync();
13975         }
13976     },
13977
13978     /**
13979      * Realigns the editor to the bound field based on the current alignment config value.
13980      */
13981     realign : function(){
13982         this.el.alignTo(this.boundEl, this.alignment);
13983     },
13984
13985     /**
13986      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13987      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13988      */
13989     completeEdit : function(remainVisible){
13990         if(!this.editing){
13991             return;
13992         }
13993         var v = this.getValue();
13994         if(this.revertInvalid !== false && !this.field.isValid()){
13995             v = this.startValue;
13996             this.cancelEdit(true);
13997         }
13998         if(String(v) === String(this.startValue) && this.ignoreNoChange){
13999             this.editing = false;
14000             this.hide();
14001             return;
14002         }
14003         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14004             this.editing = false;
14005             if(this.updateEl && this.boundEl){
14006                 this.boundEl.update(v);
14007             }
14008             if(remainVisible !== true){
14009                 this.hide();
14010             }
14011             this.fireEvent("complete", this, v, this.startValue);
14012         }
14013     },
14014
14015     // private
14016     onShow : function(){
14017         this.el.show();
14018         if(this.hideEl !== false){
14019             this.boundEl.hide();
14020         }
14021         this.field.show();
14022         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14023             this.fixIEFocus = true;
14024             this.deferredFocus.defer(50, this);
14025         }else{
14026             this.field.focus();
14027         }
14028         this.fireEvent("startedit", this.boundEl, this.startValue);
14029     },
14030
14031     deferredFocus : function(){
14032         if(this.editing){
14033             this.field.focus();
14034         }
14035     },
14036
14037     /**
14038      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14039      * reverted to the original starting value.
14040      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14041      * cancel (defaults to false)
14042      */
14043     cancelEdit : function(remainVisible){
14044         if(this.editing){
14045             this.setValue(this.startValue);
14046             if(remainVisible !== true){
14047                 this.hide();
14048             }
14049         }
14050     },
14051
14052     // private
14053     onBlur : function(){
14054         if(this.allowBlur !== true && this.editing){
14055             this.completeEdit();
14056         }
14057     },
14058
14059     // private
14060     onHide : function(){
14061         if(this.editing){
14062             this.completeEdit();
14063             return;
14064         }
14065         this.field.blur();
14066         if(this.field.collapse){
14067             this.field.collapse();
14068         }
14069         this.el.hide();
14070         if(this.hideEl !== false){
14071             this.boundEl.show();
14072         }
14073         if(Roo.QuickTips){
14074             Roo.QuickTips.enable();
14075         }
14076     },
14077
14078     /**
14079      * Sets the data value of the editor
14080      * @param {Mixed} value Any valid value supported by the underlying field
14081      */
14082     setValue : function(v){
14083         this.field.setValue(v);
14084     },
14085
14086     /**
14087      * Gets the data value of the editor
14088      * @return {Mixed} The data value
14089      */
14090     getValue : function(){
14091         return this.field.getValue();
14092     }
14093 });/*
14094  * Based on:
14095  * Ext JS Library 1.1.1
14096  * Copyright(c) 2006-2007, Ext JS, LLC.
14097  *
14098  * Originally Released Under LGPL - original licence link has changed is not relivant.
14099  *
14100  * Fork - LGPL
14101  * <script type="text/javascript">
14102  */
14103  
14104 /**
14105  * @class Roo.BasicDialog
14106  * @extends Roo.util.Observable
14107  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14108  * <pre><code>
14109 var dlg = new Roo.BasicDialog("my-dlg", {
14110     height: 200,
14111     width: 300,
14112     minHeight: 100,
14113     minWidth: 150,
14114     modal: true,
14115     proxyDrag: true,
14116     shadow: true
14117 });
14118 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14119 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14120 dlg.addButton('Cancel', dlg.hide, dlg);
14121 dlg.show();
14122 </code></pre>
14123   <b>A Dialog should always be a direct child of the body element.</b>
14124  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14125  * @cfg {String} title Default text to display in the title bar (defaults to null)
14126  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14127  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14128  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14129  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14130  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14131  * (defaults to null with no animation)
14132  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14133  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14134  * property for valid values (defaults to 'all')
14135  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14136  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14137  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14138  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14139  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14140  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14141  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14142  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14143  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14144  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14145  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14146  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14147  * draggable = true (defaults to false)
14148  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14149  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14150  * shadow (defaults to false)
14151  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14152  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14153  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14154  * @cfg {Array} buttons Array of buttons
14155  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14156  * @constructor
14157  * Create a new BasicDialog.
14158  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14159  * @param {Object} config Configuration options
14160  */
14161 Roo.BasicDialog = function(el, config){
14162     this.el = Roo.get(el);
14163     var dh = Roo.DomHelper;
14164     if(!this.el && config && config.autoCreate){
14165         if(typeof config.autoCreate == "object"){
14166             if(!config.autoCreate.id){
14167                 config.autoCreate.id = el;
14168             }
14169             this.el = dh.append(document.body,
14170                         config.autoCreate, true);
14171         }else{
14172             this.el = dh.append(document.body,
14173                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14174         }
14175     }
14176     el = this.el;
14177     el.setDisplayed(true);
14178     el.hide = this.hideAction;
14179     this.id = el.id;
14180     el.addClass("x-dlg");
14181
14182     Roo.apply(this, config);
14183
14184     this.proxy = el.createProxy("x-dlg-proxy");
14185     this.proxy.hide = this.hideAction;
14186     this.proxy.setOpacity(.5);
14187     this.proxy.hide();
14188
14189     if(config.width){
14190         el.setWidth(config.width);
14191     }
14192     if(config.height){
14193         el.setHeight(config.height);
14194     }
14195     this.size = el.getSize();
14196     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14197         this.xy = [config.x,config.y];
14198     }else{
14199         this.xy = el.getCenterXY(true);
14200     }
14201     /** The header element @type Roo.Element */
14202     this.header = el.child("> .x-dlg-hd");
14203     /** The body element @type Roo.Element */
14204     this.body = el.child("> .x-dlg-bd");
14205     /** The footer element @type Roo.Element */
14206     this.footer = el.child("> .x-dlg-ft");
14207
14208     if(!this.header){
14209         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14210     }
14211     if(!this.body){
14212         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14213     }
14214
14215     this.header.unselectable();
14216     if(this.title){
14217         this.header.update(this.title);
14218     }
14219     // this element allows the dialog to be focused for keyboard event
14220     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14221     this.focusEl.swallowEvent("click", true);
14222
14223     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14224
14225     // wrap the body and footer for special rendering
14226     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14227     if(this.footer){
14228         this.bwrap.dom.appendChild(this.footer.dom);
14229     }
14230
14231     this.bg = this.el.createChild({
14232         tag: "div", cls:"x-dlg-bg",
14233         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14234     });
14235     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14236
14237
14238     if(this.autoScroll !== false && !this.autoTabs){
14239         this.body.setStyle("overflow", "auto");
14240     }
14241
14242     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14243
14244     if(this.closable !== false){
14245         this.el.addClass("x-dlg-closable");
14246         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14247         this.close.on("click", this.closeClick, this);
14248         this.close.addClassOnOver("x-dlg-close-over");
14249     }
14250     if(this.collapsible !== false){
14251         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14252         this.collapseBtn.on("click", this.collapseClick, this);
14253         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14254         this.header.on("dblclick", this.collapseClick, this);
14255     }
14256     if(this.resizable !== false){
14257         this.el.addClass("x-dlg-resizable");
14258         this.resizer = new Roo.Resizable(el, {
14259             minWidth: this.minWidth || 80,
14260             minHeight:this.minHeight || 80,
14261             handles: this.resizeHandles || "all",
14262             pinned: true
14263         });
14264         this.resizer.on("beforeresize", this.beforeResize, this);
14265         this.resizer.on("resize", this.onResize, this);
14266     }
14267     if(this.draggable !== false){
14268         el.addClass("x-dlg-draggable");
14269         if (!this.proxyDrag) {
14270             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14271         }
14272         else {
14273             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14274         }
14275         dd.setHandleElId(this.header.id);
14276         dd.endDrag = this.endMove.createDelegate(this);
14277         dd.startDrag = this.startMove.createDelegate(this);
14278         dd.onDrag = this.onDrag.createDelegate(this);
14279         dd.scroll = false;
14280         this.dd = dd;
14281     }
14282     if(this.modal){
14283         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14284         this.mask.enableDisplayMode("block");
14285         this.mask.hide();
14286         this.el.addClass("x-dlg-modal");
14287     }
14288     if(this.shadow){
14289         this.shadow = new Roo.Shadow({
14290             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14291             offset : this.shadowOffset
14292         });
14293     }else{
14294         this.shadowOffset = 0;
14295     }
14296     if(Roo.useShims && this.shim !== false){
14297         this.shim = this.el.createShim();
14298         this.shim.hide = this.hideAction;
14299         this.shim.hide();
14300     }else{
14301         this.shim = false;
14302     }
14303     if(this.autoTabs){
14304         this.initTabs();
14305     }
14306     if (this.buttons) { 
14307         var bts= this.buttons;
14308         this.buttons = [];
14309         Roo.each(bts, function(b) {
14310             this.addButton(b);
14311         }, this);
14312     }
14313     
14314     
14315     this.addEvents({
14316         /**
14317          * @event keydown
14318          * Fires when a key is pressed
14319          * @param {Roo.BasicDialog} this
14320          * @param {Roo.EventObject} e
14321          */
14322         "keydown" : true,
14323         /**
14324          * @event move
14325          * Fires when this dialog is moved by the user.
14326          * @param {Roo.BasicDialog} this
14327          * @param {Number} x The new page X
14328          * @param {Number} y The new page Y
14329          */
14330         "move" : true,
14331         /**
14332          * @event resize
14333          * Fires when this dialog is resized by the user.
14334          * @param {Roo.BasicDialog} this
14335          * @param {Number} width The new width
14336          * @param {Number} height The new height
14337          */
14338         "resize" : true,
14339         /**
14340          * @event beforehide
14341          * Fires before this dialog is hidden.
14342          * @param {Roo.BasicDialog} this
14343          */
14344         "beforehide" : true,
14345         /**
14346          * @event hide
14347          * Fires when this dialog is hidden.
14348          * @param {Roo.BasicDialog} this
14349          */
14350         "hide" : true,
14351         /**
14352          * @event beforeshow
14353          * Fires before this dialog is shown.
14354          * @param {Roo.BasicDialog} this
14355          */
14356         "beforeshow" : true,
14357         /**
14358          * @event show
14359          * Fires when this dialog is shown.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "show" : true
14363     });
14364     el.on("keydown", this.onKeyDown, this);
14365     el.on("mousedown", this.toFront, this);
14366     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14367     this.el.hide();
14368     Roo.DialogManager.register(this);
14369     Roo.BasicDialog.superclass.constructor.call(this);
14370 };
14371
14372 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14373     shadowOffset: Roo.isIE ? 6 : 5,
14374     minHeight: 80,
14375     minWidth: 200,
14376     minButtonWidth: 75,
14377     defaultButton: null,
14378     buttonAlign: "right",
14379     tabTag: 'div',
14380     firstShow: true,
14381
14382     /**
14383      * Sets the dialog title text
14384      * @param {String} text The title text to display
14385      * @return {Roo.BasicDialog} this
14386      */
14387     setTitle : function(text){
14388         this.header.update(text);
14389         return this;
14390     },
14391
14392     // private
14393     closeClick : function(){
14394         this.hide();
14395     },
14396
14397     // private
14398     collapseClick : function(){
14399         this[this.collapsed ? "expand" : "collapse"]();
14400     },
14401
14402     /**
14403      * Collapses the dialog to its minimized state (only the title bar is visible).
14404      * Equivalent to the user clicking the collapse dialog button.
14405      */
14406     collapse : function(){
14407         if(!this.collapsed){
14408             this.collapsed = true;
14409             this.el.addClass("x-dlg-collapsed");
14410             this.restoreHeight = this.el.getHeight();
14411             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14412         }
14413     },
14414
14415     /**
14416      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14417      * clicking the expand dialog button.
14418      */
14419     expand : function(){
14420         if(this.collapsed){
14421             this.collapsed = false;
14422             this.el.removeClass("x-dlg-collapsed");
14423             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14424         }
14425     },
14426
14427     /**
14428      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14429      * @return {Roo.TabPanel} The tabs component
14430      */
14431     initTabs : function(){
14432         var tabs = this.getTabs();
14433         while(tabs.getTab(0)){
14434             tabs.removeTab(0);
14435         }
14436         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14437             var dom = el.dom;
14438             tabs.addTab(Roo.id(dom), dom.title);
14439             dom.title = "";
14440         });
14441         tabs.activate(0);
14442         return tabs;
14443     },
14444
14445     // private
14446     beforeResize : function(){
14447         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14448     },
14449
14450     // private
14451     onResize : function(){
14452         this.refreshSize();
14453         this.syncBodyHeight();
14454         this.adjustAssets();
14455         this.focus();
14456         this.fireEvent("resize", this, this.size.width, this.size.height);
14457     },
14458
14459     // private
14460     onKeyDown : function(e){
14461         if(this.isVisible()){
14462             this.fireEvent("keydown", this, e);
14463         }
14464     },
14465
14466     /**
14467      * Resizes the dialog.
14468      * @param {Number} width
14469      * @param {Number} height
14470      * @return {Roo.BasicDialog} this
14471      */
14472     resizeTo : function(width, height){
14473         this.el.setSize(width, height);
14474         this.size = {width: width, height: height};
14475         this.syncBodyHeight();
14476         if(this.fixedcenter){
14477             this.center();
14478         }
14479         if(this.isVisible()){
14480             this.constrainXY();
14481             this.adjustAssets();
14482         }
14483         this.fireEvent("resize", this, width, height);
14484         return this;
14485     },
14486
14487
14488     /**
14489      * Resizes the dialog to fit the specified content size.
14490      * @param {Number} width
14491      * @param {Number} height
14492      * @return {Roo.BasicDialog} this
14493      */
14494     setContentSize : function(w, h){
14495         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14496         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14497         //if(!this.el.isBorderBox()){
14498             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14499             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14500         //}
14501         if(this.tabs){
14502             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14503             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14504         }
14505         this.resizeTo(w, h);
14506         return this;
14507     },
14508
14509     /**
14510      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14511      * executed in response to a particular key being pressed while the dialog is active.
14512      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14513      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14514      * @param {Function} fn The function to call
14515      * @param {Object} scope (optional) The scope of the function
14516      * @return {Roo.BasicDialog} this
14517      */
14518     addKeyListener : function(key, fn, scope){
14519         var keyCode, shift, ctrl, alt;
14520         if(typeof key == "object" && !(key instanceof Array)){
14521             keyCode = key["key"];
14522             shift = key["shift"];
14523             ctrl = key["ctrl"];
14524             alt = key["alt"];
14525         }else{
14526             keyCode = key;
14527         }
14528         var handler = function(dlg, e){
14529             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14530                 var k = e.getKey();
14531                 if(keyCode instanceof Array){
14532                     for(var i = 0, len = keyCode.length; i < len; i++){
14533                         if(keyCode[i] == k){
14534                           fn.call(scope || window, dlg, k, e);
14535                           return;
14536                         }
14537                     }
14538                 }else{
14539                     if(k == keyCode){
14540                         fn.call(scope || window, dlg, k, e);
14541                     }
14542                 }
14543             }
14544         };
14545         this.on("keydown", handler);
14546         return this;
14547     },
14548
14549     /**
14550      * Returns the TabPanel component (creates it if it doesn't exist).
14551      * Note: If you wish to simply check for the existence of tabs without creating them,
14552      * check for a null 'tabs' property.
14553      * @return {Roo.TabPanel} The tabs component
14554      */
14555     getTabs : function(){
14556         if(!this.tabs){
14557             this.el.addClass("x-dlg-auto-tabs");
14558             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14559             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14560         }
14561         return this.tabs;
14562     },
14563
14564     /**
14565      * Adds a button to the footer section of the dialog.
14566      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14567      * object or a valid Roo.DomHelper element config
14568      * @param {Function} handler The function called when the button is clicked
14569      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14570      * @return {Roo.Button} The new button
14571      */
14572     addButton : function(config, handler, scope){
14573         var dh = Roo.DomHelper;
14574         if(!this.footer){
14575             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14576         }
14577         if(!this.btnContainer){
14578             var tb = this.footer.createChild({
14579
14580                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14581                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14582             }, null, true);
14583             this.btnContainer = tb.firstChild.firstChild.firstChild;
14584         }
14585         var bconfig = {
14586             handler: handler,
14587             scope: scope,
14588             minWidth: this.minButtonWidth,
14589             hideParent:true
14590         };
14591         if(typeof config == "string"){
14592             bconfig.text = config;
14593         }else{
14594             if(config.tag){
14595                 bconfig.dhconfig = config;
14596             }else{
14597                 Roo.apply(bconfig, config);
14598             }
14599         }
14600         var fc = false;
14601         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14602             bconfig.position = Math.max(0, bconfig.position);
14603             fc = this.btnContainer.childNodes[bconfig.position];
14604         }
14605          
14606         var btn = new Roo.Button(
14607             fc ? 
14608                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14609                 : this.btnContainer.appendChild(document.createElement("td")),
14610             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14611             bconfig
14612         );
14613         this.syncBodyHeight();
14614         if(!this.buttons){
14615             /**
14616              * Array of all the buttons that have been added to this dialog via addButton
14617              * @type Array
14618              */
14619             this.buttons = [];
14620         }
14621         this.buttons.push(btn);
14622         return btn;
14623     },
14624
14625     /**
14626      * Sets the default button to be focused when the dialog is displayed.
14627      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14628      * @return {Roo.BasicDialog} this
14629      */
14630     setDefaultButton : function(btn){
14631         this.defaultButton = btn;
14632         return this;
14633     },
14634
14635     // private
14636     getHeaderFooterHeight : function(safe){
14637         var height = 0;
14638         if(this.header){
14639            height += this.header.getHeight();
14640         }
14641         if(this.footer){
14642            var fm = this.footer.getMargins();
14643             height += (this.footer.getHeight()+fm.top+fm.bottom);
14644         }
14645         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14646         height += this.centerBg.getPadding("tb");
14647         return height;
14648     },
14649
14650     // private
14651     syncBodyHeight : function(){
14652         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14653         var height = this.size.height - this.getHeaderFooterHeight(false);
14654         bd.setHeight(height-bd.getMargins("tb"));
14655         var hh = this.header.getHeight();
14656         var h = this.size.height-hh;
14657         cb.setHeight(h);
14658         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14659         bw.setHeight(h-cb.getPadding("tb"));
14660         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14661         bd.setWidth(bw.getWidth(true));
14662         if(this.tabs){
14663             this.tabs.syncHeight();
14664             if(Roo.isIE){
14665                 this.tabs.el.repaint();
14666             }
14667         }
14668     },
14669
14670     /**
14671      * Restores the previous state of the dialog if Roo.state is configured.
14672      * @return {Roo.BasicDialog} this
14673      */
14674     restoreState : function(){
14675         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14676         if(box && box.width){
14677             this.xy = [box.x, box.y];
14678             this.resizeTo(box.width, box.height);
14679         }
14680         return this;
14681     },
14682
14683     // private
14684     beforeShow : function(){
14685         this.expand();
14686         if(this.fixedcenter){
14687             this.xy = this.el.getCenterXY(true);
14688         }
14689         if(this.modal){
14690             Roo.get(document.body).addClass("x-body-masked");
14691             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14692             this.mask.show();
14693         }
14694         this.constrainXY();
14695     },
14696
14697     // private
14698     animShow : function(){
14699         var b = Roo.get(this.animateTarget).getBox();
14700         this.proxy.setSize(b.width, b.height);
14701         this.proxy.setLocation(b.x, b.y);
14702         this.proxy.show();
14703         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14704                     true, .35, this.showEl.createDelegate(this));
14705     },
14706
14707     /**
14708      * Shows the dialog.
14709      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14710      * @return {Roo.BasicDialog} this
14711      */
14712     show : function(animateTarget){
14713         if (this.fireEvent("beforeshow", this) === false){
14714             return;
14715         }
14716         if(this.syncHeightBeforeShow){
14717             this.syncBodyHeight();
14718         }else if(this.firstShow){
14719             this.firstShow = false;
14720             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14721         }
14722         this.animateTarget = animateTarget || this.animateTarget;
14723         if(!this.el.isVisible()){
14724             this.beforeShow();
14725             if(this.animateTarget && Roo.get(this.animateTarget)){
14726                 this.animShow();
14727             }else{
14728                 this.showEl();
14729             }
14730         }
14731         return this;
14732     },
14733
14734     // private
14735     showEl : function(){
14736         this.proxy.hide();
14737         this.el.setXY(this.xy);
14738         this.el.show();
14739         this.adjustAssets(true);
14740         this.toFront();
14741         this.focus();
14742         // IE peekaboo bug - fix found by Dave Fenwick
14743         if(Roo.isIE){
14744             this.el.repaint();
14745         }
14746         this.fireEvent("show", this);
14747     },
14748
14749     /**
14750      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14751      * dialog itself will receive focus.
14752      */
14753     focus : function(){
14754         if(this.defaultButton){
14755             this.defaultButton.focus();
14756         }else{
14757             this.focusEl.focus();
14758         }
14759     },
14760
14761     // private
14762     constrainXY : function(){
14763         if(this.constraintoviewport !== false){
14764             if(!this.viewSize){
14765                 if(this.container){
14766                     var s = this.container.getSize();
14767                     this.viewSize = [s.width, s.height];
14768                 }else{
14769                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14770                 }
14771             }
14772             var s = Roo.get(this.container||document).getScroll();
14773
14774             var x = this.xy[0], y = this.xy[1];
14775             var w = this.size.width, h = this.size.height;
14776             var vw = this.viewSize[0], vh = this.viewSize[1];
14777             // only move it if it needs it
14778             var moved = false;
14779             // first validate right/bottom
14780             if(x + w > vw+s.left){
14781                 x = vw - w;
14782                 moved = true;
14783             }
14784             if(y + h > vh+s.top){
14785                 y = vh - h;
14786                 moved = true;
14787             }
14788             // then make sure top/left isn't negative
14789             if(x < s.left){
14790                 x = s.left;
14791                 moved = true;
14792             }
14793             if(y < s.top){
14794                 y = s.top;
14795                 moved = true;
14796             }
14797             if(moved){
14798                 // cache xy
14799                 this.xy = [x, y];
14800                 if(this.isVisible()){
14801                     this.el.setLocation(x, y);
14802                     this.adjustAssets();
14803                 }
14804             }
14805         }
14806     },
14807
14808     // private
14809     onDrag : function(){
14810         if(!this.proxyDrag){
14811             this.xy = this.el.getXY();
14812             this.adjustAssets();
14813         }
14814     },
14815
14816     // private
14817     adjustAssets : function(doShow){
14818         var x = this.xy[0], y = this.xy[1];
14819         var w = this.size.width, h = this.size.height;
14820         if(doShow === true){
14821             if(this.shadow){
14822                 this.shadow.show(this.el);
14823             }
14824             if(this.shim){
14825                 this.shim.show();
14826             }
14827         }
14828         if(this.shadow && this.shadow.isVisible()){
14829             this.shadow.show(this.el);
14830         }
14831         if(this.shim && this.shim.isVisible()){
14832             this.shim.setBounds(x, y, w, h);
14833         }
14834     },
14835
14836     // private
14837     adjustViewport : function(w, h){
14838         if(!w || !h){
14839             w = Roo.lib.Dom.getViewWidth();
14840             h = Roo.lib.Dom.getViewHeight();
14841         }
14842         // cache the size
14843         this.viewSize = [w, h];
14844         if(this.modal && this.mask.isVisible()){
14845             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14846             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14847         }
14848         if(this.isVisible()){
14849             this.constrainXY();
14850         }
14851     },
14852
14853     /**
14854      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14855      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14856      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14857      */
14858     destroy : function(removeEl){
14859         if(this.isVisible()){
14860             this.animateTarget = null;
14861             this.hide();
14862         }
14863         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14864         if(this.tabs){
14865             this.tabs.destroy(removeEl);
14866         }
14867         Roo.destroy(
14868              this.shim,
14869              this.proxy,
14870              this.resizer,
14871              this.close,
14872              this.mask
14873         );
14874         if(this.dd){
14875             this.dd.unreg();
14876         }
14877         if(this.buttons){
14878            for(var i = 0, len = this.buttons.length; i < len; i++){
14879                this.buttons[i].destroy();
14880            }
14881         }
14882         this.el.removeAllListeners();
14883         if(removeEl === true){
14884             this.el.update("");
14885             this.el.remove();
14886         }
14887         Roo.DialogManager.unregister(this);
14888     },
14889
14890     // private
14891     startMove : function(){
14892         if(this.proxyDrag){
14893             this.proxy.show();
14894         }
14895         if(this.constraintoviewport !== false){
14896             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14897         }
14898     },
14899
14900     // private
14901     endMove : function(){
14902         if(!this.proxyDrag){
14903             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14904         }else{
14905             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14906             this.proxy.hide();
14907         }
14908         this.refreshSize();
14909         this.adjustAssets();
14910         this.focus();
14911         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14912     },
14913
14914     /**
14915      * Brings this dialog to the front of any other visible dialogs
14916      * @return {Roo.BasicDialog} this
14917      */
14918     toFront : function(){
14919         Roo.DialogManager.bringToFront(this);
14920         return this;
14921     },
14922
14923     /**
14924      * Sends this dialog to the back (under) of any other visible dialogs
14925      * @return {Roo.BasicDialog} this
14926      */
14927     toBack : function(){
14928         Roo.DialogManager.sendToBack(this);
14929         return this;
14930     },
14931
14932     /**
14933      * Centers this dialog in the viewport
14934      * @return {Roo.BasicDialog} this
14935      */
14936     center : function(){
14937         var xy = this.el.getCenterXY(true);
14938         this.moveTo(xy[0], xy[1]);
14939         return this;
14940     },
14941
14942     /**
14943      * Moves the dialog's top-left corner to the specified point
14944      * @param {Number} x
14945      * @param {Number} y
14946      * @return {Roo.BasicDialog} this
14947      */
14948     moveTo : function(x, y){
14949         this.xy = [x,y];
14950         if(this.isVisible()){
14951             this.el.setXY(this.xy);
14952             this.adjustAssets();
14953         }
14954         return this;
14955     },
14956
14957     /**
14958      * Aligns the dialog to the specified element
14959      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14960      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14961      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14962      * @return {Roo.BasicDialog} this
14963      */
14964     alignTo : function(element, position, offsets){
14965         this.xy = this.el.getAlignToXY(element, position, offsets);
14966         if(this.isVisible()){
14967             this.el.setXY(this.xy);
14968             this.adjustAssets();
14969         }
14970         return this;
14971     },
14972
14973     /**
14974      * Anchors an element to another element and realigns it when the window is resized.
14975      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14976      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14977      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14978      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14979      * is a number, it is used as the buffer delay (defaults to 50ms).
14980      * @return {Roo.BasicDialog} this
14981      */
14982     anchorTo : function(el, alignment, offsets, monitorScroll){
14983         var action = function(){
14984             this.alignTo(el, alignment, offsets);
14985         };
14986         Roo.EventManager.onWindowResize(action, this);
14987         var tm = typeof monitorScroll;
14988         if(tm != 'undefined'){
14989             Roo.EventManager.on(window, 'scroll', action, this,
14990                 {buffer: tm == 'number' ? monitorScroll : 50});
14991         }
14992         action.call(this);
14993         return this;
14994     },
14995
14996     /**
14997      * Returns true if the dialog is visible
14998      * @return {Boolean}
14999      */
15000     isVisible : function(){
15001         return this.el.isVisible();
15002     },
15003
15004     // private
15005     animHide : function(callback){
15006         var b = Roo.get(this.animateTarget).getBox();
15007         this.proxy.show();
15008         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15009         this.el.hide();
15010         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15011                     this.hideEl.createDelegate(this, [callback]));
15012     },
15013
15014     /**
15015      * Hides the dialog.
15016      * @param {Function} callback (optional) Function to call when the dialog is hidden
15017      * @return {Roo.BasicDialog} this
15018      */
15019     hide : function(callback){
15020         if (this.fireEvent("beforehide", this) === false){
15021             return;
15022         }
15023         if(this.shadow){
15024             this.shadow.hide();
15025         }
15026         if(this.shim) {
15027           this.shim.hide();
15028         }
15029         // sometimes animateTarget seems to get set.. causing problems...
15030         // this just double checks..
15031         if(this.animateTarget && Roo.get(this.animateTarget)) {
15032            this.animHide(callback);
15033         }else{
15034             this.el.hide();
15035             this.hideEl(callback);
15036         }
15037         return this;
15038     },
15039
15040     // private
15041     hideEl : function(callback){
15042         this.proxy.hide();
15043         if(this.modal){
15044             this.mask.hide();
15045             Roo.get(document.body).removeClass("x-body-masked");
15046         }
15047         this.fireEvent("hide", this);
15048         if(typeof callback == "function"){
15049             callback();
15050         }
15051     },
15052
15053     // private
15054     hideAction : function(){
15055         this.setLeft("-10000px");
15056         this.setTop("-10000px");
15057         this.setStyle("visibility", "hidden");
15058     },
15059
15060     // private
15061     refreshSize : function(){
15062         this.size = this.el.getSize();
15063         this.xy = this.el.getXY();
15064         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15065     },
15066
15067     // private
15068     // z-index is managed by the DialogManager and may be overwritten at any time
15069     setZIndex : function(index){
15070         if(this.modal){
15071             this.mask.setStyle("z-index", index);
15072         }
15073         if(this.shim){
15074             this.shim.setStyle("z-index", ++index);
15075         }
15076         if(this.shadow){
15077             this.shadow.setZIndex(++index);
15078         }
15079         this.el.setStyle("z-index", ++index);
15080         if(this.proxy){
15081             this.proxy.setStyle("z-index", ++index);
15082         }
15083         if(this.resizer){
15084             this.resizer.proxy.setStyle("z-index", ++index);
15085         }
15086
15087         this.lastZIndex = index;
15088     },
15089
15090     /**
15091      * Returns the element for this dialog
15092      * @return {Roo.Element} The underlying dialog Element
15093      */
15094     getEl : function(){
15095         return this.el;
15096     }
15097 });
15098
15099 /**
15100  * @class Roo.DialogManager
15101  * Provides global access to BasicDialogs that have been created and
15102  * support for z-indexing (layering) multiple open dialogs.
15103  */
15104 Roo.DialogManager = function(){
15105     var list = {};
15106     var accessList = [];
15107     var front = null;
15108
15109     // private
15110     var sortDialogs = function(d1, d2){
15111         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15112     };
15113
15114     // private
15115     var orderDialogs = function(){
15116         accessList.sort(sortDialogs);
15117         var seed = Roo.DialogManager.zseed;
15118         for(var i = 0, len = accessList.length; i < len; i++){
15119             var dlg = accessList[i];
15120             if(dlg){
15121                 dlg.setZIndex(seed + (i*10));
15122             }
15123         }
15124     };
15125
15126     return {
15127         /**
15128          * The starting z-index for BasicDialogs (defaults to 9000)
15129          * @type Number The z-index value
15130          */
15131         zseed : 9000,
15132
15133         // private
15134         register : function(dlg){
15135             list[dlg.id] = dlg;
15136             accessList.push(dlg);
15137         },
15138
15139         // private
15140         unregister : function(dlg){
15141             delete list[dlg.id];
15142             var i=0;
15143             var len=0;
15144             if(!accessList.indexOf){
15145                 for(  i = 0, len = accessList.length; i < len; i++){
15146                     if(accessList[i] == dlg){
15147                         accessList.splice(i, 1);
15148                         return;
15149                     }
15150                 }
15151             }else{
15152                  i = accessList.indexOf(dlg);
15153                 if(i != -1){
15154                     accessList.splice(i, 1);
15155                 }
15156             }
15157         },
15158
15159         /**
15160          * Gets a registered dialog by id
15161          * @param {String/Object} id The id of the dialog or a dialog
15162          * @return {Roo.BasicDialog} this
15163          */
15164         get : function(id){
15165             return typeof id == "object" ? id : list[id];
15166         },
15167
15168         /**
15169          * Brings the specified dialog to the front
15170          * @param {String/Object} dlg The id of the dialog or a dialog
15171          * @return {Roo.BasicDialog} this
15172          */
15173         bringToFront : function(dlg){
15174             dlg = this.get(dlg);
15175             if(dlg != front){
15176                 front = dlg;
15177                 dlg._lastAccess = new Date().getTime();
15178                 orderDialogs();
15179             }
15180             return dlg;
15181         },
15182
15183         /**
15184          * Sends the specified dialog to the back
15185          * @param {String/Object} dlg The id of the dialog or a dialog
15186          * @return {Roo.BasicDialog} this
15187          */
15188         sendToBack : function(dlg){
15189             dlg = this.get(dlg);
15190             dlg._lastAccess = -(new Date().getTime());
15191             orderDialogs();
15192             return dlg;
15193         },
15194
15195         /**
15196          * Hides all dialogs
15197          */
15198         hideAll : function(){
15199             for(var id in list){
15200                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15201                     list[id].hide();
15202                 }
15203             }
15204         }
15205     };
15206 }();
15207
15208 /**
15209  * @class Roo.LayoutDialog
15210  * @extends Roo.BasicDialog
15211  * Dialog which provides adjustments for working with a layout in a Dialog.
15212  * Add your necessary layout config options to the dialog's config.<br>
15213  * Example usage (including a nested layout):
15214  * <pre><code>
15215 if(!dialog){
15216     dialog = new Roo.LayoutDialog("download-dlg", {
15217         modal: true,
15218         width:600,
15219         height:450,
15220         shadow:true,
15221         minWidth:500,
15222         minHeight:350,
15223         autoTabs:true,
15224         proxyDrag:true,
15225         // layout config merges with the dialog config
15226         center:{
15227             tabPosition: "top",
15228             alwaysShowTabs: true
15229         }
15230     });
15231     dialog.addKeyListener(27, dialog.hide, dialog);
15232     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15233     dialog.addButton("Build It!", this.getDownload, this);
15234
15235     // we can even add nested layouts
15236     var innerLayout = new Roo.BorderLayout("dl-inner", {
15237         east: {
15238             initialSize: 200,
15239             autoScroll:true,
15240             split:true
15241         },
15242         center: {
15243             autoScroll:true
15244         }
15245     });
15246     innerLayout.beginUpdate();
15247     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15248     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15249     innerLayout.endUpdate(true);
15250
15251     var layout = dialog.getLayout();
15252     layout.beginUpdate();
15253     layout.add("center", new Roo.ContentPanel("standard-panel",
15254                         {title: "Download the Source", fitToFrame:true}));
15255     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15256                {title: "Build your own roo.js"}));
15257     layout.getRegion("center").showPanel(sp);
15258     layout.endUpdate();
15259 }
15260 </code></pre>
15261     * @constructor
15262     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15263     * @param {Object} config configuration options
15264   */
15265 Roo.LayoutDialog = function(el, cfg){
15266     
15267     var config=  cfg;
15268     if (typeof(cfg) == 'undefined') {
15269         config = Roo.apply({}, el);
15270         // not sure why we use documentElement here.. - it should always be body.
15271         // IE7 borks horribly if we use documentElement.
15272         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15273         //config.autoCreate = true;
15274     }
15275     
15276     
15277     config.autoTabs = false;
15278     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15279     this.body.setStyle({overflow:"hidden", position:"relative"});
15280     this.layout = new Roo.BorderLayout(this.body.dom, config);
15281     this.layout.monitorWindowResize = false;
15282     this.el.addClass("x-dlg-auto-layout");
15283     // fix case when center region overwrites center function
15284     this.center = Roo.BasicDialog.prototype.center;
15285     this.on("show", this.layout.layout, this.layout, true);
15286     if (config.items) {
15287         var xitems = config.items;
15288         delete config.items;
15289         Roo.each(xitems, this.addxtype, this);
15290     }
15291     
15292     
15293 };
15294 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15295     /**
15296      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15297      * @deprecated
15298      */
15299     endUpdate : function(){
15300         this.layout.endUpdate();
15301     },
15302
15303     /**
15304      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15305      *  @deprecated
15306      */
15307     beginUpdate : function(){
15308         this.layout.beginUpdate();
15309     },
15310
15311     /**
15312      * Get the BorderLayout for this dialog
15313      * @return {Roo.BorderLayout}
15314      */
15315     getLayout : function(){
15316         return this.layout;
15317     },
15318
15319     showEl : function(){
15320         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15321         if(Roo.isIE7){
15322             this.layout.layout();
15323         }
15324     },
15325
15326     // private
15327     // Use the syncHeightBeforeShow config option to control this automatically
15328     syncBodyHeight : function(){
15329         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15330         if(this.layout){this.layout.layout();}
15331     },
15332     
15333       /**
15334      * Add an xtype element (actually adds to the layout.)
15335      * @return {Object} xdata xtype object data.
15336      */
15337     
15338     addxtype : function(c) {
15339         return this.layout.addxtype(c);
15340     }
15341 });/*
15342  * Based on:
15343  * Ext JS Library 1.1.1
15344  * Copyright(c) 2006-2007, Ext JS, LLC.
15345  *
15346  * Originally Released Under LGPL - original licence link has changed is not relivant.
15347  *
15348  * Fork - LGPL
15349  * <script type="text/javascript">
15350  */
15351  
15352 /**
15353  * @class Roo.MessageBox
15354  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15355  * Example usage:
15356  *<pre><code>
15357 // Basic alert:
15358 Roo.Msg.alert('Status', 'Changes saved successfully.');
15359
15360 // Prompt for user data:
15361 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15362     if (btn == 'ok'){
15363         // process text value...
15364     }
15365 });
15366
15367 // Show a dialog using config options:
15368 Roo.Msg.show({
15369    title:'Save Changes?',
15370    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15371    buttons: Roo.Msg.YESNOCANCEL,
15372    fn: processResult,
15373    animEl: 'elId'
15374 });
15375 </code></pre>
15376  * @singleton
15377  */
15378 Roo.MessageBox = function(){
15379     var dlg, opt, mask, waitTimer;
15380     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15381     var buttons, activeTextEl, bwidth;
15382
15383     // private
15384     var handleButton = function(button){
15385         dlg.hide();
15386         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15387     };
15388
15389     // private
15390     var handleHide = function(){
15391         if(opt && opt.cls){
15392             dlg.el.removeClass(opt.cls);
15393         }
15394         if(waitTimer){
15395             Roo.TaskMgr.stop(waitTimer);
15396             waitTimer = null;
15397         }
15398     };
15399
15400     // private
15401     var updateButtons = function(b){
15402         var width = 0;
15403         if(!b){
15404             buttons["ok"].hide();
15405             buttons["cancel"].hide();
15406             buttons["yes"].hide();
15407             buttons["no"].hide();
15408             dlg.footer.dom.style.display = 'none';
15409             return width;
15410         }
15411         dlg.footer.dom.style.display = '';
15412         for(var k in buttons){
15413             if(typeof buttons[k] != "function"){
15414                 if(b[k]){
15415                     buttons[k].show();
15416                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15417                     width += buttons[k].el.getWidth()+15;
15418                 }else{
15419                     buttons[k].hide();
15420                 }
15421             }
15422         }
15423         return width;
15424     };
15425
15426     // private
15427     var handleEsc = function(d, k, e){
15428         if(opt && opt.closable !== false){
15429             dlg.hide();
15430         }
15431         if(e){
15432             e.stopEvent();
15433         }
15434     };
15435
15436     return {
15437         /**
15438          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15439          * @return {Roo.BasicDialog} The BasicDialog element
15440          */
15441         getDialog : function(){
15442            if(!dlg){
15443                 dlg = new Roo.BasicDialog("x-msg-box", {
15444                     autoCreate : true,
15445                     shadow: true,
15446                     draggable: true,
15447                     resizable:false,
15448                     constraintoviewport:false,
15449                     fixedcenter:true,
15450                     collapsible : false,
15451                     shim:true,
15452                     modal: true,
15453                     width:400, height:100,
15454                     buttonAlign:"center",
15455                     closeClick : function(){
15456                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15457                             handleButton("no");
15458                         }else{
15459                             handleButton("cancel");
15460                         }
15461                     }
15462                 });
15463                 dlg.on("hide", handleHide);
15464                 mask = dlg.mask;
15465                 dlg.addKeyListener(27, handleEsc);
15466                 buttons = {};
15467                 var bt = this.buttonText;
15468                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15469                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15470                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15471                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15472                 bodyEl = dlg.body.createChild({
15473
15474                     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>'
15475                 });
15476                 msgEl = bodyEl.dom.firstChild;
15477                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15478                 textboxEl.enableDisplayMode();
15479                 textboxEl.addKeyListener([10,13], function(){
15480                     if(dlg.isVisible() && opt && opt.buttons){
15481                         if(opt.buttons.ok){
15482                             handleButton("ok");
15483                         }else if(opt.buttons.yes){
15484                             handleButton("yes");
15485                         }
15486                     }
15487                 });
15488                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15489                 textareaEl.enableDisplayMode();
15490                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15491                 progressEl.enableDisplayMode();
15492                 var pf = progressEl.dom.firstChild;
15493                 if (pf) {
15494                     pp = Roo.get(pf.firstChild);
15495                     pp.setHeight(pf.offsetHeight);
15496                 }
15497                 
15498             }
15499             return dlg;
15500         },
15501
15502         /**
15503          * Updates the message box body text
15504          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15505          * the XHTML-compliant non-breaking space character '&amp;#160;')
15506          * @return {Roo.MessageBox} This message box
15507          */
15508         updateText : function(text){
15509             if(!dlg.isVisible() && !opt.width){
15510                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15511             }
15512             msgEl.innerHTML = text || '&#160;';
15513             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15514                         Math.max(opt.minWidth || this.minWidth, bwidth));
15515             if(opt.prompt){
15516                 activeTextEl.setWidth(w);
15517             }
15518             if(dlg.isVisible()){
15519                 dlg.fixedcenter = false;
15520             }
15521             dlg.setContentSize(w, bodyEl.getHeight());
15522             if(dlg.isVisible()){
15523                 dlg.fixedcenter = true;
15524             }
15525             return this;
15526         },
15527
15528         /**
15529          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15530          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15531          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15532          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15533          * @return {Roo.MessageBox} This message box
15534          */
15535         updateProgress : function(value, text){
15536             if(text){
15537                 this.updateText(text);
15538             }
15539             if (pp) { // weird bug on my firefox - for some reason this is not defined
15540                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15541             }
15542             return this;
15543         },        
15544
15545         /**
15546          * Returns true if the message box is currently displayed
15547          * @return {Boolean} True if the message box is visible, else false
15548          */
15549         isVisible : function(){
15550             return dlg && dlg.isVisible();  
15551         },
15552
15553         /**
15554          * Hides the message box if it is displayed
15555          */
15556         hide : function(){
15557             if(this.isVisible()){
15558                 dlg.hide();
15559             }  
15560         },
15561
15562         /**
15563          * Displays a new message box, or reinitializes an existing message box, based on the config options
15564          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15565          * The following config object properties are supported:
15566          * <pre>
15567 Property    Type             Description
15568 ----------  ---------------  ------------------------------------------------------------------------------------
15569 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15570                                    closes (defaults to undefined)
15571 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15572                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15573 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15574                                    progress and wait dialogs will ignore this property and always hide the
15575                                    close button as they can only be closed programmatically.
15576 cls               String           A custom CSS class to apply to the message box element
15577 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15578                                    displayed (defaults to 75)
15579 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15580                                    function will be btn (the name of the button that was clicked, if applicable,
15581                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15582                                    Progress and wait dialogs will ignore this option since they do not respond to
15583                                    user actions and can only be closed programmatically, so any required function
15584                                    should be called by the same code after it closes the dialog.
15585 icon              String           A CSS class that provides a background image to be used as an icon for
15586                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15587 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15588 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15589 modal             Boolean          False to allow user interaction with the page while the message box is
15590                                    displayed (defaults to true)
15591 msg               String           A string that will replace the existing message box body text (defaults
15592                                    to the XHTML-compliant non-breaking space character '&#160;')
15593 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15594 progress          Boolean          True to display a progress bar (defaults to false)
15595 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15596 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15597 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15598 title             String           The title text
15599 value             String           The string value to set into the active textbox element if displayed
15600 wait              Boolean          True to display a progress bar (defaults to false)
15601 width             Number           The width of the dialog in pixels
15602 </pre>
15603          *
15604          * Example usage:
15605          * <pre><code>
15606 Roo.Msg.show({
15607    title: 'Address',
15608    msg: 'Please enter your address:',
15609    width: 300,
15610    buttons: Roo.MessageBox.OKCANCEL,
15611    multiline: true,
15612    fn: saveAddress,
15613    animEl: 'addAddressBtn'
15614 });
15615 </code></pre>
15616          * @param {Object} config Configuration options
15617          * @return {Roo.MessageBox} This message box
15618          */
15619         show : function(options){
15620             if(this.isVisible()){
15621                 this.hide();
15622             }
15623             var d = this.getDialog();
15624             opt = options;
15625             d.setTitle(opt.title || "&#160;");
15626             d.close.setDisplayed(opt.closable !== false);
15627             activeTextEl = textboxEl;
15628             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15629             if(opt.prompt){
15630                 if(opt.multiline){
15631                     textboxEl.hide();
15632                     textareaEl.show();
15633                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15634                         opt.multiline : this.defaultTextHeight);
15635                     activeTextEl = textareaEl;
15636                 }else{
15637                     textboxEl.show();
15638                     textareaEl.hide();
15639                 }
15640             }else{
15641                 textboxEl.hide();
15642                 textareaEl.hide();
15643             }
15644             progressEl.setDisplayed(opt.progress === true);
15645             this.updateProgress(0);
15646             activeTextEl.dom.value = opt.value || "";
15647             if(opt.prompt){
15648                 dlg.setDefaultButton(activeTextEl);
15649             }else{
15650                 var bs = opt.buttons;
15651                 var db = null;
15652                 if(bs && bs.ok){
15653                     db = buttons["ok"];
15654                 }else if(bs && bs.yes){
15655                     db = buttons["yes"];
15656                 }
15657                 dlg.setDefaultButton(db);
15658             }
15659             bwidth = updateButtons(opt.buttons);
15660             this.updateText(opt.msg);
15661             if(opt.cls){
15662                 d.el.addClass(opt.cls);
15663             }
15664             d.proxyDrag = opt.proxyDrag === true;
15665             d.modal = opt.modal !== false;
15666             d.mask = opt.modal !== false ? mask : false;
15667             if(!d.isVisible()){
15668                 // force it to the end of the z-index stack so it gets a cursor in FF
15669                 document.body.appendChild(dlg.el.dom);
15670                 d.animateTarget = null;
15671                 d.show(options.animEl);
15672             }
15673             return this;
15674         },
15675
15676         /**
15677          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15678          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15679          * and closing the message box when the process is complete.
15680          * @param {String} title The title bar text
15681          * @param {String} msg The message box body text
15682          * @return {Roo.MessageBox} This message box
15683          */
15684         progress : function(title, msg){
15685             this.show({
15686                 title : title,
15687                 msg : msg,
15688                 buttons: false,
15689                 progress:true,
15690                 closable:false,
15691                 minWidth: this.minProgressWidth,
15692                 modal : true
15693             });
15694             return this;
15695         },
15696
15697         /**
15698          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15699          * If a callback function is passed it will be called after the user clicks the button, and the
15700          * id of the button that was clicked will be passed as the only parameter to the callback
15701          * (could also be the top-right close button).
15702          * @param {String} title The title bar text
15703          * @param {String} msg The message box body text
15704          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15705          * @param {Object} scope (optional) The scope of the callback function
15706          * @return {Roo.MessageBox} This message box
15707          */
15708         alert : function(title, msg, fn, scope){
15709             this.show({
15710                 title : title,
15711                 msg : msg,
15712                 buttons: this.OK,
15713                 fn: fn,
15714                 scope : scope,
15715                 modal : true
15716             });
15717             return this;
15718         },
15719
15720         /**
15721          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15722          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15723          * You are responsible for closing the message box when the process is complete.
15724          * @param {String} msg The message box body text
15725          * @param {String} title (optional) The title bar text
15726          * @return {Roo.MessageBox} This message box
15727          */
15728         wait : function(msg, title){
15729             this.show({
15730                 title : title,
15731                 msg : msg,
15732                 buttons: false,
15733                 closable:false,
15734                 progress:true,
15735                 modal:true,
15736                 width:300,
15737                 wait:true
15738             });
15739             waitTimer = Roo.TaskMgr.start({
15740                 run: function(i){
15741                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15742                 },
15743                 interval: 1000
15744             });
15745             return this;
15746         },
15747
15748         /**
15749          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15750          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15751          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15752          * @param {String} title The title bar text
15753          * @param {String} msg The message box body text
15754          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15755          * @param {Object} scope (optional) The scope of the callback function
15756          * @return {Roo.MessageBox} This message box
15757          */
15758         confirm : function(title, msg, fn, scope){
15759             this.show({
15760                 title : title,
15761                 msg : msg,
15762                 buttons: this.YESNO,
15763                 fn: fn,
15764                 scope : scope,
15765                 modal : true
15766             });
15767             return this;
15768         },
15769
15770         /**
15771          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15772          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15773          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15774          * (could also be the top-right close button) and the text that was entered will be passed as the two
15775          * parameters to the callback.
15776          * @param {String} title The title bar text
15777          * @param {String} msg The message box body text
15778          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15779          * @param {Object} scope (optional) The scope of the callback function
15780          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15781          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15782          * @return {Roo.MessageBox} This message box
15783          */
15784         prompt : function(title, msg, fn, scope, multiline){
15785             this.show({
15786                 title : title,
15787                 msg : msg,
15788                 buttons: this.OKCANCEL,
15789                 fn: fn,
15790                 minWidth:250,
15791                 scope : scope,
15792                 prompt:true,
15793                 multiline: multiline,
15794                 modal : true
15795             });
15796             return this;
15797         },
15798
15799         /**
15800          * Button config that displays a single OK button
15801          * @type Object
15802          */
15803         OK : {ok:true},
15804         /**
15805          * Button config that displays Yes and No buttons
15806          * @type Object
15807          */
15808         YESNO : {yes:true, no:true},
15809         /**
15810          * Button config that displays OK and Cancel buttons
15811          * @type Object
15812          */
15813         OKCANCEL : {ok:true, cancel:true},
15814         /**
15815          * Button config that displays Yes, No and Cancel buttons
15816          * @type Object
15817          */
15818         YESNOCANCEL : {yes:true, no:true, cancel:true},
15819
15820         /**
15821          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15822          * @type Number
15823          */
15824         defaultTextHeight : 75,
15825         /**
15826          * The maximum width in pixels of the message box (defaults to 600)
15827          * @type Number
15828          */
15829         maxWidth : 600,
15830         /**
15831          * The minimum width in pixels of the message box (defaults to 100)
15832          * @type Number
15833          */
15834         minWidth : 100,
15835         /**
15836          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15837          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15838          * @type Number
15839          */
15840         minProgressWidth : 250,
15841         /**
15842          * An object containing the default button text strings that can be overriden for localized language support.
15843          * Supported properties are: ok, cancel, yes and no.
15844          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15845          * @type Object
15846          */
15847         buttonText : {
15848             ok : "OK",
15849             cancel : "Cancel",
15850             yes : "Yes",
15851             no : "No"
15852         }
15853     };
15854 }();
15855
15856 /**
15857  * Shorthand for {@link Roo.MessageBox}
15858  */
15859 Roo.Msg = Roo.MessageBox;/*
15860  * Based on:
15861  * Ext JS Library 1.1.1
15862  * Copyright(c) 2006-2007, Ext JS, LLC.
15863  *
15864  * Originally Released Under LGPL - original licence link has changed is not relivant.
15865  *
15866  * Fork - LGPL
15867  * <script type="text/javascript">
15868  */
15869 /**
15870  * @class Roo.QuickTips
15871  * Provides attractive and customizable tooltips for any element.
15872  * @singleton
15873  */
15874 Roo.QuickTips = function(){
15875     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15876     var ce, bd, xy, dd;
15877     var visible = false, disabled = true, inited = false;
15878     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15879     
15880     var onOver = function(e){
15881         if(disabled){
15882             return;
15883         }
15884         var t = e.getTarget();
15885         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15886             return;
15887         }
15888         if(ce && t == ce.el){
15889             clearTimeout(hideProc);
15890             return;
15891         }
15892         if(t && tagEls[t.id]){
15893             tagEls[t.id].el = t;
15894             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15895             return;
15896         }
15897         var ttp, et = Roo.fly(t);
15898         var ns = cfg.namespace;
15899         if(tm.interceptTitles && t.title){
15900             ttp = t.title;
15901             t.qtip = ttp;
15902             t.removeAttribute("title");
15903             e.preventDefault();
15904         }else{
15905             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15906         }
15907         if(ttp){
15908             showProc = show.defer(tm.showDelay, tm, [{
15909                 el: t, 
15910                 text: ttp, 
15911                 width: et.getAttributeNS(ns, cfg.width),
15912                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15913                 title: et.getAttributeNS(ns, cfg.title),
15914                     cls: et.getAttributeNS(ns, cfg.cls)
15915             }]);
15916         }
15917     };
15918     
15919     var onOut = function(e){
15920         clearTimeout(showProc);
15921         var t = e.getTarget();
15922         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15923             hideProc = setTimeout(hide, tm.hideDelay);
15924         }
15925     };
15926     
15927     var onMove = function(e){
15928         if(disabled){
15929             return;
15930         }
15931         xy = e.getXY();
15932         xy[1] += 18;
15933         if(tm.trackMouse && ce){
15934             el.setXY(xy);
15935         }
15936     };
15937     
15938     var onDown = function(e){
15939         clearTimeout(showProc);
15940         clearTimeout(hideProc);
15941         if(!e.within(el)){
15942             if(tm.hideOnClick){
15943                 hide();
15944                 tm.disable();
15945                 tm.enable.defer(100, tm);
15946             }
15947         }
15948     };
15949     
15950     var getPad = function(){
15951         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15952     };
15953
15954     var show = function(o){
15955         if(disabled){
15956             return;
15957         }
15958         clearTimeout(dismissProc);
15959         ce = o;
15960         if(removeCls){ // in case manually hidden
15961             el.removeClass(removeCls);
15962             removeCls = null;
15963         }
15964         if(ce.cls){
15965             el.addClass(ce.cls);
15966             removeCls = ce.cls;
15967         }
15968         if(ce.title){
15969             tipTitle.update(ce.title);
15970             tipTitle.show();
15971         }else{
15972             tipTitle.update('');
15973             tipTitle.hide();
15974         }
15975         el.dom.style.width  = tm.maxWidth+'px';
15976         //tipBody.dom.style.width = '';
15977         tipBodyText.update(o.text);
15978         var p = getPad(), w = ce.width;
15979         if(!w){
15980             var td = tipBodyText.dom;
15981             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15982             if(aw > tm.maxWidth){
15983                 w = tm.maxWidth;
15984             }else if(aw < tm.minWidth){
15985                 w = tm.minWidth;
15986             }else{
15987                 w = aw;
15988             }
15989         }
15990         //tipBody.setWidth(w);
15991         el.setWidth(parseInt(w, 10) + p);
15992         if(ce.autoHide === false){
15993             close.setDisplayed(true);
15994             if(dd){
15995                 dd.unlock();
15996             }
15997         }else{
15998             close.setDisplayed(false);
15999             if(dd){
16000                 dd.lock();
16001             }
16002         }
16003         if(xy){
16004             el.avoidY = xy[1]-18;
16005             el.setXY(xy);
16006         }
16007         if(tm.animate){
16008             el.setOpacity(.1);
16009             el.setStyle("visibility", "visible");
16010             el.fadeIn({callback: afterShow});
16011         }else{
16012             afterShow();
16013         }
16014     };
16015     
16016     var afterShow = function(){
16017         if(ce){
16018             el.show();
16019             esc.enable();
16020             if(tm.autoDismiss && ce.autoHide !== false){
16021                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16022             }
16023         }
16024     };
16025     
16026     var hide = function(noanim){
16027         clearTimeout(dismissProc);
16028         clearTimeout(hideProc);
16029         ce = null;
16030         if(el.isVisible()){
16031             esc.disable();
16032             if(noanim !== true && tm.animate){
16033                 el.fadeOut({callback: afterHide});
16034             }else{
16035                 afterHide();
16036             } 
16037         }
16038     };
16039     
16040     var afterHide = function(){
16041         el.hide();
16042         if(removeCls){
16043             el.removeClass(removeCls);
16044             removeCls = null;
16045         }
16046     };
16047     
16048     return {
16049         /**
16050         * @cfg {Number} minWidth
16051         * The minimum width of the quick tip (defaults to 40)
16052         */
16053        minWidth : 40,
16054         /**
16055         * @cfg {Number} maxWidth
16056         * The maximum width of the quick tip (defaults to 300)
16057         */
16058        maxWidth : 300,
16059         /**
16060         * @cfg {Boolean} interceptTitles
16061         * True to automatically use the element's DOM title value if available (defaults to false)
16062         */
16063        interceptTitles : false,
16064         /**
16065         * @cfg {Boolean} trackMouse
16066         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16067         */
16068        trackMouse : false,
16069         /**
16070         * @cfg {Boolean} hideOnClick
16071         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16072         */
16073        hideOnClick : true,
16074         /**
16075         * @cfg {Number} showDelay
16076         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16077         */
16078        showDelay : 500,
16079         /**
16080         * @cfg {Number} hideDelay
16081         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16082         */
16083        hideDelay : 200,
16084         /**
16085         * @cfg {Boolean} autoHide
16086         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16087         * Used in conjunction with hideDelay.
16088         */
16089        autoHide : true,
16090         /**
16091         * @cfg {Boolean}
16092         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16093         * (defaults to true).  Used in conjunction with autoDismissDelay.
16094         */
16095        autoDismiss : true,
16096         /**
16097         * @cfg {Number}
16098         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16099         */
16100        autoDismissDelay : 5000,
16101        /**
16102         * @cfg {Boolean} animate
16103         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16104         */
16105        animate : false,
16106
16107        /**
16108         * @cfg {String} title
16109         * Title text to display (defaults to '').  This can be any valid HTML markup.
16110         */
16111         title: '',
16112        /**
16113         * @cfg {String} text
16114         * Body text to display (defaults to '').  This can be any valid HTML markup.
16115         */
16116         text : '',
16117        /**
16118         * @cfg {String} cls
16119         * A CSS class to apply to the base quick tip element (defaults to '').
16120         */
16121         cls : '',
16122        /**
16123         * @cfg {Number} width
16124         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16125         * minWidth or maxWidth.
16126         */
16127         width : null,
16128
16129     /**
16130      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16131      * or display QuickTips in a page.
16132      */
16133        init : function(){
16134           tm = Roo.QuickTips;
16135           cfg = tm.tagConfig;
16136           if(!inited){
16137               if(!Roo.isReady){ // allow calling of init() before onReady
16138                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16139                   return;
16140               }
16141               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16142               el.fxDefaults = {stopFx: true};
16143               // maximum custom styling
16144               //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>');
16145               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>');              
16146               tipTitle = el.child('h3');
16147               tipTitle.enableDisplayMode("block");
16148               tipBody = el.child('div.x-tip-bd');
16149               tipBodyText = el.child('div.x-tip-bd-inner');
16150               //bdLeft = el.child('div.x-tip-bd-left');
16151               //bdRight = el.child('div.x-tip-bd-right');
16152               close = el.child('div.x-tip-close');
16153               close.enableDisplayMode("block");
16154               close.on("click", hide);
16155               var d = Roo.get(document);
16156               d.on("mousedown", onDown);
16157               d.on("mouseover", onOver);
16158               d.on("mouseout", onOut);
16159               d.on("mousemove", onMove);
16160               esc = d.addKeyListener(27, hide);
16161               esc.disable();
16162               if(Roo.dd.DD){
16163                   dd = el.initDD("default", null, {
16164                       onDrag : function(){
16165                           el.sync();  
16166                       }
16167                   });
16168                   dd.setHandleElId(tipTitle.id);
16169                   dd.lock();
16170               }
16171               inited = true;
16172           }
16173           this.enable(); 
16174        },
16175
16176     /**
16177      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16178      * are supported:
16179      * <pre>
16180 Property    Type                   Description
16181 ----------  ---------------------  ------------------------------------------------------------------------
16182 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16183      * </ul>
16184      * @param {Object} config The config object
16185      */
16186        register : function(config){
16187            var cs = config instanceof Array ? config : arguments;
16188            for(var i = 0, len = cs.length; i < len; i++) {
16189                var c = cs[i];
16190                var target = c.target;
16191                if(target){
16192                    if(target instanceof Array){
16193                        for(var j = 0, jlen = target.length; j < jlen; j++){
16194                            tagEls[target[j]] = c;
16195                        }
16196                    }else{
16197                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16198                    }
16199                }
16200            }
16201        },
16202
16203     /**
16204      * Removes this quick tip from its element and destroys it.
16205      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16206      */
16207        unregister : function(el){
16208            delete tagEls[Roo.id(el)];
16209        },
16210
16211     /**
16212      * Enable this quick tip.
16213      */
16214        enable : function(){
16215            if(inited && disabled){
16216                locks.pop();
16217                if(locks.length < 1){
16218                    disabled = false;
16219                }
16220            }
16221        },
16222
16223     /**
16224      * Disable this quick tip.
16225      */
16226        disable : function(){
16227           disabled = true;
16228           clearTimeout(showProc);
16229           clearTimeout(hideProc);
16230           clearTimeout(dismissProc);
16231           if(ce){
16232               hide(true);
16233           }
16234           locks.push(1);
16235        },
16236
16237     /**
16238      * Returns true if the quick tip is enabled, else false.
16239      */
16240        isEnabled : function(){
16241             return !disabled;
16242        },
16243
16244         // private
16245        tagConfig : {
16246            namespace : "ext",
16247            attribute : "qtip",
16248            width : "width",
16249            target : "target",
16250            title : "qtitle",
16251            hide : "hide",
16252            cls : "qclass"
16253        }
16254    };
16255 }();
16256
16257 // backwards compat
16258 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16259  * Based on:
16260  * Ext JS Library 1.1.1
16261  * Copyright(c) 2006-2007, Ext JS, LLC.
16262  *
16263  * Originally Released Under LGPL - original licence link has changed is not relivant.
16264  *
16265  * Fork - LGPL
16266  * <script type="text/javascript">
16267  */
16268  
16269
16270 /**
16271  * @class Roo.tree.TreePanel
16272  * @extends Roo.data.Tree
16273
16274  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16275  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16276  * @cfg {Boolean} enableDD true to enable drag and drop
16277  * @cfg {Boolean} enableDrag true to enable just drag
16278  * @cfg {Boolean} enableDrop true to enable just drop
16279  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16280  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16281  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16282  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16283  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16284  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16285  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16286  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16287  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16288  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16289  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16290  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16291  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16292  * @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>
16293  * @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>
16294  * 
16295  * @constructor
16296  * @param {String/HTMLElement/Element} el The container element
16297  * @param {Object} config
16298  */
16299 Roo.tree.TreePanel = function(el, config){
16300     var root = false;
16301     var loader = false;
16302     if (config.root) {
16303         root = config.root;
16304         delete config.root;
16305     }
16306     if (config.loader) {
16307         loader = config.loader;
16308         delete config.loader;
16309     }
16310     
16311     Roo.apply(this, config);
16312     Roo.tree.TreePanel.superclass.constructor.call(this);
16313     this.el = Roo.get(el);
16314     this.el.addClass('x-tree');
16315     //console.log(root);
16316     if (root) {
16317         this.setRootNode( Roo.factory(root, Roo.tree));
16318     }
16319     if (loader) {
16320         this.loader = Roo.factory(loader, Roo.tree);
16321     }
16322    /**
16323     * Read-only. The id of the container element becomes this TreePanel's id.
16324     */
16325    this.id = this.el.id;
16326    this.addEvents({
16327         /**
16328         * @event beforeload
16329         * Fires before a node is loaded, return false to cancel
16330         * @param {Node} node The node being loaded
16331         */
16332         "beforeload" : true,
16333         /**
16334         * @event load
16335         * Fires when a node is loaded
16336         * @param {Node} node The node that was loaded
16337         */
16338         "load" : true,
16339         /**
16340         * @event textchange
16341         * Fires when the text for a node is changed
16342         * @param {Node} node The node
16343         * @param {String} text The new text
16344         * @param {String} oldText The old text
16345         */
16346         "textchange" : true,
16347         /**
16348         * @event beforeexpand
16349         * Fires before a node is expanded, return false to cancel.
16350         * @param {Node} node The node
16351         * @param {Boolean} deep
16352         * @param {Boolean} anim
16353         */
16354         "beforeexpand" : true,
16355         /**
16356         * @event beforecollapse
16357         * Fires before a node is collapsed, return false to cancel.
16358         * @param {Node} node The node
16359         * @param {Boolean} deep
16360         * @param {Boolean} anim
16361         */
16362         "beforecollapse" : true,
16363         /**
16364         * @event expand
16365         * Fires when a node is expanded
16366         * @param {Node} node The node
16367         */
16368         "expand" : true,
16369         /**
16370         * @event disabledchange
16371         * Fires when the disabled status of a node changes
16372         * @param {Node} node The node
16373         * @param {Boolean} disabled
16374         */
16375         "disabledchange" : true,
16376         /**
16377         * @event collapse
16378         * Fires when a node is collapsed
16379         * @param {Node} node The node
16380         */
16381         "collapse" : true,
16382         /**
16383         * @event beforeclick
16384         * Fires before click processing on a node. Return false to cancel the default action.
16385         * @param {Node} node The node
16386         * @param {Roo.EventObject} e The event object
16387         */
16388         "beforeclick":true,
16389         /**
16390         * @event checkchange
16391         * Fires when a node with a checkbox's checked property changes
16392         * @param {Node} this This node
16393         * @param {Boolean} checked
16394         */
16395         "checkchange":true,
16396         /**
16397         * @event click
16398         * Fires when a node is clicked
16399         * @param {Node} node The node
16400         * @param {Roo.EventObject} e The event object
16401         */
16402         "click":true,
16403         /**
16404         * @event dblclick
16405         * Fires when a node is double clicked
16406         * @param {Node} node The node
16407         * @param {Roo.EventObject} e The event object
16408         */
16409         "dblclick":true,
16410         /**
16411         * @event contextmenu
16412         * Fires when a node is right clicked
16413         * @param {Node} node The node
16414         * @param {Roo.EventObject} e The event object
16415         */
16416         "contextmenu":true,
16417         /**
16418         * @event beforechildrenrendered
16419         * Fires right before the child nodes for a node are rendered
16420         * @param {Node} node The node
16421         */
16422         "beforechildrenrendered":true,
16423        /**
16424              * @event startdrag
16425              * Fires when a node starts being dragged
16426              * @param {Roo.tree.TreePanel} this
16427              * @param {Roo.tree.TreeNode} node
16428              * @param {event} e The raw browser event
16429              */ 
16430             "startdrag" : true,
16431             /**
16432              * @event enddrag
16433              * Fires when a drag operation is complete
16434              * @param {Roo.tree.TreePanel} this
16435              * @param {Roo.tree.TreeNode} node
16436              * @param {event} e The raw browser event
16437              */
16438             "enddrag" : true,
16439             /**
16440              * @event dragdrop
16441              * Fires when a dragged node is dropped on a valid DD target
16442              * @param {Roo.tree.TreePanel} this
16443              * @param {Roo.tree.TreeNode} node
16444              * @param {DD} dd The dd it was dropped on
16445              * @param {event} e The raw browser event
16446              */
16447             "dragdrop" : true,
16448             /**
16449              * @event beforenodedrop
16450              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16451              * passed to handlers has the following properties:<br />
16452              * <ul style="padding:5px;padding-left:16px;">
16453              * <li>tree - The TreePanel</li>
16454              * <li>target - The node being targeted for the drop</li>
16455              * <li>data - The drag data from the drag source</li>
16456              * <li>point - The point of the drop - append, above or below</li>
16457              * <li>source - The drag source</li>
16458              * <li>rawEvent - Raw mouse event</li>
16459              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16460              * to be inserted by setting them on this object.</li>
16461              * <li>cancel - Set this to true to cancel the drop.</li>
16462              * </ul>
16463              * @param {Object} dropEvent
16464              */
16465             "beforenodedrop" : true,
16466             /**
16467              * @event nodedrop
16468              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16469              * passed to handlers has the following properties:<br />
16470              * <ul style="padding:5px;padding-left:16px;">
16471              * <li>tree - The TreePanel</li>
16472              * <li>target - The node being targeted for the drop</li>
16473              * <li>data - The drag data from the drag source</li>
16474              * <li>point - The point of the drop - append, above or below</li>
16475              * <li>source - The drag source</li>
16476              * <li>rawEvent - Raw mouse event</li>
16477              * <li>dropNode - Dropped node(s).</li>
16478              * </ul>
16479              * @param {Object} dropEvent
16480              */
16481             "nodedrop" : true,
16482              /**
16483              * @event nodedragover
16484              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16485              * passed to handlers has the following properties:<br />
16486              * <ul style="padding:5px;padding-left:16px;">
16487              * <li>tree - The TreePanel</li>
16488              * <li>target - The node being targeted for the drop</li>
16489              * <li>data - The drag data from the drag source</li>
16490              * <li>point - The point of the drop - append, above or below</li>
16491              * <li>source - The drag source</li>
16492              * <li>rawEvent - Raw mouse event</li>
16493              * <li>dropNode - Drop node(s) provided by the source.</li>
16494              * <li>cancel - Set this to true to signal drop not allowed.</li>
16495              * </ul>
16496              * @param {Object} dragOverEvent
16497              */
16498             "nodedragover" : true
16499         
16500    });
16501    if(this.singleExpand){
16502        this.on("beforeexpand", this.restrictExpand, this);
16503    }
16504 };
16505 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16506     rootVisible : true,
16507     animate: Roo.enableFx,
16508     lines : true,
16509     enableDD : false,
16510     hlDrop : Roo.enableFx,
16511   
16512     renderer: false,
16513     
16514     rendererTip: false,
16515     // private
16516     restrictExpand : function(node){
16517         var p = node.parentNode;
16518         if(p){
16519             if(p.expandedChild && p.expandedChild.parentNode == p){
16520                 p.expandedChild.collapse();
16521             }
16522             p.expandedChild = node;
16523         }
16524     },
16525
16526     // private override
16527     setRootNode : function(node){
16528         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16529         if(!this.rootVisible){
16530             node.ui = new Roo.tree.RootTreeNodeUI(node);
16531         }
16532         return node;
16533     },
16534
16535     /**
16536      * Returns the container element for this TreePanel
16537      */
16538     getEl : function(){
16539         return this.el;
16540     },
16541
16542     /**
16543      * Returns the default TreeLoader for this TreePanel
16544      */
16545     getLoader : function(){
16546         return this.loader;
16547     },
16548
16549     /**
16550      * Expand all nodes
16551      */
16552     expandAll : function(){
16553         this.root.expand(true);
16554     },
16555
16556     /**
16557      * Collapse all nodes
16558      */
16559     collapseAll : function(){
16560         this.root.collapse(true);
16561     },
16562
16563     /**
16564      * Returns the selection model used by this TreePanel
16565      */
16566     getSelectionModel : function(){
16567         if(!this.selModel){
16568             this.selModel = new Roo.tree.DefaultSelectionModel();
16569         }
16570         return this.selModel;
16571     },
16572
16573     /**
16574      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16575      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16576      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16577      * @return {Array}
16578      */
16579     getChecked : function(a, startNode){
16580         startNode = startNode || this.root;
16581         var r = [];
16582         var f = function(){
16583             if(this.attributes.checked){
16584                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16585             }
16586         }
16587         startNode.cascade(f);
16588         return r;
16589     },
16590
16591     /**
16592      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16593      * @param {String} path
16594      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16595      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16596      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16597      */
16598     expandPath : function(path, attr, callback){
16599         attr = attr || "id";
16600         var keys = path.split(this.pathSeparator);
16601         var curNode = this.root;
16602         if(curNode.attributes[attr] != keys[1]){ // invalid root
16603             if(callback){
16604                 callback(false, null);
16605             }
16606             return;
16607         }
16608         var index = 1;
16609         var f = function(){
16610             if(++index == keys.length){
16611                 if(callback){
16612                     callback(true, curNode);
16613                 }
16614                 return;
16615             }
16616             var c = curNode.findChild(attr, keys[index]);
16617             if(!c){
16618                 if(callback){
16619                     callback(false, curNode);
16620                 }
16621                 return;
16622             }
16623             curNode = c;
16624             c.expand(false, false, f);
16625         };
16626         curNode.expand(false, false, f);
16627     },
16628
16629     /**
16630      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16631      * @param {String} path
16632      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16633      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16634      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16635      */
16636     selectPath : function(path, attr, callback){
16637         attr = attr || "id";
16638         var keys = path.split(this.pathSeparator);
16639         var v = keys.pop();
16640         if(keys.length > 0){
16641             var f = function(success, node){
16642                 if(success && node){
16643                     var n = node.findChild(attr, v);
16644                     if(n){
16645                         n.select();
16646                         if(callback){
16647                             callback(true, n);
16648                         }
16649                     }else if(callback){
16650                         callback(false, n);
16651                     }
16652                 }else{
16653                     if(callback){
16654                         callback(false, n);
16655                     }
16656                 }
16657             };
16658             this.expandPath(keys.join(this.pathSeparator), attr, f);
16659         }else{
16660             this.root.select();
16661             if(callback){
16662                 callback(true, this.root);
16663             }
16664         }
16665     },
16666
16667     getTreeEl : function(){
16668         return this.el;
16669     },
16670
16671     /**
16672      * Trigger rendering of this TreePanel
16673      */
16674     render : function(){
16675         if (this.innerCt) {
16676             return this; // stop it rendering more than once!!
16677         }
16678         
16679         this.innerCt = this.el.createChild({tag:"ul",
16680                cls:"x-tree-root-ct " +
16681                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16682
16683         if(this.containerScroll){
16684             Roo.dd.ScrollManager.register(this.el);
16685         }
16686         if((this.enableDD || this.enableDrop) && !this.dropZone){
16687            /**
16688             * The dropZone used by this tree if drop is enabled
16689             * @type Roo.tree.TreeDropZone
16690             */
16691              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16692                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16693            });
16694         }
16695         if((this.enableDD || this.enableDrag) && !this.dragZone){
16696            /**
16697             * The dragZone used by this tree if drag is enabled
16698             * @type Roo.tree.TreeDragZone
16699             */
16700             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16701                ddGroup: this.ddGroup || "TreeDD",
16702                scroll: this.ddScroll
16703            });
16704         }
16705         this.getSelectionModel().init(this);
16706         if (!this.root) {
16707             console.log("ROOT not set in tree");
16708             return;
16709         }
16710         this.root.render();
16711         if(!this.rootVisible){
16712             this.root.renderChildren();
16713         }
16714         return this;
16715     }
16716 });/*
16717  * Based on:
16718  * Ext JS Library 1.1.1
16719  * Copyright(c) 2006-2007, Ext JS, LLC.
16720  *
16721  * Originally Released Under LGPL - original licence link has changed is not relivant.
16722  *
16723  * Fork - LGPL
16724  * <script type="text/javascript">
16725  */
16726  
16727
16728 /**
16729  * @class Roo.tree.DefaultSelectionModel
16730  * @extends Roo.util.Observable
16731  * The default single selection for a TreePanel.
16732  */
16733 Roo.tree.DefaultSelectionModel = function(){
16734    this.selNode = null;
16735    
16736    this.addEvents({
16737        /**
16738         * @event selectionchange
16739         * Fires when the selected node changes
16740         * @param {DefaultSelectionModel} this
16741         * @param {TreeNode} node the new selection
16742         */
16743        "selectionchange" : true,
16744
16745        /**
16746         * @event beforeselect
16747         * Fires before the selected node changes, return false to cancel the change
16748         * @param {DefaultSelectionModel} this
16749         * @param {TreeNode} node the new selection
16750         * @param {TreeNode} node the old selection
16751         */
16752        "beforeselect" : true
16753    });
16754 };
16755
16756 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16757     init : function(tree){
16758         this.tree = tree;
16759         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16760         tree.on("click", this.onNodeClick, this);
16761     },
16762     
16763     onNodeClick : function(node, e){
16764         if (e.ctrlKey && this.selNode == node)  {
16765             this.unselect(node);
16766             return;
16767         }
16768         this.select(node);
16769     },
16770     
16771     /**
16772      * Select a node.
16773      * @param {TreeNode} node The node to select
16774      * @return {TreeNode} The selected node
16775      */
16776     select : function(node){
16777         var last = this.selNode;
16778         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16779             if(last){
16780                 last.ui.onSelectedChange(false);
16781             }
16782             this.selNode = node;
16783             node.ui.onSelectedChange(true);
16784             this.fireEvent("selectionchange", this, node, last);
16785         }
16786         return node;
16787     },
16788     
16789     /**
16790      * Deselect a node.
16791      * @param {TreeNode} node The node to unselect
16792      */
16793     unselect : function(node){
16794         if(this.selNode == node){
16795             this.clearSelections();
16796         }    
16797     },
16798     
16799     /**
16800      * Clear all selections
16801      */
16802     clearSelections : function(){
16803         var n = this.selNode;
16804         if(n){
16805             n.ui.onSelectedChange(false);
16806             this.selNode = null;
16807             this.fireEvent("selectionchange", this, null);
16808         }
16809         return n;
16810     },
16811     
16812     /**
16813      * Get the selected node
16814      * @return {TreeNode} The selected node
16815      */
16816     getSelectedNode : function(){
16817         return this.selNode;    
16818     },
16819     
16820     /**
16821      * Returns true if the node is selected
16822      * @param {TreeNode} node The node to check
16823      * @return {Boolean}
16824      */
16825     isSelected : function(node){
16826         return this.selNode == node;  
16827     },
16828
16829     /**
16830      * Selects the node above the selected node in the tree, intelligently walking the nodes
16831      * @return TreeNode The new selection
16832      */
16833     selectPrevious : function(){
16834         var s = this.selNode || this.lastSelNode;
16835         if(!s){
16836             return null;
16837         }
16838         var ps = s.previousSibling;
16839         if(ps){
16840             if(!ps.isExpanded() || ps.childNodes.length < 1){
16841                 return this.select(ps);
16842             } else{
16843                 var lc = ps.lastChild;
16844                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16845                     lc = lc.lastChild;
16846                 }
16847                 return this.select(lc);
16848             }
16849         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16850             return this.select(s.parentNode);
16851         }
16852         return null;
16853     },
16854
16855     /**
16856      * Selects the node above the selected node in the tree, intelligently walking the nodes
16857      * @return TreeNode The new selection
16858      */
16859     selectNext : function(){
16860         var s = this.selNode || this.lastSelNode;
16861         if(!s){
16862             return null;
16863         }
16864         if(s.firstChild && s.isExpanded()){
16865              return this.select(s.firstChild);
16866          }else if(s.nextSibling){
16867              return this.select(s.nextSibling);
16868          }else if(s.parentNode){
16869             var newS = null;
16870             s.parentNode.bubble(function(){
16871                 if(this.nextSibling){
16872                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16873                     return false;
16874                 }
16875             });
16876             return newS;
16877          }
16878         return null;
16879     },
16880
16881     onKeyDown : function(e){
16882         var s = this.selNode || this.lastSelNode;
16883         // undesirable, but required
16884         var sm = this;
16885         if(!s){
16886             return;
16887         }
16888         var k = e.getKey();
16889         switch(k){
16890              case e.DOWN:
16891                  e.stopEvent();
16892                  this.selectNext();
16893              break;
16894              case e.UP:
16895                  e.stopEvent();
16896                  this.selectPrevious();
16897              break;
16898              case e.RIGHT:
16899                  e.preventDefault();
16900                  if(s.hasChildNodes()){
16901                      if(!s.isExpanded()){
16902                          s.expand();
16903                      }else if(s.firstChild){
16904                          this.select(s.firstChild, e);
16905                      }
16906                  }
16907              break;
16908              case e.LEFT:
16909                  e.preventDefault();
16910                  if(s.hasChildNodes() && s.isExpanded()){
16911                      s.collapse();
16912                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16913                      this.select(s.parentNode, e);
16914                  }
16915              break;
16916         };
16917     }
16918 });
16919
16920 /**
16921  * @class Roo.tree.MultiSelectionModel
16922  * @extends Roo.util.Observable
16923  * Multi selection for a TreePanel.
16924  */
16925 Roo.tree.MultiSelectionModel = function(){
16926    this.selNodes = [];
16927    this.selMap = {};
16928    this.addEvents({
16929        /**
16930         * @event selectionchange
16931         * Fires when the selected nodes change
16932         * @param {MultiSelectionModel} this
16933         * @param {Array} nodes Array of the selected nodes
16934         */
16935        "selectionchange" : true
16936    });
16937 };
16938
16939 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16940     init : function(tree){
16941         this.tree = tree;
16942         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16943         tree.on("click", this.onNodeClick, this);
16944     },
16945     
16946     onNodeClick : function(node, e){
16947         this.select(node, e, e.ctrlKey);
16948     },
16949     
16950     /**
16951      * Select a node.
16952      * @param {TreeNode} node The node to select
16953      * @param {EventObject} e (optional) An event associated with the selection
16954      * @param {Boolean} keepExisting True to retain existing selections
16955      * @return {TreeNode} The selected node
16956      */
16957     select : function(node, e, keepExisting){
16958         if(keepExisting !== true){
16959             this.clearSelections(true);
16960         }
16961         if(this.isSelected(node)){
16962             this.lastSelNode = node;
16963             return node;
16964         }
16965         this.selNodes.push(node);
16966         this.selMap[node.id] = node;
16967         this.lastSelNode = node;
16968         node.ui.onSelectedChange(true);
16969         this.fireEvent("selectionchange", this, this.selNodes);
16970         return node;
16971     },
16972     
16973     /**
16974      * Deselect a node.
16975      * @param {TreeNode} node The node to unselect
16976      */
16977     unselect : function(node){
16978         if(this.selMap[node.id]){
16979             node.ui.onSelectedChange(false);
16980             var sn = this.selNodes;
16981             var index = -1;
16982             if(sn.indexOf){
16983                 index = sn.indexOf(node);
16984             }else{
16985                 for(var i = 0, len = sn.length; i < len; i++){
16986                     if(sn[i] == node){
16987                         index = i;
16988                         break;
16989                     }
16990                 }
16991             }
16992             if(index != -1){
16993                 this.selNodes.splice(index, 1);
16994             }
16995             delete this.selMap[node.id];
16996             this.fireEvent("selectionchange", this, this.selNodes);
16997         }
16998     },
16999     
17000     /**
17001      * Clear all selections
17002      */
17003     clearSelections : function(suppressEvent){
17004         var sn = this.selNodes;
17005         if(sn.length > 0){
17006             for(var i = 0, len = sn.length; i < len; i++){
17007                 sn[i].ui.onSelectedChange(false);
17008             }
17009             this.selNodes = [];
17010             this.selMap = {};
17011             if(suppressEvent !== true){
17012                 this.fireEvent("selectionchange", this, this.selNodes);
17013             }
17014         }
17015     },
17016     
17017     /**
17018      * Returns true if the node is selected
17019      * @param {TreeNode} node The node to check
17020      * @return {Boolean}
17021      */
17022     isSelected : function(node){
17023         return this.selMap[node.id] ? true : false;  
17024     },
17025     
17026     /**
17027      * Returns an array of the selected nodes
17028      * @return {Array}
17029      */
17030     getSelectedNodes : function(){
17031         return this.selNodes;    
17032     },
17033
17034     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17035
17036     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17037
17038     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17039 });/*
17040  * Based on:
17041  * Ext JS Library 1.1.1
17042  * Copyright(c) 2006-2007, Ext JS, LLC.
17043  *
17044  * Originally Released Under LGPL - original licence link has changed is not relivant.
17045  *
17046  * Fork - LGPL
17047  * <script type="text/javascript">
17048  */
17049  
17050 /**
17051  * @class Roo.tree.TreeNode
17052  * @extends Roo.data.Node
17053  * @cfg {String} text The text for this node
17054  * @cfg {Boolean} expanded true to start the node expanded
17055  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17056  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17057  * @cfg {Boolean} disabled true to start the node disabled
17058  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17059  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17060  * @cfg {String} cls A css class to be added to the node
17061  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17062  * @cfg {String} href URL of the link used for the node (defaults to #)
17063  * @cfg {String} hrefTarget target frame for the link
17064  * @cfg {String} qtip An Ext QuickTip for the node
17065  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17066  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17067  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17068  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17069  * (defaults to undefined with no checkbox rendered)
17070  * @constructor
17071  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17072  */
17073 Roo.tree.TreeNode = function(attributes){
17074     attributes = attributes || {};
17075     if(typeof attributes == "string"){
17076         attributes = {text: attributes};
17077     }
17078     this.childrenRendered = false;
17079     this.rendered = false;
17080     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17081     this.expanded = attributes.expanded === true;
17082     this.isTarget = attributes.isTarget !== false;
17083     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17084     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17085
17086     /**
17087      * Read-only. The text for this node. To change it use setText().
17088      * @type String
17089      */
17090     this.text = attributes.text;
17091     /**
17092      * True if this node is disabled.
17093      * @type Boolean
17094      */
17095     this.disabled = attributes.disabled === true;
17096
17097     this.addEvents({
17098         /**
17099         * @event textchange
17100         * Fires when the text for this node is changed
17101         * @param {Node} this This node
17102         * @param {String} text The new text
17103         * @param {String} oldText The old text
17104         */
17105         "textchange" : true,
17106         /**
17107         * @event beforeexpand
17108         * Fires before this node is expanded, return false to cancel.
17109         * @param {Node} this This node
17110         * @param {Boolean} deep
17111         * @param {Boolean} anim
17112         */
17113         "beforeexpand" : true,
17114         /**
17115         * @event beforecollapse
17116         * Fires before this node is collapsed, return false to cancel.
17117         * @param {Node} this This node
17118         * @param {Boolean} deep
17119         * @param {Boolean} anim
17120         */
17121         "beforecollapse" : true,
17122         /**
17123         * @event expand
17124         * Fires when this node is expanded
17125         * @param {Node} this This node
17126         */
17127         "expand" : true,
17128         /**
17129         * @event disabledchange
17130         * Fires when the disabled status of this node changes
17131         * @param {Node} this This node
17132         * @param {Boolean} disabled
17133         */
17134         "disabledchange" : true,
17135         /**
17136         * @event collapse
17137         * Fires when this node is collapsed
17138         * @param {Node} this This node
17139         */
17140         "collapse" : true,
17141         /**
17142         * @event beforeclick
17143         * Fires before click processing. Return false to cancel the default action.
17144         * @param {Node} this This node
17145         * @param {Roo.EventObject} e The event object
17146         */
17147         "beforeclick":true,
17148         /**
17149         * @event checkchange
17150         * Fires when a node with a checkbox's checked property changes
17151         * @param {Node} this This node
17152         * @param {Boolean} checked
17153         */
17154         "checkchange":true,
17155         /**
17156         * @event click
17157         * Fires when this node is clicked
17158         * @param {Node} this This node
17159         * @param {Roo.EventObject} e The event object
17160         */
17161         "click":true,
17162         /**
17163         * @event dblclick
17164         * Fires when this node is double clicked
17165         * @param {Node} this This node
17166         * @param {Roo.EventObject} e The event object
17167         */
17168         "dblclick":true,
17169         /**
17170         * @event contextmenu
17171         * Fires when this node is right clicked
17172         * @param {Node} this This node
17173         * @param {Roo.EventObject} e The event object
17174         */
17175         "contextmenu":true,
17176         /**
17177         * @event beforechildrenrendered
17178         * Fires right before the child nodes for this node are rendered
17179         * @param {Node} this This node
17180         */
17181         "beforechildrenrendered":true
17182     });
17183
17184     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17185
17186     /**
17187      * Read-only. The UI for this node
17188      * @type TreeNodeUI
17189      */
17190     this.ui = new uiClass(this);
17191 };
17192 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17193     preventHScroll: true,
17194     /**
17195      * Returns true if this node is expanded
17196      * @return {Boolean}
17197      */
17198     isExpanded : function(){
17199         return this.expanded;
17200     },
17201
17202     /**
17203      * Returns the UI object for this node
17204      * @return {TreeNodeUI}
17205      */
17206     getUI : function(){
17207         return this.ui;
17208     },
17209
17210     // private override
17211     setFirstChild : function(node){
17212         var of = this.firstChild;
17213         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17214         if(this.childrenRendered && of && node != of){
17215             of.renderIndent(true, true);
17216         }
17217         if(this.rendered){
17218             this.renderIndent(true, true);
17219         }
17220     },
17221
17222     // private override
17223     setLastChild : function(node){
17224         var ol = this.lastChild;
17225         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17226         if(this.childrenRendered && ol && node != ol){
17227             ol.renderIndent(true, true);
17228         }
17229         if(this.rendered){
17230             this.renderIndent(true, true);
17231         }
17232     },
17233
17234     // these methods are overridden to provide lazy rendering support
17235     // private override
17236     appendChild : function(){
17237         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17238         if(node && this.childrenRendered){
17239             node.render();
17240         }
17241         this.ui.updateExpandIcon();
17242         return node;
17243     },
17244
17245     // private override
17246     removeChild : function(node){
17247         this.ownerTree.getSelectionModel().unselect(node);
17248         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17249         // if it's been rendered remove dom node
17250         if(this.childrenRendered){
17251             node.ui.remove();
17252         }
17253         if(this.childNodes.length < 1){
17254             this.collapse(false, false);
17255         }else{
17256             this.ui.updateExpandIcon();
17257         }
17258         if(!this.firstChild) {
17259             this.childrenRendered = false;
17260         }
17261         return node;
17262     },
17263
17264     // private override
17265     insertBefore : function(node, refNode){
17266         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17267         if(newNode && refNode && this.childrenRendered){
17268             node.render();
17269         }
17270         this.ui.updateExpandIcon();
17271         return newNode;
17272     },
17273
17274     /**
17275      * Sets the text for this node
17276      * @param {String} text
17277      */
17278     setText : function(text){
17279         var oldText = this.text;
17280         this.text = text;
17281         this.attributes.text = text;
17282         if(this.rendered){ // event without subscribing
17283             this.ui.onTextChange(this, text, oldText);
17284         }
17285         this.fireEvent("textchange", this, text, oldText);
17286     },
17287
17288     /**
17289      * Triggers selection of this node
17290      */
17291     select : function(){
17292         this.getOwnerTree().getSelectionModel().select(this);
17293     },
17294
17295     /**
17296      * Triggers deselection of this node
17297      */
17298     unselect : function(){
17299         this.getOwnerTree().getSelectionModel().unselect(this);
17300     },
17301
17302     /**
17303      * Returns true if this node is selected
17304      * @return {Boolean}
17305      */
17306     isSelected : function(){
17307         return this.getOwnerTree().getSelectionModel().isSelected(this);
17308     },
17309
17310     /**
17311      * Expand this node.
17312      * @param {Boolean} deep (optional) True to expand all children as well
17313      * @param {Boolean} anim (optional) false to cancel the default animation
17314      * @param {Function} callback (optional) A callback to be called when
17315      * expanding this node completes (does not wait for deep expand to complete).
17316      * Called with 1 parameter, this node.
17317      */
17318     expand : function(deep, anim, callback){
17319         if(!this.expanded){
17320             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17321                 return;
17322             }
17323             if(!this.childrenRendered){
17324                 this.renderChildren();
17325             }
17326             this.expanded = true;
17327             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17328                 this.ui.animExpand(function(){
17329                     this.fireEvent("expand", this);
17330                     if(typeof callback == "function"){
17331                         callback(this);
17332                     }
17333                     if(deep === true){
17334                         this.expandChildNodes(true);
17335                     }
17336                 }.createDelegate(this));
17337                 return;
17338             }else{
17339                 this.ui.expand();
17340                 this.fireEvent("expand", this);
17341                 if(typeof callback == "function"){
17342                     callback(this);
17343                 }
17344             }
17345         }else{
17346            if(typeof callback == "function"){
17347                callback(this);
17348            }
17349         }
17350         if(deep === true){
17351             this.expandChildNodes(true);
17352         }
17353     },
17354
17355     isHiddenRoot : function(){
17356         return this.isRoot && !this.getOwnerTree().rootVisible;
17357     },
17358
17359     /**
17360      * Collapse this node.
17361      * @param {Boolean} deep (optional) True to collapse all children as well
17362      * @param {Boolean} anim (optional) false to cancel the default animation
17363      */
17364     collapse : function(deep, anim){
17365         if(this.expanded && !this.isHiddenRoot()){
17366             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17367                 return;
17368             }
17369             this.expanded = false;
17370             if((this.getOwnerTree().animate && anim !== false) || anim){
17371                 this.ui.animCollapse(function(){
17372                     this.fireEvent("collapse", this);
17373                     if(deep === true){
17374                         this.collapseChildNodes(true);
17375                     }
17376                 }.createDelegate(this));
17377                 return;
17378             }else{
17379                 this.ui.collapse();
17380                 this.fireEvent("collapse", this);
17381             }
17382         }
17383         if(deep === true){
17384             var cs = this.childNodes;
17385             for(var i = 0, len = cs.length; i < len; i++) {
17386                 cs[i].collapse(true, false);
17387             }
17388         }
17389     },
17390
17391     // private
17392     delayedExpand : function(delay){
17393         if(!this.expandProcId){
17394             this.expandProcId = this.expand.defer(delay, this);
17395         }
17396     },
17397
17398     // private
17399     cancelExpand : function(){
17400         if(this.expandProcId){
17401             clearTimeout(this.expandProcId);
17402         }
17403         this.expandProcId = false;
17404     },
17405
17406     /**
17407      * Toggles expanded/collapsed state of the node
17408      */
17409     toggle : function(){
17410         if(this.expanded){
17411             this.collapse();
17412         }else{
17413             this.expand();
17414         }
17415     },
17416
17417     /**
17418      * Ensures all parent nodes are expanded
17419      */
17420     ensureVisible : function(callback){
17421         var tree = this.getOwnerTree();
17422         tree.expandPath(this.parentNode.getPath(), false, function(){
17423             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17424             Roo.callback(callback);
17425         }.createDelegate(this));
17426     },
17427
17428     /**
17429      * Expand all child nodes
17430      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17431      */
17432     expandChildNodes : function(deep){
17433         var cs = this.childNodes;
17434         for(var i = 0, len = cs.length; i < len; i++) {
17435                 cs[i].expand(deep);
17436         }
17437     },
17438
17439     /**
17440      * Collapse all child nodes
17441      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17442      */
17443     collapseChildNodes : function(deep){
17444         var cs = this.childNodes;
17445         for(var i = 0, len = cs.length; i < len; i++) {
17446                 cs[i].collapse(deep);
17447         }
17448     },
17449
17450     /**
17451      * Disables this node
17452      */
17453     disable : function(){
17454         this.disabled = true;
17455         this.unselect();
17456         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17457             this.ui.onDisableChange(this, true);
17458         }
17459         this.fireEvent("disabledchange", this, true);
17460     },
17461
17462     /**
17463      * Enables this node
17464      */
17465     enable : function(){
17466         this.disabled = false;
17467         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17468             this.ui.onDisableChange(this, false);
17469         }
17470         this.fireEvent("disabledchange", this, false);
17471     },
17472
17473     // private
17474     renderChildren : function(suppressEvent){
17475         if(suppressEvent !== false){
17476             this.fireEvent("beforechildrenrendered", this);
17477         }
17478         var cs = this.childNodes;
17479         for(var i = 0, len = cs.length; i < len; i++){
17480             cs[i].render(true);
17481         }
17482         this.childrenRendered = true;
17483     },
17484
17485     // private
17486     sort : function(fn, scope){
17487         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17488         if(this.childrenRendered){
17489             var cs = this.childNodes;
17490             for(var i = 0, len = cs.length; i < len; i++){
17491                 cs[i].render(true);
17492             }
17493         }
17494     },
17495
17496     // private
17497     render : function(bulkRender){
17498         this.ui.render(bulkRender);
17499         if(!this.rendered){
17500             this.rendered = true;
17501             if(this.expanded){
17502                 this.expanded = false;
17503                 this.expand(false, false);
17504             }
17505         }
17506     },
17507
17508     // private
17509     renderIndent : function(deep, refresh){
17510         if(refresh){
17511             this.ui.childIndent = null;
17512         }
17513         this.ui.renderIndent();
17514         if(deep === true && this.childrenRendered){
17515             var cs = this.childNodes;
17516             for(var i = 0, len = cs.length; i < len; i++){
17517                 cs[i].renderIndent(true, refresh);
17518             }
17519         }
17520     }
17521 });/*
17522  * Based on:
17523  * Ext JS Library 1.1.1
17524  * Copyright(c) 2006-2007, Ext JS, LLC.
17525  *
17526  * Originally Released Under LGPL - original licence link has changed is not relivant.
17527  *
17528  * Fork - LGPL
17529  * <script type="text/javascript">
17530  */
17531  
17532 /**
17533  * @class Roo.tree.AsyncTreeNode
17534  * @extends Roo.tree.TreeNode
17535  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17536  * @constructor
17537  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17538  */
17539  Roo.tree.AsyncTreeNode = function(config){
17540     this.loaded = false;
17541     this.loading = false;
17542     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17543     /**
17544     * @event beforeload
17545     * Fires before this node is loaded, return false to cancel
17546     * @param {Node} this This node
17547     */
17548     this.addEvents({'beforeload':true, 'load': true});
17549     /**
17550     * @event load
17551     * Fires when this node is loaded
17552     * @param {Node} this This node
17553     */
17554     /**
17555      * The loader used by this node (defaults to using the tree's defined loader)
17556      * @type TreeLoader
17557      * @property loader
17558      */
17559 };
17560 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17561     expand : function(deep, anim, callback){
17562         if(this.loading){ // if an async load is already running, waiting til it's done
17563             var timer;
17564             var f = function(){
17565                 if(!this.loading){ // done loading
17566                     clearInterval(timer);
17567                     this.expand(deep, anim, callback);
17568                 }
17569             }.createDelegate(this);
17570             timer = setInterval(f, 200);
17571             return;
17572         }
17573         if(!this.loaded){
17574             if(this.fireEvent("beforeload", this) === false){
17575                 return;
17576             }
17577             this.loading = true;
17578             this.ui.beforeLoad(this);
17579             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17580             if(loader){
17581                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17582                 return;
17583             }
17584         }
17585         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17586     },
17587     
17588     /**
17589      * Returns true if this node is currently loading
17590      * @return {Boolean}
17591      */
17592     isLoading : function(){
17593         return this.loading;  
17594     },
17595     
17596     loadComplete : function(deep, anim, callback){
17597         this.loading = false;
17598         this.loaded = true;
17599         this.ui.afterLoad(this);
17600         this.fireEvent("load", this);
17601         this.expand(deep, anim, callback);
17602     },
17603     
17604     /**
17605      * Returns true if this node has been loaded
17606      * @return {Boolean}
17607      */
17608     isLoaded : function(){
17609         return this.loaded;
17610     },
17611     
17612     hasChildNodes : function(){
17613         if(!this.isLeaf() && !this.loaded){
17614             return true;
17615         }else{
17616             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17617         }
17618     },
17619
17620     /**
17621      * Trigger a reload for this node
17622      * @param {Function} callback
17623      */
17624     reload : function(callback){
17625         this.collapse(false, false);
17626         while(this.firstChild){
17627             this.removeChild(this.firstChild);
17628         }
17629         this.childrenRendered = false;
17630         this.loaded = false;
17631         if(this.isHiddenRoot()){
17632             this.expanded = false;
17633         }
17634         this.expand(false, false, callback);
17635     }
17636 });/*
17637  * Based on:
17638  * Ext JS Library 1.1.1
17639  * Copyright(c) 2006-2007, Ext JS, LLC.
17640  *
17641  * Originally Released Under LGPL - original licence link has changed is not relivant.
17642  *
17643  * Fork - LGPL
17644  * <script type="text/javascript">
17645  */
17646  
17647 /**
17648  * @class Roo.tree.TreeNodeUI
17649  * @constructor
17650  * @param {Object} node The node to render
17651  * The TreeNode UI implementation is separate from the
17652  * tree implementation. Unless you are customizing the tree UI,
17653  * you should never have to use this directly.
17654  */
17655 Roo.tree.TreeNodeUI = function(node){
17656     this.node = node;
17657     this.rendered = false;
17658     this.animating = false;
17659     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17660 };
17661
17662 Roo.tree.TreeNodeUI.prototype = {
17663     removeChild : function(node){
17664         if(this.rendered){
17665             this.ctNode.removeChild(node.ui.getEl());
17666         }
17667     },
17668
17669     beforeLoad : function(){
17670          this.addClass("x-tree-node-loading");
17671     },
17672
17673     afterLoad : function(){
17674          this.removeClass("x-tree-node-loading");
17675     },
17676
17677     onTextChange : function(node, text, oldText){
17678         if(this.rendered){
17679             this.textNode.innerHTML = text;
17680         }
17681     },
17682
17683     onDisableChange : function(node, state){
17684         this.disabled = state;
17685         if(state){
17686             this.addClass("x-tree-node-disabled");
17687         }else{
17688             this.removeClass("x-tree-node-disabled");
17689         }
17690     },
17691
17692     onSelectedChange : function(state){
17693         if(state){
17694             this.focus();
17695             this.addClass("x-tree-selected");
17696         }else{
17697             //this.blur();
17698             this.removeClass("x-tree-selected");
17699         }
17700     },
17701
17702     onMove : function(tree, node, oldParent, newParent, index, refNode){
17703         this.childIndent = null;
17704         if(this.rendered){
17705             var targetNode = newParent.ui.getContainer();
17706             if(!targetNode){//target not rendered
17707                 this.holder = document.createElement("div");
17708                 this.holder.appendChild(this.wrap);
17709                 return;
17710             }
17711             var insertBefore = refNode ? refNode.ui.getEl() : null;
17712             if(insertBefore){
17713                 targetNode.insertBefore(this.wrap, insertBefore);
17714             }else{
17715                 targetNode.appendChild(this.wrap);
17716             }
17717             this.node.renderIndent(true);
17718         }
17719     },
17720
17721     addClass : function(cls){
17722         if(this.elNode){
17723             Roo.fly(this.elNode).addClass(cls);
17724         }
17725     },
17726
17727     removeClass : function(cls){
17728         if(this.elNode){
17729             Roo.fly(this.elNode).removeClass(cls);
17730         }
17731     },
17732
17733     remove : function(){
17734         if(this.rendered){
17735             this.holder = document.createElement("div");
17736             this.holder.appendChild(this.wrap);
17737         }
17738     },
17739
17740     fireEvent : function(){
17741         return this.node.fireEvent.apply(this.node, arguments);
17742     },
17743
17744     initEvents : function(){
17745         this.node.on("move", this.onMove, this);
17746         var E = Roo.EventManager;
17747         var a = this.anchor;
17748
17749         var el = Roo.fly(a, '_treeui');
17750
17751         if(Roo.isOpera){ // opera render bug ignores the CSS
17752             el.setStyle("text-decoration", "none");
17753         }
17754
17755         el.on("click", this.onClick, this);
17756         el.on("dblclick", this.onDblClick, this);
17757
17758         if(this.checkbox){
17759             Roo.EventManager.on(this.checkbox,
17760                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17761         }
17762
17763         el.on("contextmenu", this.onContextMenu, this);
17764
17765         var icon = Roo.fly(this.iconNode);
17766         icon.on("click", this.onClick, this);
17767         icon.on("dblclick", this.onDblClick, this);
17768         icon.on("contextmenu", this.onContextMenu, this);
17769         E.on(this.ecNode, "click", this.ecClick, this, true);
17770
17771         if(this.node.disabled){
17772             this.addClass("x-tree-node-disabled");
17773         }
17774         if(this.node.hidden){
17775             this.addClass("x-tree-node-disabled");
17776         }
17777         var ot = this.node.getOwnerTree();
17778         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17779         if(dd && (!this.node.isRoot || ot.rootVisible)){
17780             Roo.dd.Registry.register(this.elNode, {
17781                 node: this.node,
17782                 handles: this.getDDHandles(),
17783                 isHandle: false
17784             });
17785         }
17786     },
17787
17788     getDDHandles : function(){
17789         return [this.iconNode, this.textNode];
17790     },
17791
17792     hide : function(){
17793         if(this.rendered){
17794             this.wrap.style.display = "none";
17795         }
17796     },
17797
17798     show : function(){
17799         if(this.rendered){
17800             this.wrap.style.display = "";
17801         }
17802     },
17803
17804     onContextMenu : function(e){
17805         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17806             e.preventDefault();
17807             this.focus();
17808             this.fireEvent("contextmenu", this.node, e);
17809         }
17810     },
17811
17812     onClick : function(e){
17813         if(this.dropping){
17814             e.stopEvent();
17815             return;
17816         }
17817         if(this.fireEvent("beforeclick", this.node, e) !== false){
17818             if(!this.disabled && this.node.attributes.href){
17819                 this.fireEvent("click", this.node, e);
17820                 return;
17821             }
17822             e.preventDefault();
17823             if(this.disabled){
17824                 return;
17825             }
17826
17827             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17828                 this.node.toggle();
17829             }
17830
17831             this.fireEvent("click", this.node, e);
17832         }else{
17833             e.stopEvent();
17834         }
17835     },
17836
17837     onDblClick : function(e){
17838         e.preventDefault();
17839         if(this.disabled){
17840             return;
17841         }
17842         if(this.checkbox){
17843             this.toggleCheck();
17844         }
17845         if(!this.animating && this.node.hasChildNodes()){
17846             this.node.toggle();
17847         }
17848         this.fireEvent("dblclick", this.node, e);
17849     },
17850
17851     onCheckChange : function(){
17852         var checked = this.checkbox.checked;
17853         this.node.attributes.checked = checked;
17854         this.fireEvent('checkchange', this.node, checked);
17855     },
17856
17857     ecClick : function(e){
17858         if(!this.animating && this.node.hasChildNodes()){
17859             this.node.toggle();
17860         }
17861     },
17862
17863     startDrop : function(){
17864         this.dropping = true;
17865     },
17866
17867     // delayed drop so the click event doesn't get fired on a drop
17868     endDrop : function(){
17869        setTimeout(function(){
17870            this.dropping = false;
17871        }.createDelegate(this), 50);
17872     },
17873
17874     expand : function(){
17875         this.updateExpandIcon();
17876         this.ctNode.style.display = "";
17877     },
17878
17879     focus : function(){
17880         if(!this.node.preventHScroll){
17881             try{this.anchor.focus();
17882             }catch(e){}
17883         }else if(!Roo.isIE){
17884             try{
17885                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17886                 var l = noscroll.scrollLeft;
17887                 this.anchor.focus();
17888                 noscroll.scrollLeft = l;
17889             }catch(e){}
17890         }
17891     },
17892
17893     toggleCheck : function(value){
17894         var cb = this.checkbox;
17895         if(cb){
17896             cb.checked = (value === undefined ? !cb.checked : value);
17897         }
17898     },
17899
17900     blur : function(){
17901         try{
17902             this.anchor.blur();
17903         }catch(e){}
17904     },
17905
17906     animExpand : function(callback){
17907         var ct = Roo.get(this.ctNode);
17908         ct.stopFx();
17909         if(!this.node.hasChildNodes()){
17910             this.updateExpandIcon();
17911             this.ctNode.style.display = "";
17912             Roo.callback(callback);
17913             return;
17914         }
17915         this.animating = true;
17916         this.updateExpandIcon();
17917
17918         ct.slideIn('t', {
17919            callback : function(){
17920                this.animating = false;
17921                Roo.callback(callback);
17922             },
17923             scope: this,
17924             duration: this.node.ownerTree.duration || .25
17925         });
17926     },
17927
17928     highlight : function(){
17929         var tree = this.node.getOwnerTree();
17930         Roo.fly(this.wrap).highlight(
17931             tree.hlColor || "C3DAF9",
17932             {endColor: tree.hlBaseColor}
17933         );
17934     },
17935
17936     collapse : function(){
17937         this.updateExpandIcon();
17938         this.ctNode.style.display = "none";
17939     },
17940
17941     animCollapse : function(callback){
17942         var ct = Roo.get(this.ctNode);
17943         ct.enableDisplayMode('block');
17944         ct.stopFx();
17945
17946         this.animating = true;
17947         this.updateExpandIcon();
17948
17949         ct.slideOut('t', {
17950             callback : function(){
17951                this.animating = false;
17952                Roo.callback(callback);
17953             },
17954             scope: this,
17955             duration: this.node.ownerTree.duration || .25
17956         });
17957     },
17958
17959     getContainer : function(){
17960         return this.ctNode;
17961     },
17962
17963     getEl : function(){
17964         return this.wrap;
17965     },
17966
17967     appendDDGhost : function(ghostNode){
17968         ghostNode.appendChild(this.elNode.cloneNode(true));
17969     },
17970
17971     getDDRepairXY : function(){
17972         return Roo.lib.Dom.getXY(this.iconNode);
17973     },
17974
17975     onRender : function(){
17976         this.render();
17977     },
17978
17979     render : function(bulkRender){
17980         var n = this.node, a = n.attributes;
17981         var targetNode = n.parentNode ?
17982               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17983
17984         if(!this.rendered){
17985             this.rendered = true;
17986
17987             this.renderElements(n, a, targetNode, bulkRender);
17988
17989             if(a.qtip){
17990                if(this.textNode.setAttributeNS){
17991                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17992                    if(a.qtipTitle){
17993                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
17994                    }
17995                }else{
17996                    this.textNode.setAttribute("ext:qtip", a.qtip);
17997                    if(a.qtipTitle){
17998                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
17999                    }
18000                }
18001             }else if(a.qtipCfg){
18002                 a.qtipCfg.target = Roo.id(this.textNode);
18003                 Roo.QuickTips.register(a.qtipCfg);
18004             }
18005             this.initEvents();
18006             if(!this.node.expanded){
18007                 this.updateExpandIcon();
18008             }
18009         }else{
18010             if(bulkRender === true) {
18011                 targetNode.appendChild(this.wrap);
18012             }
18013         }
18014     },
18015
18016     renderElements : function(n, a, targetNode, bulkRender){
18017         // add some indent caching, this helps performance when rendering a large tree
18018         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18019         var t = n.getOwnerTree();
18020         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18021         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18022         var cb = typeof a.checked == 'boolean';
18023         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18024         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18025             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18026             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18027             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18028             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18029             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18030              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18031                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18032             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18033             "</li>"];
18034
18035         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18036             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18037                                 n.nextSibling.ui.getEl(), buf.join(""));
18038         }else{
18039             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18040         }
18041
18042         this.elNode = this.wrap.childNodes[0];
18043         this.ctNode = this.wrap.childNodes[1];
18044         var cs = this.elNode.childNodes;
18045         this.indentNode = cs[0];
18046         this.ecNode = cs[1];
18047         this.iconNode = cs[2];
18048         var index = 3;
18049         if(cb){
18050             this.checkbox = cs[3];
18051             index++;
18052         }
18053         this.anchor = cs[index];
18054         this.textNode = cs[index].firstChild;
18055     },
18056
18057     getAnchor : function(){
18058         return this.anchor;
18059     },
18060
18061     getTextEl : function(){
18062         return this.textNode;
18063     },
18064
18065     getIconEl : function(){
18066         return this.iconNode;
18067     },
18068
18069     isChecked : function(){
18070         return this.checkbox ? this.checkbox.checked : false;
18071     },
18072
18073     updateExpandIcon : function(){
18074         if(this.rendered){
18075             var n = this.node, c1, c2;
18076             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18077             var hasChild = n.hasChildNodes();
18078             if(hasChild){
18079                 if(n.expanded){
18080                     cls += "-minus";
18081                     c1 = "x-tree-node-collapsed";
18082                     c2 = "x-tree-node-expanded";
18083                 }else{
18084                     cls += "-plus";
18085                     c1 = "x-tree-node-expanded";
18086                     c2 = "x-tree-node-collapsed";
18087                 }
18088                 if(this.wasLeaf){
18089                     this.removeClass("x-tree-node-leaf");
18090                     this.wasLeaf = false;
18091                 }
18092                 if(this.c1 != c1 || this.c2 != c2){
18093                     Roo.fly(this.elNode).replaceClass(c1, c2);
18094                     this.c1 = c1; this.c2 = c2;
18095                 }
18096             }else{
18097                 if(!this.wasLeaf){
18098                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18099                     delete this.c1;
18100                     delete this.c2;
18101                     this.wasLeaf = true;
18102                 }
18103             }
18104             var ecc = "x-tree-ec-icon "+cls;
18105             if(this.ecc != ecc){
18106                 this.ecNode.className = ecc;
18107                 this.ecc = ecc;
18108             }
18109         }
18110     },
18111
18112     getChildIndent : function(){
18113         if(!this.childIndent){
18114             var buf = [];
18115             var p = this.node;
18116             while(p){
18117                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18118                     if(!p.isLast()) {
18119                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18120                     } else {
18121                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18122                     }
18123                 }
18124                 p = p.parentNode;
18125             }
18126             this.childIndent = buf.join("");
18127         }
18128         return this.childIndent;
18129     },
18130
18131     renderIndent : function(){
18132         if(this.rendered){
18133             var indent = "";
18134             var p = this.node.parentNode;
18135             if(p){
18136                 indent = p.ui.getChildIndent();
18137             }
18138             if(this.indentMarkup != indent){ // don't rerender if not required
18139                 this.indentNode.innerHTML = indent;
18140                 this.indentMarkup = indent;
18141             }
18142             this.updateExpandIcon();
18143         }
18144     }
18145 };
18146
18147 Roo.tree.RootTreeNodeUI = function(){
18148     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18149 };
18150 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18151     render : function(){
18152         if(!this.rendered){
18153             var targetNode = this.node.ownerTree.innerCt.dom;
18154             this.node.expanded = true;
18155             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18156             this.wrap = this.ctNode = targetNode.firstChild;
18157         }
18158     },
18159     collapse : function(){
18160     },
18161     expand : function(){
18162     }
18163 });/*
18164  * Based on:
18165  * Ext JS Library 1.1.1
18166  * Copyright(c) 2006-2007, Ext JS, LLC.
18167  *
18168  * Originally Released Under LGPL - original licence link has changed is not relivant.
18169  *
18170  * Fork - LGPL
18171  * <script type="text/javascript">
18172  */
18173 /**
18174  * @class Roo.tree.TreeLoader
18175  * @extends Roo.util.Observable
18176  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18177  * nodes from a specified URL. The response must be a javascript Array definition
18178  * who's elements are node definition objects. eg:
18179  * <pre><code>
18180    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18181     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18182 </code></pre>
18183  * <br><br>
18184  * A server request is sent, and child nodes are loaded only when a node is expanded.
18185  * The loading node's id is passed to the server under the parameter name "node" to
18186  * enable the server to produce the correct child nodes.
18187  * <br><br>
18188  * To pass extra parameters, an event handler may be attached to the "beforeload"
18189  * event, and the parameters specified in the TreeLoader's baseParams property:
18190  * <pre><code>
18191     myTreeLoader.on("beforeload", function(treeLoader, node) {
18192         this.baseParams.category = node.attributes.category;
18193     }, this);
18194 </code></pre><
18195  * This would pass an HTTP parameter called "category" to the server containing
18196  * the value of the Node's "category" attribute.
18197  * @constructor
18198  * Creates a new Treeloader.
18199  * @param {Object} config A config object containing config properties.
18200  */
18201 Roo.tree.TreeLoader = function(config){
18202     this.baseParams = {};
18203     this.requestMethod = "POST";
18204     Roo.apply(this, config);
18205
18206     this.addEvents({
18207     
18208         /**
18209          * @event beforeload
18210          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18211          * @param {Object} This TreeLoader object.
18212          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18213          * @param {Object} callback The callback function specified in the {@link #load} call.
18214          */
18215         beforeload : true,
18216         /**
18217          * @event load
18218          * Fires when the node has been successfuly loaded.
18219          * @param {Object} This TreeLoader object.
18220          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18221          * @param {Object} response The response object containing the data from the server.
18222          */
18223         load : true,
18224         /**
18225          * @event loadexception
18226          * Fires if the network request failed.
18227          * @param {Object} This TreeLoader object.
18228          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18229          * @param {Object} response The response object containing the data from the server.
18230          */
18231         loadexception : true,
18232         /**
18233          * @event create
18234          * Fires before a node is created, enabling you to return custom Node types 
18235          * @param {Object} This TreeLoader object.
18236          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18237          */
18238         create : true
18239     });
18240
18241     Roo.tree.TreeLoader.superclass.constructor.call(this);
18242 };
18243
18244 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18245     /**
18246     * @cfg {String} dataUrl The URL from which to request a Json string which
18247     * specifies an array of node definition object representing the child nodes
18248     * to be loaded.
18249     */
18250     /**
18251     * @cfg {Object} baseParams (optional) An object containing properties which
18252     * specify HTTP parameters to be passed to each request for child nodes.
18253     */
18254     /**
18255     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18256     * created by this loader. If the attributes sent by the server have an attribute in this object,
18257     * they take priority.
18258     */
18259     /**
18260     * @cfg {Object} uiProviders (optional) An object containing properties which
18261     * 
18262     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18263     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18264     * <i>uiProvider</i> attribute of a returned child node is a string rather
18265     * than a reference to a TreeNodeUI implementation, this that string value
18266     * is used as a property name in the uiProviders object. You can define the provider named
18267     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18268     */
18269     uiProviders : {},
18270
18271     /**
18272     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18273     * child nodes before loading.
18274     */
18275     clearOnLoad : true,
18276
18277     /**
18278     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18279     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18280     * Grid query { data : [ .....] }
18281     */
18282     
18283     root : false,
18284      /**
18285     * @cfg {String} queryParam (optional) 
18286     * Name of the query as it will be passed on the querystring (defaults to 'node')
18287     * eg. the request will be ?node=[id]
18288     */
18289     
18290     
18291     queryParam: false,
18292     
18293     /**
18294      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18295      * This is called automatically when a node is expanded, but may be used to reload
18296      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18297      * @param {Roo.tree.TreeNode} node
18298      * @param {Function} callback
18299      */
18300     load : function(node, callback){
18301         if(this.clearOnLoad){
18302             while(node.firstChild){
18303                 node.removeChild(node.firstChild);
18304             }
18305         }
18306         if(node.attributes.children){ // preloaded json children
18307             var cs = node.attributes.children;
18308             for(var i = 0, len = cs.length; i < len; i++){
18309                 node.appendChild(this.createNode(cs[i]));
18310             }
18311             if(typeof callback == "function"){
18312                 callback();
18313             }
18314         }else if(this.dataUrl){
18315             this.requestData(node, callback);
18316         }
18317     },
18318
18319     getParams: function(node){
18320         var buf = [], bp = this.baseParams;
18321         for(var key in bp){
18322             if(typeof bp[key] != "function"){
18323                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18324             }
18325         }
18326         var n = this.queryParam === false ? 'node' : this.queryParam;
18327         buf.push(n + "=", encodeURIComponent(node.id));
18328         return buf.join("");
18329     },
18330
18331     requestData : function(node, callback){
18332         if(this.fireEvent("beforeload", this, node, callback) !== false){
18333             this.transId = Roo.Ajax.request({
18334                 method:this.requestMethod,
18335                 url: this.dataUrl||this.url,
18336                 success: this.handleResponse,
18337                 failure: this.handleFailure,
18338                 scope: this,
18339                 argument: {callback: callback, node: node},
18340                 params: this.getParams(node)
18341             });
18342         }else{
18343             // if the load is cancelled, make sure we notify
18344             // the node that we are done
18345             if(typeof callback == "function"){
18346                 callback();
18347             }
18348         }
18349     },
18350
18351     isLoading : function(){
18352         return this.transId ? true : false;
18353     },
18354
18355     abort : function(){
18356         if(this.isLoading()){
18357             Roo.Ajax.abort(this.transId);
18358         }
18359     },
18360
18361     // private
18362     createNode : function(attr){
18363         // apply baseAttrs, nice idea Corey!
18364         if(this.baseAttrs){
18365             Roo.applyIf(attr, this.baseAttrs);
18366         }
18367         if(this.applyLoader !== false){
18368             attr.loader = this;
18369         }
18370         // uiProvider = depreciated..
18371         
18372         if(typeof(attr.uiProvider) == 'string'){
18373            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18374                 /**  eval:var:attr */ eval(attr.uiProvider);
18375         }
18376         if(typeof(this.uiProviders['default']) != 'undefined') {
18377             attr.uiProvider = this.uiProviders['default'];
18378         }
18379         
18380         this.fireEvent('create', this, attr);
18381         
18382         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18383         return(attr.leaf ?
18384                         new Roo.tree.TreeNode(attr) :
18385                         new Roo.tree.AsyncTreeNode(attr));
18386     },
18387
18388     processResponse : function(response, node, callback){
18389         var json = response.responseText;
18390         try {
18391             
18392             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18393             if (this.root !== false) {
18394                 o = o[this.root];
18395             }
18396             
18397             for(var i = 0, len = o.length; i < len; i++){
18398                 var n = this.createNode(o[i]);
18399                 if(n){
18400                     node.appendChild(n);
18401                 }
18402             }
18403             if(typeof callback == "function"){
18404                 callback(this, node);
18405             }
18406         }catch(e){
18407             this.handleFailure(response);
18408         }
18409     },
18410
18411     handleResponse : function(response){
18412         this.transId = false;
18413         var a = response.argument;
18414         this.processResponse(response, a.node, a.callback);
18415         this.fireEvent("load", this, a.node, response);
18416     },
18417
18418     handleFailure : function(response){
18419         this.transId = false;
18420         var a = response.argument;
18421         this.fireEvent("loadexception", this, a.node, response);
18422         if(typeof a.callback == "function"){
18423             a.callback(this, a.node);
18424         }
18425     }
18426 });/*
18427  * Based on:
18428  * Ext JS Library 1.1.1
18429  * Copyright(c) 2006-2007, Ext JS, LLC.
18430  *
18431  * Originally Released Under LGPL - original licence link has changed is not relivant.
18432  *
18433  * Fork - LGPL
18434  * <script type="text/javascript">
18435  */
18436
18437 /**
18438 * @class Roo.tree.TreeFilter
18439 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18440 * @param {TreePanel} tree
18441 * @param {Object} config (optional)
18442  */
18443 Roo.tree.TreeFilter = function(tree, config){
18444     this.tree = tree;
18445     this.filtered = {};
18446     Roo.apply(this, config);
18447 };
18448
18449 Roo.tree.TreeFilter.prototype = {
18450     clearBlank:false,
18451     reverse:false,
18452     autoClear:false,
18453     remove:false,
18454
18455      /**
18456      * Filter the data by a specific attribute.
18457      * @param {String/RegExp} value Either string that the attribute value
18458      * should start with or a RegExp to test against the attribute
18459      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18460      * @param {TreeNode} startNode (optional) The node to start the filter at.
18461      */
18462     filter : function(value, attr, startNode){
18463         attr = attr || "text";
18464         var f;
18465         if(typeof value == "string"){
18466             var vlen = value.length;
18467             // auto clear empty filter
18468             if(vlen == 0 && this.clearBlank){
18469                 this.clear();
18470                 return;
18471             }
18472             value = value.toLowerCase();
18473             f = function(n){
18474                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18475             };
18476         }else if(value.exec){ // regex?
18477             f = function(n){
18478                 return value.test(n.attributes[attr]);
18479             };
18480         }else{
18481             throw 'Illegal filter type, must be string or regex';
18482         }
18483         this.filterBy(f, null, startNode);
18484         },
18485
18486     /**
18487      * Filter by a function. The passed function will be called with each
18488      * node in the tree (or from the startNode). If the function returns true, the node is kept
18489      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18490      * @param {Function} fn The filter function
18491      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18492      */
18493     filterBy : function(fn, scope, startNode){
18494         startNode = startNode || this.tree.root;
18495         if(this.autoClear){
18496             this.clear();
18497         }
18498         var af = this.filtered, rv = this.reverse;
18499         var f = function(n){
18500             if(n == startNode){
18501                 return true;
18502             }
18503             if(af[n.id]){
18504                 return false;
18505             }
18506             var m = fn.call(scope || n, n);
18507             if(!m || rv){
18508                 af[n.id] = n;
18509                 n.ui.hide();
18510                 return false;
18511             }
18512             return true;
18513         };
18514         startNode.cascade(f);
18515         if(this.remove){
18516            for(var id in af){
18517                if(typeof id != "function"){
18518                    var n = af[id];
18519                    if(n && n.parentNode){
18520                        n.parentNode.removeChild(n);
18521                    }
18522                }
18523            }
18524         }
18525     },
18526
18527     /**
18528      * Clears the current filter. Note: with the "remove" option
18529      * set a filter cannot be cleared.
18530      */
18531     clear : function(){
18532         var t = this.tree;
18533         var af = this.filtered;
18534         for(var id in af){
18535             if(typeof id != "function"){
18536                 var n = af[id];
18537                 if(n){
18538                     n.ui.show();
18539                 }
18540             }
18541         }
18542         this.filtered = {};
18543     }
18544 };
18545 /*
18546  * Based on:
18547  * Ext JS Library 1.1.1
18548  * Copyright(c) 2006-2007, Ext JS, LLC.
18549  *
18550  * Originally Released Under LGPL - original licence link has changed is not relivant.
18551  *
18552  * Fork - LGPL
18553  * <script type="text/javascript">
18554  */
18555  
18556
18557 /**
18558  * @class Roo.tree.TreeSorter
18559  * Provides sorting of nodes in a TreePanel
18560  * 
18561  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18562  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18563  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18564  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18565  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18566  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18567  * @constructor
18568  * @param {TreePanel} tree
18569  * @param {Object} config
18570  */
18571 Roo.tree.TreeSorter = function(tree, config){
18572     Roo.apply(this, config);
18573     tree.on("beforechildrenrendered", this.doSort, this);
18574     tree.on("append", this.updateSort, this);
18575     tree.on("insert", this.updateSort, this);
18576     
18577     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18578     var p = this.property || "text";
18579     var sortType = this.sortType;
18580     var fs = this.folderSort;
18581     var cs = this.caseSensitive === true;
18582     var leafAttr = this.leafAttr || 'leaf';
18583
18584     this.sortFn = function(n1, n2){
18585         if(fs){
18586             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18587                 return 1;
18588             }
18589             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18590                 return -1;
18591             }
18592         }
18593         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18594         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18595         if(v1 < v2){
18596                         return dsc ? +1 : -1;
18597                 }else if(v1 > v2){
18598                         return dsc ? -1 : +1;
18599         }else{
18600                 return 0;
18601         }
18602     };
18603 };
18604
18605 Roo.tree.TreeSorter.prototype = {
18606     doSort : function(node){
18607         node.sort(this.sortFn);
18608     },
18609     
18610     compareNodes : function(n1, n2){
18611         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18612     },
18613     
18614     updateSort : function(tree, node){
18615         if(node.childrenRendered){
18616             this.doSort.defer(1, this, [node]);
18617         }
18618     }
18619 };/*
18620  * Based on:
18621  * Ext JS Library 1.1.1
18622  * Copyright(c) 2006-2007, Ext JS, LLC.
18623  *
18624  * Originally Released Under LGPL - original licence link has changed is not relivant.
18625  *
18626  * Fork - LGPL
18627  * <script type="text/javascript">
18628  */
18629
18630 if(Roo.dd.DropZone){
18631     
18632 Roo.tree.TreeDropZone = function(tree, config){
18633     this.allowParentInsert = false;
18634     this.allowContainerDrop = false;
18635     this.appendOnly = false;
18636     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18637     this.tree = tree;
18638     this.lastInsertClass = "x-tree-no-status";
18639     this.dragOverData = {};
18640 };
18641
18642 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18643     ddGroup : "TreeDD",
18644     
18645     expandDelay : 1000,
18646     
18647     expandNode : function(node){
18648         if(node.hasChildNodes() && !node.isExpanded()){
18649             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18650         }
18651     },
18652     
18653     queueExpand : function(node){
18654         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18655     },
18656     
18657     cancelExpand : function(){
18658         if(this.expandProcId){
18659             clearTimeout(this.expandProcId);
18660             this.expandProcId = false;
18661         }
18662     },
18663     
18664     isValidDropPoint : function(n, pt, dd, e, data){
18665         if(!n || !data){ return false; }
18666         var targetNode = n.node;
18667         var dropNode = data.node;
18668         // default drop rules
18669         if(!(targetNode && targetNode.isTarget && pt)){
18670             return false;
18671         }
18672         if(pt == "append" && targetNode.allowChildren === false){
18673             return false;
18674         }
18675         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18676             return false;
18677         }
18678         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18679             return false;
18680         }
18681         // reuse the object
18682         var overEvent = this.dragOverData;
18683         overEvent.tree = this.tree;
18684         overEvent.target = targetNode;
18685         overEvent.data = data;
18686         overEvent.point = pt;
18687         overEvent.source = dd;
18688         overEvent.rawEvent = e;
18689         overEvent.dropNode = dropNode;
18690         overEvent.cancel = false;  
18691         var result = this.tree.fireEvent("nodedragover", overEvent);
18692         return overEvent.cancel === false && result !== false;
18693     },
18694     
18695     getDropPoint : function(e, n, dd){
18696         var tn = n.node;
18697         if(tn.isRoot){
18698             return tn.allowChildren !== false ? "append" : false; // always append for root
18699         }
18700         var dragEl = n.ddel;
18701         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18702         var y = Roo.lib.Event.getPageY(e);
18703         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18704         
18705         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18706         var noAppend = tn.allowChildren === false;
18707         if(this.appendOnly || tn.parentNode.allowChildren === false){
18708             return noAppend ? false : "append";
18709         }
18710         var noBelow = false;
18711         if(!this.allowParentInsert){
18712             noBelow = tn.hasChildNodes() && tn.isExpanded();
18713         }
18714         var q = (b - t) / (noAppend ? 2 : 3);
18715         if(y >= t && y < (t + q)){
18716             return "above";
18717         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18718             return "below";
18719         }else{
18720             return "append";
18721         }
18722     },
18723     
18724     onNodeEnter : function(n, dd, e, data){
18725         this.cancelExpand();
18726     },
18727     
18728     onNodeOver : function(n, dd, e, data){
18729         var pt = this.getDropPoint(e, n, dd);
18730         var node = n.node;
18731         
18732         // auto node expand check
18733         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18734             this.queueExpand(node);
18735         }else if(pt != "append"){
18736             this.cancelExpand();
18737         }
18738         
18739         // set the insert point style on the target node
18740         var returnCls = this.dropNotAllowed;
18741         if(this.isValidDropPoint(n, pt, dd, e, data)){
18742            if(pt){
18743                var el = n.ddel;
18744                var cls;
18745                if(pt == "above"){
18746                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18747                    cls = "x-tree-drag-insert-above";
18748                }else if(pt == "below"){
18749                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18750                    cls = "x-tree-drag-insert-below";
18751                }else{
18752                    returnCls = "x-tree-drop-ok-append";
18753                    cls = "x-tree-drag-append";
18754                }
18755                if(this.lastInsertClass != cls){
18756                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18757                    this.lastInsertClass = cls;
18758                }
18759            }
18760        }
18761        return returnCls;
18762     },
18763     
18764     onNodeOut : function(n, dd, e, data){
18765         this.cancelExpand();
18766         this.removeDropIndicators(n);
18767     },
18768     
18769     onNodeDrop : function(n, dd, e, data){
18770         var point = this.getDropPoint(e, n, dd);
18771         var targetNode = n.node;
18772         targetNode.ui.startDrop();
18773         if(!this.isValidDropPoint(n, point, dd, e, data)){
18774             targetNode.ui.endDrop();
18775             return false;
18776         }
18777         // first try to find the drop node
18778         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18779         var dropEvent = {
18780             tree : this.tree,
18781             target: targetNode,
18782             data: data,
18783             point: point,
18784             source: dd,
18785             rawEvent: e,
18786             dropNode: dropNode,
18787             cancel: !dropNode   
18788         };
18789         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18790         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18791             targetNode.ui.endDrop();
18792             return false;
18793         }
18794         // allow target changing
18795         targetNode = dropEvent.target;
18796         if(point == "append" && !targetNode.isExpanded()){
18797             targetNode.expand(false, null, function(){
18798                 this.completeDrop(dropEvent);
18799             }.createDelegate(this));
18800         }else{
18801             this.completeDrop(dropEvent);
18802         }
18803         return true;
18804     },
18805     
18806     completeDrop : function(de){
18807         var ns = de.dropNode, p = de.point, t = de.target;
18808         if(!(ns instanceof Array)){
18809             ns = [ns];
18810         }
18811         var n;
18812         for(var i = 0, len = ns.length; i < len; i++){
18813             n = ns[i];
18814             if(p == "above"){
18815                 t.parentNode.insertBefore(n, t);
18816             }else if(p == "below"){
18817                 t.parentNode.insertBefore(n, t.nextSibling);
18818             }else{
18819                 t.appendChild(n);
18820             }
18821         }
18822         n.ui.focus();
18823         if(this.tree.hlDrop){
18824             n.ui.highlight();
18825         }
18826         t.ui.endDrop();
18827         this.tree.fireEvent("nodedrop", de);
18828     },
18829     
18830     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18831         if(this.tree.hlDrop){
18832             dropNode.ui.focus();
18833             dropNode.ui.highlight();
18834         }
18835         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18836     },
18837     
18838     getTree : function(){
18839         return this.tree;
18840     },
18841     
18842     removeDropIndicators : function(n){
18843         if(n && n.ddel){
18844             var el = n.ddel;
18845             Roo.fly(el).removeClass([
18846                     "x-tree-drag-insert-above",
18847                     "x-tree-drag-insert-below",
18848                     "x-tree-drag-append"]);
18849             this.lastInsertClass = "_noclass";
18850         }
18851     },
18852     
18853     beforeDragDrop : function(target, e, id){
18854         this.cancelExpand();
18855         return true;
18856     },
18857     
18858     afterRepair : function(data){
18859         if(data && Roo.enableFx){
18860             data.node.ui.highlight();
18861         }
18862         this.hideProxy();
18863     }    
18864 });
18865
18866 }
18867 /*
18868  * Based on:
18869  * Ext JS Library 1.1.1
18870  * Copyright(c) 2006-2007, Ext JS, LLC.
18871  *
18872  * Originally Released Under LGPL - original licence link has changed is not relivant.
18873  *
18874  * Fork - LGPL
18875  * <script type="text/javascript">
18876  */
18877  
18878
18879 if(Roo.dd.DragZone){
18880 Roo.tree.TreeDragZone = function(tree, config){
18881     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18882     this.tree = tree;
18883 };
18884
18885 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18886     ddGroup : "TreeDD",
18887     
18888     onBeforeDrag : function(data, e){
18889         var n = data.node;
18890         return n && n.draggable && !n.disabled;
18891     },
18892     
18893     onInitDrag : function(e){
18894         var data = this.dragData;
18895         this.tree.getSelectionModel().select(data.node);
18896         this.proxy.update("");
18897         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18898         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18899     },
18900     
18901     getRepairXY : function(e, data){
18902         return data.node.ui.getDDRepairXY();
18903     },
18904     
18905     onEndDrag : function(data, e){
18906         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18907     },
18908     
18909     onValidDrop : function(dd, e, id){
18910         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18911         this.hideProxy();
18912     },
18913     
18914     beforeInvalidDrop : function(e, id){
18915         // this scrolls the original position back into view
18916         var sm = this.tree.getSelectionModel();
18917         sm.clearSelections();
18918         sm.select(this.dragData.node);
18919     }
18920 });
18921 }/*
18922  * Based on:
18923  * Ext JS Library 1.1.1
18924  * Copyright(c) 2006-2007, Ext JS, LLC.
18925  *
18926  * Originally Released Under LGPL - original licence link has changed is not relivant.
18927  *
18928  * Fork - LGPL
18929  * <script type="text/javascript">
18930  */
18931 /**
18932  * @class Roo.tree.TreeEditor
18933  * @extends Roo.Editor
18934  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18935  * as the editor field.
18936  * @constructor
18937  * @param {TreePanel} tree
18938  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18939  */
18940 Roo.tree.TreeEditor = function(tree, config){
18941     config = config || {};
18942     var field = config.events ? config : new Roo.form.TextField(config);
18943     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18944
18945     this.tree = tree;
18946
18947     tree.on('beforeclick', this.beforeNodeClick, this);
18948     tree.getTreeEl().on('mousedown', this.hide, this);
18949     this.on('complete', this.updateNode, this);
18950     this.on('beforestartedit', this.fitToTree, this);
18951     this.on('startedit', this.bindScroll, this, {delay:10});
18952     this.on('specialkey', this.onSpecialKey, this);
18953 };
18954
18955 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18956     /**
18957      * @cfg {String} alignment
18958      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18959      */
18960     alignment: "l-l",
18961     // inherit
18962     autoSize: false,
18963     /**
18964      * @cfg {Boolean} hideEl
18965      * True to hide the bound element while the editor is displayed (defaults to false)
18966      */
18967     hideEl : false,
18968     /**
18969      * @cfg {String} cls
18970      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18971      */
18972     cls: "x-small-editor x-tree-editor",
18973     /**
18974      * @cfg {Boolean} shim
18975      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18976      */
18977     shim:false,
18978     // inherit
18979     shadow:"frame",
18980     /**
18981      * @cfg {Number} maxWidth
18982      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18983      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18984      * scroll and client offsets into account prior to each edit.
18985      */
18986     maxWidth: 250,
18987
18988     editDelay : 350,
18989
18990     // private
18991     fitToTree : function(ed, el){
18992         var td = this.tree.getTreeEl().dom, nd = el.dom;
18993         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
18994             td.scrollLeft = nd.offsetLeft;
18995         }
18996         var w = Math.min(
18997                 this.maxWidth,
18998                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
18999         this.setSize(w, '');
19000     },
19001
19002     // private
19003     triggerEdit : function(node){
19004         this.completeEdit();
19005         this.editNode = node;
19006         this.startEdit(node.ui.textNode, node.text);
19007     },
19008
19009     // private
19010     bindScroll : function(){
19011         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19012     },
19013
19014     // private
19015     beforeNodeClick : function(node, e){
19016         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19017         this.lastClick = new Date();
19018         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19019             e.stopEvent();
19020             this.triggerEdit(node);
19021             return false;
19022         }
19023     },
19024
19025     // private
19026     updateNode : function(ed, value){
19027         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19028         this.editNode.setText(value);
19029     },
19030
19031     // private
19032     onHide : function(){
19033         Roo.tree.TreeEditor.superclass.onHide.call(this);
19034         if(this.editNode){
19035             this.editNode.ui.focus();
19036         }
19037     },
19038
19039     // private
19040     onSpecialKey : function(field, e){
19041         var k = e.getKey();
19042         if(k == e.ESC){
19043             e.stopEvent();
19044             this.cancelEdit();
19045         }else if(k == e.ENTER && !e.hasModifier()){
19046             e.stopEvent();
19047             this.completeEdit();
19048         }
19049     }
19050 });//<Script type="text/javascript">
19051 /*
19052  * Based on:
19053  * Ext JS Library 1.1.1
19054  * Copyright(c) 2006-2007, Ext JS, LLC.
19055  *
19056  * Originally Released Under LGPL - original licence link has changed is not relivant.
19057  *
19058  * Fork - LGPL
19059  * <script type="text/javascript">
19060  */
19061  
19062 /**
19063  * Not documented??? - probably should be...
19064  */
19065
19066 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19067     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19068     
19069     renderElements : function(n, a, targetNode, bulkRender){
19070         //consel.log("renderElements?");
19071         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19072
19073         var t = n.getOwnerTree();
19074         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19075         
19076         var cols = t.columns;
19077         var bw = t.borderWidth;
19078         var c = cols[0];
19079         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19080          var cb = typeof a.checked == "boolean";
19081         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19082         var colcls = 'x-t-' + tid + '-c0';
19083         var buf = [
19084             '<li class="x-tree-node">',
19085             
19086                 
19087                 '<div class="x-tree-node-el ', a.cls,'">',
19088                     // extran...
19089                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19090                 
19091                 
19092                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19093                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19094                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19095                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19096                            (a.iconCls ? ' '+a.iconCls : ''),
19097                            '" unselectable="on" />',
19098                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19099                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19100                              
19101                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19102                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19103                             '<span unselectable="on" qtip="' + tx + '">',
19104                              tx,
19105                              '</span></a>' ,
19106                     '</div>',
19107                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19108                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19109                  ];
19110         for(var i = 1, len = cols.length; i < len; i++){
19111             c = cols[i];
19112             colcls = 'x-t-' + tid + '-c' +i;
19113             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19114             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19115                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19116                       "</div>");
19117          }
19118          
19119          buf.push(
19120             '</a>',
19121             '<div class="x-clear"></div></div>',
19122             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19123             "</li>");
19124         
19125         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19126             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19127                                 n.nextSibling.ui.getEl(), buf.join(""));
19128         }else{
19129             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19130         }
19131         var el = this.wrap.firstChild;
19132         this.elRow = el;
19133         this.elNode = el.firstChild;
19134         this.ranchor = el.childNodes[1];
19135         this.ctNode = this.wrap.childNodes[1];
19136         var cs = el.firstChild.childNodes;
19137         this.indentNode = cs[0];
19138         this.ecNode = cs[1];
19139         this.iconNode = cs[2];
19140         var index = 3;
19141         if(cb){
19142             this.checkbox = cs[3];
19143             index++;
19144         }
19145         this.anchor = cs[index];
19146         
19147         this.textNode = cs[index].firstChild;
19148         
19149         //el.on("click", this.onClick, this);
19150         //el.on("dblclick", this.onDblClick, this);
19151         
19152         
19153        // console.log(this);
19154     },
19155     initEvents : function(){
19156         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19157         
19158             
19159         var a = this.ranchor;
19160
19161         var el = Roo.get(a);
19162
19163         if(Roo.isOpera){ // opera render bug ignores the CSS
19164             el.setStyle("text-decoration", "none");
19165         }
19166
19167         el.on("click", this.onClick, this);
19168         el.on("dblclick", this.onDblClick, this);
19169         el.on("contextmenu", this.onContextMenu, this);
19170         
19171     },
19172     
19173     /*onSelectedChange : function(state){
19174         if(state){
19175             this.focus();
19176             this.addClass("x-tree-selected");
19177         }else{
19178             //this.blur();
19179             this.removeClass("x-tree-selected");
19180         }
19181     },*/
19182     addClass : function(cls){
19183         if(this.elRow){
19184             Roo.fly(this.elRow).addClass(cls);
19185         }
19186         
19187     },
19188     
19189     
19190     removeClass : function(cls){
19191         if(this.elRow){
19192             Roo.fly(this.elRow).removeClass(cls);
19193         }
19194     }
19195
19196     
19197     
19198 });//<Script type="text/javascript">
19199
19200 /*
19201  * Based on:
19202  * Ext JS Library 1.1.1
19203  * Copyright(c) 2006-2007, Ext JS, LLC.
19204  *
19205  * Originally Released Under LGPL - original licence link has changed is not relivant.
19206  *
19207  * Fork - LGPL
19208  * <script type="text/javascript">
19209  */
19210  
19211
19212 /**
19213  * @class Roo.tree.ColumnTree
19214  * @extends Roo.data.TreePanel
19215  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19216  * @cfg {int} borderWidth  compined right/left border allowance
19217  * @constructor
19218  * @param {String/HTMLElement/Element} el The container element
19219  * @param {Object} config
19220  */
19221 Roo.tree.ColumnTree =  function(el, config)
19222 {
19223    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19224    this.addEvents({
19225         /**
19226         * @event resize
19227         * Fire this event on a container when it resizes
19228         * @param {int} w Width
19229         * @param {int} h Height
19230         */
19231        "resize" : true
19232     });
19233     this.on('resize', this.onResize, this);
19234 };
19235
19236 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19237     //lines:false,
19238     
19239     
19240     borderWidth: Roo.isBorderBox ? 0 : 2, 
19241     headEls : false,
19242     
19243     render : function(){
19244         // add the header.....
19245        
19246         Roo.tree.ColumnTree.superclass.render.apply(this);
19247         
19248         this.el.addClass('x-column-tree');
19249         
19250         this.headers = this.el.createChild(
19251             {cls:'x-tree-headers'},this.innerCt.dom);
19252    
19253         var cols = this.columns, c;
19254         var totalWidth = 0;
19255         this.headEls = [];
19256         var  len = cols.length;
19257         for(var i = 0; i < len; i++){
19258              c = cols[i];
19259              totalWidth += c.width;
19260             this.headEls.push(this.headers.createChild({
19261                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19262                  cn: {
19263                      cls:'x-tree-hd-text',
19264                      html: c.header
19265                  },
19266                  style:'width:'+(c.width-this.borderWidth)+'px;'
19267              }));
19268         }
19269         this.headers.createChild({cls:'x-clear'});
19270         // prevent floats from wrapping when clipped
19271         this.headers.setWidth(totalWidth);
19272         //this.innerCt.setWidth(totalWidth);
19273         this.innerCt.setStyle({ overflow: 'auto' });
19274         this.onResize(this.width, this.height);
19275              
19276         
19277     },
19278     onResize : function(w,h)
19279     {
19280         this.height = h;
19281         this.width = w;
19282         // resize cols..
19283         this.innerCt.setWidth(this.width);
19284         this.innerCt.setHeight(this.height-20);
19285         
19286         // headers...
19287         var cols = this.columns, c;
19288         var totalWidth = 0;
19289         var expEl = false;
19290         var len = cols.length;
19291         for(var i = 0; i < len; i++){
19292             c = cols[i];
19293             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19294                 // it's the expander..
19295                 expEl  = this.headEls[i];
19296                 continue;
19297             }
19298             totalWidth += c.width;
19299             
19300         }
19301         if (expEl) {
19302             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19303         }
19304         this.headers.setWidth(w-20);
19305
19306         
19307         
19308         
19309     }
19310 });
19311 /*
19312  * Based on:
19313  * Ext JS Library 1.1.1
19314  * Copyright(c) 2006-2007, Ext JS, LLC.
19315  *
19316  * Originally Released Under LGPL - original licence link has changed is not relivant.
19317  *
19318  * Fork - LGPL
19319  * <script type="text/javascript">
19320  */
19321  
19322 /**
19323  * @class Roo.menu.Menu
19324  * @extends Roo.util.Observable
19325  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19326  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19327  * @constructor
19328  * Creates a new Menu
19329  * @param {Object} config Configuration options
19330  */
19331 Roo.menu.Menu = function(config){
19332     Roo.apply(this, config);
19333     this.id = this.id || Roo.id();
19334     this.addEvents({
19335         /**
19336          * @event beforeshow
19337          * Fires before this menu is displayed
19338          * @param {Roo.menu.Menu} this
19339          */
19340         beforeshow : true,
19341         /**
19342          * @event beforehide
19343          * Fires before this menu is hidden
19344          * @param {Roo.menu.Menu} this
19345          */
19346         beforehide : true,
19347         /**
19348          * @event show
19349          * Fires after this menu is displayed
19350          * @param {Roo.menu.Menu} this
19351          */
19352         show : true,
19353         /**
19354          * @event hide
19355          * Fires after this menu is hidden
19356          * @param {Roo.menu.Menu} this
19357          */
19358         hide : true,
19359         /**
19360          * @event click
19361          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19362          * @param {Roo.menu.Menu} this
19363          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19364          * @param {Roo.EventObject} e
19365          */
19366         click : true,
19367         /**
19368          * @event mouseover
19369          * Fires when the mouse is hovering over this menu
19370          * @param {Roo.menu.Menu} this
19371          * @param {Roo.EventObject} e
19372          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19373          */
19374         mouseover : true,
19375         /**
19376          * @event mouseout
19377          * Fires when the mouse exits this menu
19378          * @param {Roo.menu.Menu} this
19379          * @param {Roo.EventObject} e
19380          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19381          */
19382         mouseout : true,
19383         /**
19384          * @event itemclick
19385          * Fires when a menu item contained in this menu is clicked
19386          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19387          * @param {Roo.EventObject} e
19388          */
19389         itemclick: true
19390     });
19391     if (this.registerMenu) {
19392         Roo.menu.MenuMgr.register(this);
19393     }
19394     
19395     var mis = this.items;
19396     this.items = new Roo.util.MixedCollection();
19397     if(mis){
19398         this.add.apply(this, mis);
19399     }
19400 };
19401
19402 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19403     /**
19404      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19405      */
19406     minWidth : 120,
19407     /**
19408      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19409      * for bottom-right shadow (defaults to "sides")
19410      */
19411     shadow : "sides",
19412     /**
19413      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19414      * this menu (defaults to "tl-tr?")
19415      */
19416     subMenuAlign : "tl-tr?",
19417     /**
19418      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19419      * relative to its element of origin (defaults to "tl-bl?")
19420      */
19421     defaultAlign : "tl-bl?",
19422     /**
19423      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19424      */
19425     allowOtherMenus : false,
19426     /**
19427      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19428      */
19429     registerMenu : true,
19430
19431     hidden:true,
19432
19433     // private
19434     render : function(){
19435         if(this.el){
19436             return;
19437         }
19438         var el = this.el = new Roo.Layer({
19439             cls: "x-menu",
19440             shadow:this.shadow,
19441             constrain: false,
19442             parentEl: this.parentEl || document.body,
19443             zindex:15000
19444         });
19445
19446         this.keyNav = new Roo.menu.MenuNav(this);
19447
19448         if(this.plain){
19449             el.addClass("x-menu-plain");
19450         }
19451         if(this.cls){
19452             el.addClass(this.cls);
19453         }
19454         // generic focus element
19455         this.focusEl = el.createChild({
19456             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19457         });
19458         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19459         ul.on("click", this.onClick, this);
19460         ul.on("mouseover", this.onMouseOver, this);
19461         ul.on("mouseout", this.onMouseOut, this);
19462         this.items.each(function(item){
19463             var li = document.createElement("li");
19464             li.className = "x-menu-list-item";
19465             ul.dom.appendChild(li);
19466             item.render(li, this);
19467         }, this);
19468         this.ul = ul;
19469         this.autoWidth();
19470     },
19471
19472     // private
19473     autoWidth : function(){
19474         var el = this.el, ul = this.ul;
19475         if(!el){
19476             return;
19477         }
19478         var w = this.width;
19479         if(w){
19480             el.setWidth(w);
19481         }else if(Roo.isIE){
19482             el.setWidth(this.minWidth);
19483             var t = el.dom.offsetWidth; // force recalc
19484             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19485         }
19486     },
19487
19488     // private
19489     delayAutoWidth : function(){
19490         if(this.rendered){
19491             if(!this.awTask){
19492                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19493             }
19494             this.awTask.delay(20);
19495         }
19496     },
19497
19498     // private
19499     findTargetItem : function(e){
19500         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19501         if(t && t.menuItemId){
19502             return this.items.get(t.menuItemId);
19503         }
19504     },
19505
19506     // private
19507     onClick : function(e){
19508         var t;
19509         if(t = this.findTargetItem(e)){
19510             t.onClick(e);
19511             this.fireEvent("click", this, t, e);
19512         }
19513     },
19514
19515     // private
19516     setActiveItem : function(item, autoExpand){
19517         if(item != this.activeItem){
19518             if(this.activeItem){
19519                 this.activeItem.deactivate();
19520             }
19521             this.activeItem = item;
19522             item.activate(autoExpand);
19523         }else if(autoExpand){
19524             item.expandMenu();
19525         }
19526     },
19527
19528     // private
19529     tryActivate : function(start, step){
19530         var items = this.items;
19531         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19532             var item = items.get(i);
19533             if(!item.disabled && item.canActivate){
19534                 this.setActiveItem(item, false);
19535                 return item;
19536             }
19537         }
19538         return false;
19539     },
19540
19541     // private
19542     onMouseOver : function(e){
19543         var t;
19544         if(t = this.findTargetItem(e)){
19545             if(t.canActivate && !t.disabled){
19546                 this.setActiveItem(t, true);
19547             }
19548         }
19549         this.fireEvent("mouseover", this, e, t);
19550     },
19551
19552     // private
19553     onMouseOut : function(e){
19554         var t;
19555         if(t = this.findTargetItem(e)){
19556             if(t == this.activeItem && t.shouldDeactivate(e)){
19557                 this.activeItem.deactivate();
19558                 delete this.activeItem;
19559             }
19560         }
19561         this.fireEvent("mouseout", this, e, t);
19562     },
19563
19564     /**
19565      * Read-only.  Returns true if the menu is currently displayed, else false.
19566      * @type Boolean
19567      */
19568     isVisible : function(){
19569         return this.el && !this.hidden;
19570     },
19571
19572     /**
19573      * Displays this menu relative to another element
19574      * @param {String/HTMLElement/Roo.Element} element The element to align to
19575      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19576      * the element (defaults to this.defaultAlign)
19577      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19578      */
19579     show : function(el, pos, parentMenu){
19580         this.parentMenu = parentMenu;
19581         if(!this.el){
19582             this.render();
19583         }
19584         this.fireEvent("beforeshow", this);
19585         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19586     },
19587
19588     /**
19589      * Displays this menu at a specific xy position
19590      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19591      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19592      */
19593     showAt : function(xy, parentMenu, /* private: */_e){
19594         this.parentMenu = parentMenu;
19595         if(!this.el){
19596             this.render();
19597         }
19598         if(_e !== false){
19599             this.fireEvent("beforeshow", this);
19600             xy = this.el.adjustForConstraints(xy);
19601         }
19602         this.el.setXY(xy);
19603         this.el.show();
19604         this.hidden = false;
19605         this.focus();
19606         this.fireEvent("show", this);
19607     },
19608
19609     focus : function(){
19610         if(!this.hidden){
19611             this.doFocus.defer(50, this);
19612         }
19613     },
19614
19615     doFocus : function(){
19616         if(!this.hidden){
19617             this.focusEl.focus();
19618         }
19619     },
19620
19621     /**
19622      * Hides this menu and optionally all parent menus
19623      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19624      */
19625     hide : function(deep){
19626         if(this.el && this.isVisible()){
19627             this.fireEvent("beforehide", this);
19628             if(this.activeItem){
19629                 this.activeItem.deactivate();
19630                 this.activeItem = null;
19631             }
19632             this.el.hide();
19633             this.hidden = true;
19634             this.fireEvent("hide", this);
19635         }
19636         if(deep === true && this.parentMenu){
19637             this.parentMenu.hide(true);
19638         }
19639     },
19640
19641     /**
19642      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19643      * Any of the following are valid:
19644      * <ul>
19645      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19646      * <li>An HTMLElement object which will be converted to a menu item</li>
19647      * <li>A menu item config object that will be created as a new menu item</li>
19648      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19649      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19650      * </ul>
19651      * Usage:
19652      * <pre><code>
19653 // Create the menu
19654 var menu = new Roo.menu.Menu();
19655
19656 // Create a menu item to add by reference
19657 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19658
19659 // Add a bunch of items at once using different methods.
19660 // Only the last item added will be returned.
19661 var item = menu.add(
19662     menuItem,                // add existing item by ref
19663     'Dynamic Item',          // new TextItem
19664     '-',                     // new separator
19665     { text: 'Config Item' }  // new item by config
19666 );
19667 </code></pre>
19668      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19669      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19670      */
19671     add : function(){
19672         var a = arguments, l = a.length, item;
19673         for(var i = 0; i < l; i++){
19674             var el = a[i];
19675             if ((typeof(el) == "object") && el.xtype && el.xns) {
19676                 el = Roo.factory(el, Roo.menu);
19677             }
19678             
19679             if(el.render){ // some kind of Item
19680                 item = this.addItem(el);
19681             }else if(typeof el == "string"){ // string
19682                 if(el == "separator" || el == "-"){
19683                     item = this.addSeparator();
19684                 }else{
19685                     item = this.addText(el);
19686                 }
19687             }else if(el.tagName || el.el){ // element
19688                 item = this.addElement(el);
19689             }else if(typeof el == "object"){ // must be menu item config?
19690                 item = this.addMenuItem(el);
19691             }
19692         }
19693         return item;
19694     },
19695
19696     /**
19697      * Returns this menu's underlying {@link Roo.Element} object
19698      * @return {Roo.Element} The element
19699      */
19700     getEl : function(){
19701         if(!this.el){
19702             this.render();
19703         }
19704         return this.el;
19705     },
19706
19707     /**
19708      * Adds a separator bar to the menu
19709      * @return {Roo.menu.Item} The menu item that was added
19710      */
19711     addSeparator : function(){
19712         return this.addItem(new Roo.menu.Separator());
19713     },
19714
19715     /**
19716      * Adds an {@link Roo.Element} object to the menu
19717      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19718      * @return {Roo.menu.Item} The menu item that was added
19719      */
19720     addElement : function(el){
19721         return this.addItem(new Roo.menu.BaseItem(el));
19722     },
19723
19724     /**
19725      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19726      * @param {Roo.menu.Item} item The menu item to add
19727      * @return {Roo.menu.Item} The menu item that was added
19728      */
19729     addItem : function(item){
19730         this.items.add(item);
19731         if(this.ul){
19732             var li = document.createElement("li");
19733             li.className = "x-menu-list-item";
19734             this.ul.dom.appendChild(li);
19735             item.render(li, this);
19736             this.delayAutoWidth();
19737         }
19738         return item;
19739     },
19740
19741     /**
19742      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19743      * @param {Object} config A MenuItem config object
19744      * @return {Roo.menu.Item} The menu item that was added
19745      */
19746     addMenuItem : function(config){
19747         if(!(config instanceof Roo.menu.Item)){
19748             if(typeof config.checked == "boolean"){ // must be check menu item config?
19749                 config = new Roo.menu.CheckItem(config);
19750             }else{
19751                 config = new Roo.menu.Item(config);
19752             }
19753         }
19754         return this.addItem(config);
19755     },
19756
19757     /**
19758      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19759      * @param {String} text The text to display in the menu item
19760      * @return {Roo.menu.Item} The menu item that was added
19761      */
19762     addText : function(text){
19763         return this.addItem(new Roo.menu.TextItem({ text : text }));
19764     },
19765
19766     /**
19767      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19768      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19769      * @param {Roo.menu.Item} item The menu item to add
19770      * @return {Roo.menu.Item} The menu item that was added
19771      */
19772     insert : function(index, item){
19773         this.items.insert(index, item);
19774         if(this.ul){
19775             var li = document.createElement("li");
19776             li.className = "x-menu-list-item";
19777             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19778             item.render(li, this);
19779             this.delayAutoWidth();
19780         }
19781         return item;
19782     },
19783
19784     /**
19785      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19786      * @param {Roo.menu.Item} item The menu item to remove
19787      */
19788     remove : function(item){
19789         this.items.removeKey(item.id);
19790         item.destroy();
19791     },
19792
19793     /**
19794      * Removes and destroys all items in the menu
19795      */
19796     removeAll : function(){
19797         var f;
19798         while(f = this.items.first()){
19799             this.remove(f);
19800         }
19801     }
19802 });
19803
19804 // MenuNav is a private utility class used internally by the Menu
19805 Roo.menu.MenuNav = function(menu){
19806     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19807     this.scope = this.menu = menu;
19808 };
19809
19810 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19811     doRelay : function(e, h){
19812         var k = e.getKey();
19813         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19814             this.menu.tryActivate(0, 1);
19815             return false;
19816         }
19817         return h.call(this.scope || this, e, this.menu);
19818     },
19819
19820     up : function(e, m){
19821         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19822             m.tryActivate(m.items.length-1, -1);
19823         }
19824     },
19825
19826     down : function(e, m){
19827         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19828             m.tryActivate(0, 1);
19829         }
19830     },
19831
19832     right : function(e, m){
19833         if(m.activeItem){
19834             m.activeItem.expandMenu(true);
19835         }
19836     },
19837
19838     left : function(e, m){
19839         m.hide();
19840         if(m.parentMenu && m.parentMenu.activeItem){
19841             m.parentMenu.activeItem.activate();
19842         }
19843     },
19844
19845     enter : function(e, m){
19846         if(m.activeItem){
19847             e.stopPropagation();
19848             m.activeItem.onClick(e);
19849             m.fireEvent("click", this, m.activeItem);
19850             return true;
19851         }
19852     }
19853 });/*
19854  * Based on:
19855  * Ext JS Library 1.1.1
19856  * Copyright(c) 2006-2007, Ext JS, LLC.
19857  *
19858  * Originally Released Under LGPL - original licence link has changed is not relivant.
19859  *
19860  * Fork - LGPL
19861  * <script type="text/javascript">
19862  */
19863  
19864 /**
19865  * @class Roo.menu.MenuMgr
19866  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19867  * @singleton
19868  */
19869 Roo.menu.MenuMgr = function(){
19870    var menus, active, groups = {}, attached = false, lastShow = new Date();
19871
19872    // private - called when first menu is created
19873    function init(){
19874        menus = {};
19875        active = new Roo.util.MixedCollection();
19876        Roo.get(document).addKeyListener(27, function(){
19877            if(active.length > 0){
19878                hideAll();
19879            }
19880        });
19881    }
19882
19883    // private
19884    function hideAll(){
19885        if(active && active.length > 0){
19886            var c = active.clone();
19887            c.each(function(m){
19888                m.hide();
19889            });
19890        }
19891    }
19892
19893    // private
19894    function onHide(m){
19895        active.remove(m);
19896        if(active.length < 1){
19897            Roo.get(document).un("mousedown", onMouseDown);
19898            attached = false;
19899        }
19900    }
19901
19902    // private
19903    function onShow(m){
19904        var last = active.last();
19905        lastShow = new Date();
19906        active.add(m);
19907        if(!attached){
19908            Roo.get(document).on("mousedown", onMouseDown);
19909            attached = true;
19910        }
19911        if(m.parentMenu){
19912           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19913           m.parentMenu.activeChild = m;
19914        }else if(last && last.isVisible()){
19915           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19916        }
19917    }
19918
19919    // private
19920    function onBeforeHide(m){
19921        if(m.activeChild){
19922            m.activeChild.hide();
19923        }
19924        if(m.autoHideTimer){
19925            clearTimeout(m.autoHideTimer);
19926            delete m.autoHideTimer;
19927        }
19928    }
19929
19930    // private
19931    function onBeforeShow(m){
19932        var pm = m.parentMenu;
19933        if(!pm && !m.allowOtherMenus){
19934            hideAll();
19935        }else if(pm && pm.activeChild && active != m){
19936            pm.activeChild.hide();
19937        }
19938    }
19939
19940    // private
19941    function onMouseDown(e){
19942        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19943            hideAll();
19944        }
19945    }
19946
19947    // private
19948    function onBeforeCheck(mi, state){
19949        if(state){
19950            var g = groups[mi.group];
19951            for(var i = 0, l = g.length; i < l; i++){
19952                if(g[i] != mi){
19953                    g[i].setChecked(false);
19954                }
19955            }
19956        }
19957    }
19958
19959    return {
19960
19961        /**
19962         * Hides all menus that are currently visible
19963         */
19964        hideAll : function(){
19965             hideAll();  
19966        },
19967
19968        // private
19969        register : function(menu){
19970            if(!menus){
19971                init();
19972            }
19973            menus[menu.id] = menu;
19974            menu.on("beforehide", onBeforeHide);
19975            menu.on("hide", onHide);
19976            menu.on("beforeshow", onBeforeShow);
19977            menu.on("show", onShow);
19978            var g = menu.group;
19979            if(g && menu.events["checkchange"]){
19980                if(!groups[g]){
19981                    groups[g] = [];
19982                }
19983                groups[g].push(menu);
19984                menu.on("checkchange", onCheck);
19985            }
19986        },
19987
19988         /**
19989          * Returns a {@link Roo.menu.Menu} object
19990          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19991          * be used to generate and return a new Menu instance.
19992          */
19993        get : function(menu){
19994            if(typeof menu == "string"){ // menu id
19995                return menus[menu];
19996            }else if(menu.events){  // menu instance
19997                return menu;
19998            }else if(typeof menu.length == 'number'){ // array of menu items?
19999                return new Roo.menu.Menu({items:menu});
20000            }else{ // otherwise, must be a config
20001                return new Roo.menu.Menu(menu);
20002            }
20003        },
20004
20005        // private
20006        unregister : function(menu){
20007            delete menus[menu.id];
20008            menu.un("beforehide", onBeforeHide);
20009            menu.un("hide", onHide);
20010            menu.un("beforeshow", onBeforeShow);
20011            menu.un("show", onShow);
20012            var g = menu.group;
20013            if(g && menu.events["checkchange"]){
20014                groups[g].remove(menu);
20015                menu.un("checkchange", onCheck);
20016            }
20017        },
20018
20019        // private
20020        registerCheckable : function(menuItem){
20021            var g = menuItem.group;
20022            if(g){
20023                if(!groups[g]){
20024                    groups[g] = [];
20025                }
20026                groups[g].push(menuItem);
20027                menuItem.on("beforecheckchange", onBeforeCheck);
20028            }
20029        },
20030
20031        // private
20032        unregisterCheckable : function(menuItem){
20033            var g = menuItem.group;
20034            if(g){
20035                groups[g].remove(menuItem);
20036                menuItem.un("beforecheckchange", onBeforeCheck);
20037            }
20038        }
20039    };
20040 }();/*
20041  * Based on:
20042  * Ext JS Library 1.1.1
20043  * Copyright(c) 2006-2007, Ext JS, LLC.
20044  *
20045  * Originally Released Under LGPL - original licence link has changed is not relivant.
20046  *
20047  * Fork - LGPL
20048  * <script type="text/javascript">
20049  */
20050  
20051
20052 /**
20053  * @class Roo.menu.BaseItem
20054  * @extends Roo.Component
20055  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20056  * management and base configuration options shared by all menu components.
20057  * @constructor
20058  * Creates a new BaseItem
20059  * @param {Object} config Configuration options
20060  */
20061 Roo.menu.BaseItem = function(config){
20062     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20063
20064     this.addEvents({
20065         /**
20066          * @event click
20067          * Fires when this item is clicked
20068          * @param {Roo.menu.BaseItem} this
20069          * @param {Roo.EventObject} e
20070          */
20071         click: true,
20072         /**
20073          * @event activate
20074          * Fires when this item is activated
20075          * @param {Roo.menu.BaseItem} this
20076          */
20077         activate : true,
20078         /**
20079          * @event deactivate
20080          * Fires when this item is deactivated
20081          * @param {Roo.menu.BaseItem} this
20082          */
20083         deactivate : true
20084     });
20085
20086     if(this.handler){
20087         this.on("click", this.handler, this.scope, true);
20088     }
20089 };
20090
20091 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20092     /**
20093      * @cfg {Function} handler
20094      * A function that will handle the click event of this menu item (defaults to undefined)
20095      */
20096     /**
20097      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20098      */
20099     canActivate : false,
20100     /**
20101      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20102      */
20103     activeClass : "x-menu-item-active",
20104     /**
20105      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20106      */
20107     hideOnClick : true,
20108     /**
20109      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20110      */
20111     hideDelay : 100,
20112
20113     // private
20114     ctype: "Roo.menu.BaseItem",
20115
20116     // private
20117     actionMode : "container",
20118
20119     // private
20120     render : function(container, parentMenu){
20121         this.parentMenu = parentMenu;
20122         Roo.menu.BaseItem.superclass.render.call(this, container);
20123         this.container.menuItemId = this.id;
20124     },
20125
20126     // private
20127     onRender : function(container, position){
20128         this.el = Roo.get(this.el);
20129         container.dom.appendChild(this.el.dom);
20130     },
20131
20132     // private
20133     onClick : function(e){
20134         if(!this.disabled && this.fireEvent("click", this, e) !== false
20135                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20136             this.handleClick(e);
20137         }else{
20138             e.stopEvent();
20139         }
20140     },
20141
20142     // private
20143     activate : function(){
20144         if(this.disabled){
20145             return false;
20146         }
20147         var li = this.container;
20148         li.addClass(this.activeClass);
20149         this.region = li.getRegion().adjust(2, 2, -2, -2);
20150         this.fireEvent("activate", this);
20151         return true;
20152     },
20153
20154     // private
20155     deactivate : function(){
20156         this.container.removeClass(this.activeClass);
20157         this.fireEvent("deactivate", this);
20158     },
20159
20160     // private
20161     shouldDeactivate : function(e){
20162         return !this.region || !this.region.contains(e.getPoint());
20163     },
20164
20165     // private
20166     handleClick : function(e){
20167         if(this.hideOnClick){
20168             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20169         }
20170     },
20171
20172     // private
20173     expandMenu : function(autoActivate){
20174         // do nothing
20175     },
20176
20177     // private
20178     hideMenu : function(){
20179         // do nothing
20180     }
20181 });/*
20182  * Based on:
20183  * Ext JS Library 1.1.1
20184  * Copyright(c) 2006-2007, Ext JS, LLC.
20185  *
20186  * Originally Released Under LGPL - original licence link has changed is not relivant.
20187  *
20188  * Fork - LGPL
20189  * <script type="text/javascript">
20190  */
20191  
20192 /**
20193  * @class Roo.menu.Adapter
20194  * @extends Roo.menu.BaseItem
20195  * 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.
20196  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20197  * @constructor
20198  * Creates a new Adapter
20199  * @param {Object} config Configuration options
20200  */
20201 Roo.menu.Adapter = function(component, config){
20202     Roo.menu.Adapter.superclass.constructor.call(this, config);
20203     this.component = component;
20204 };
20205 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20206     // private
20207     canActivate : true,
20208
20209     // private
20210     onRender : function(container, position){
20211         this.component.render(container);
20212         this.el = this.component.getEl();
20213     },
20214
20215     // private
20216     activate : function(){
20217         if(this.disabled){
20218             return false;
20219         }
20220         this.component.focus();
20221         this.fireEvent("activate", this);
20222         return true;
20223     },
20224
20225     // private
20226     deactivate : function(){
20227         this.fireEvent("deactivate", this);
20228     },
20229
20230     // private
20231     disable : function(){
20232         this.component.disable();
20233         Roo.menu.Adapter.superclass.disable.call(this);
20234     },
20235
20236     // private
20237     enable : function(){
20238         this.component.enable();
20239         Roo.menu.Adapter.superclass.enable.call(this);
20240     }
20241 });/*
20242  * Based on:
20243  * Ext JS Library 1.1.1
20244  * Copyright(c) 2006-2007, Ext JS, LLC.
20245  *
20246  * Originally Released Under LGPL - original licence link has changed is not relivant.
20247  *
20248  * Fork - LGPL
20249  * <script type="text/javascript">
20250  */
20251
20252 /**
20253  * @class Roo.menu.TextItem
20254  * @extends Roo.menu.BaseItem
20255  * Adds a static text string to a menu, usually used as either a heading or group separator.
20256  * Note: old style constructor with text is still supported.
20257  * 
20258  * @constructor
20259  * Creates a new TextItem
20260  * @param {Object} cfg Configuration
20261  */
20262 Roo.menu.TextItem = function(cfg){
20263     if (typeof(cfg) == 'string') {
20264         this.text = cfg;
20265     } else {
20266         Roo.apply(this,cfg);
20267     }
20268     
20269     Roo.menu.TextItem.superclass.constructor.call(this);
20270 };
20271
20272 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20273     /**
20274      * @cfg {Boolean} text Text to show on item.
20275      */
20276     text : '',
20277     
20278     /**
20279      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20280      */
20281     hideOnClick : false,
20282     /**
20283      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20284      */
20285     itemCls : "x-menu-text",
20286
20287     // private
20288     onRender : function(){
20289         var s = document.createElement("span");
20290         s.className = this.itemCls;
20291         s.innerHTML = this.text;
20292         this.el = s;
20293         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20294     }
20295 });/*
20296  * Based on:
20297  * Ext JS Library 1.1.1
20298  * Copyright(c) 2006-2007, Ext JS, LLC.
20299  *
20300  * Originally Released Under LGPL - original licence link has changed is not relivant.
20301  *
20302  * Fork - LGPL
20303  * <script type="text/javascript">
20304  */
20305
20306 /**
20307  * @class Roo.menu.Separator
20308  * @extends Roo.menu.BaseItem
20309  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20310  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20311  * @constructor
20312  * @param {Object} config Configuration options
20313  */
20314 Roo.menu.Separator = function(config){
20315     Roo.menu.Separator.superclass.constructor.call(this, config);
20316 };
20317
20318 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20319     /**
20320      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20321      */
20322     itemCls : "x-menu-sep",
20323     /**
20324      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20325      */
20326     hideOnClick : false,
20327
20328     // private
20329     onRender : function(li){
20330         var s = document.createElement("span");
20331         s.className = this.itemCls;
20332         s.innerHTML = "&#160;";
20333         this.el = s;
20334         li.addClass("x-menu-sep-li");
20335         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20336     }
20337 });/*
20338  * Based on:
20339  * Ext JS Library 1.1.1
20340  * Copyright(c) 2006-2007, Ext JS, LLC.
20341  *
20342  * Originally Released Under LGPL - original licence link has changed is not relivant.
20343  *
20344  * Fork - LGPL
20345  * <script type="text/javascript">
20346  */
20347 /**
20348  * @class Roo.menu.Item
20349  * @extends Roo.menu.BaseItem
20350  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20351  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20352  * activation and click handling.
20353  * @constructor
20354  * Creates a new Item
20355  * @param {Object} config Configuration options
20356  */
20357 Roo.menu.Item = function(config){
20358     Roo.menu.Item.superclass.constructor.call(this, config);
20359     if(this.menu){
20360         this.menu = Roo.menu.MenuMgr.get(this.menu);
20361     }
20362 };
20363 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20364     
20365     /**
20366      * @cfg {String} text
20367      * The text to show on the menu item.
20368      */
20369     text: '',
20370      /**
20371      * @cfg {String} HTML to render in menu
20372      * The text to show on the menu item (HTML version).
20373      */
20374     html: '',
20375     /**
20376      * @cfg {String} icon
20377      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20378      */
20379     icon: undefined,
20380     /**
20381      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20382      */
20383     itemCls : "x-menu-item",
20384     /**
20385      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20386      */
20387     canActivate : true,
20388     /**
20389      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20390      */
20391     showDelay: 200,
20392     // doc'd in BaseItem
20393     hideDelay: 200,
20394
20395     // private
20396     ctype: "Roo.menu.Item",
20397     
20398     // private
20399     onRender : function(container, position){
20400         var el = document.createElement("a");
20401         el.hideFocus = true;
20402         el.unselectable = "on";
20403         el.href = this.href || "#";
20404         if(this.hrefTarget){
20405             el.target = this.hrefTarget;
20406         }
20407         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20408         
20409         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20410         
20411         el.innerHTML = String.format(
20412                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20413                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20414         this.el = el;
20415         Roo.menu.Item.superclass.onRender.call(this, container, position);
20416     },
20417
20418     /**
20419      * Sets the text to display in this menu item
20420      * @param {String} text The text to display
20421      * @param {Boolean} isHTML true to indicate text is pure html.
20422      */
20423     setText : function(text, isHTML){
20424         if (isHTML) {
20425             this.html = text;
20426         } else {
20427             this.text = text;
20428             this.html = '';
20429         }
20430         if(this.rendered){
20431             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20432      
20433             this.el.update(String.format(
20434                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20435                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20436             this.parentMenu.autoWidth();
20437         }
20438     },
20439
20440     // private
20441     handleClick : function(e){
20442         if(!this.href){ // if no link defined, stop the event automatically
20443             e.stopEvent();
20444         }
20445         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20446     },
20447
20448     // private
20449     activate : function(autoExpand){
20450         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20451             this.focus();
20452             if(autoExpand){
20453                 this.expandMenu();
20454             }
20455         }
20456         return true;
20457     },
20458
20459     // private
20460     shouldDeactivate : function(e){
20461         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20462             if(this.menu && this.menu.isVisible()){
20463                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20464             }
20465             return true;
20466         }
20467         return false;
20468     },
20469
20470     // private
20471     deactivate : function(){
20472         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20473         this.hideMenu();
20474     },
20475
20476     // private
20477     expandMenu : function(autoActivate){
20478         if(!this.disabled && this.menu){
20479             clearTimeout(this.hideTimer);
20480             delete this.hideTimer;
20481             if(!this.menu.isVisible() && !this.showTimer){
20482                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20483             }else if (this.menu.isVisible() && autoActivate){
20484                 this.menu.tryActivate(0, 1);
20485             }
20486         }
20487     },
20488
20489     // private
20490     deferExpand : function(autoActivate){
20491         delete this.showTimer;
20492         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20493         if(autoActivate){
20494             this.menu.tryActivate(0, 1);
20495         }
20496     },
20497
20498     // private
20499     hideMenu : function(){
20500         clearTimeout(this.showTimer);
20501         delete this.showTimer;
20502         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20503             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20504         }
20505     },
20506
20507     // private
20508     deferHide : function(){
20509         delete this.hideTimer;
20510         this.menu.hide();
20511     }
20512 });/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522  
20523 /**
20524  * @class Roo.menu.CheckItem
20525  * @extends Roo.menu.Item
20526  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20527  * @constructor
20528  * Creates a new CheckItem
20529  * @param {Object} config Configuration options
20530  */
20531 Roo.menu.CheckItem = function(config){
20532     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20533     this.addEvents({
20534         /**
20535          * @event beforecheckchange
20536          * Fires before the checked value is set, providing an opportunity to cancel if needed
20537          * @param {Roo.menu.CheckItem} this
20538          * @param {Boolean} checked The new checked value that will be set
20539          */
20540         "beforecheckchange" : true,
20541         /**
20542          * @event checkchange
20543          * Fires after the checked value has been set
20544          * @param {Roo.menu.CheckItem} this
20545          * @param {Boolean} checked The checked value that was set
20546          */
20547         "checkchange" : true
20548     });
20549     if(this.checkHandler){
20550         this.on('checkchange', this.checkHandler, this.scope);
20551     }
20552 };
20553 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20554     /**
20555      * @cfg {String} group
20556      * All check items with the same group name will automatically be grouped into a single-select
20557      * radio button group (defaults to '')
20558      */
20559     /**
20560      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20561      */
20562     itemCls : "x-menu-item x-menu-check-item",
20563     /**
20564      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20565      */
20566     groupClass : "x-menu-group-item",
20567
20568     /**
20569      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20570      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20571      * initialized with checked = true will be rendered as checked.
20572      */
20573     checked: false,
20574
20575     // private
20576     ctype: "Roo.menu.CheckItem",
20577
20578     // private
20579     onRender : function(c){
20580         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20581         if(this.group){
20582             this.el.addClass(this.groupClass);
20583         }
20584         Roo.menu.MenuMgr.registerCheckable(this);
20585         if(this.checked){
20586             this.checked = false;
20587             this.setChecked(true, true);
20588         }
20589     },
20590
20591     // private
20592     destroy : function(){
20593         if(this.rendered){
20594             Roo.menu.MenuMgr.unregisterCheckable(this);
20595         }
20596         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20597     },
20598
20599     /**
20600      * Set the checked state of this item
20601      * @param {Boolean} checked The new checked value
20602      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20603      */
20604     setChecked : function(state, suppressEvent){
20605         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20606             if(this.container){
20607                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20608             }
20609             this.checked = state;
20610             if(suppressEvent !== true){
20611                 this.fireEvent("checkchange", this, state);
20612             }
20613         }
20614     },
20615
20616     // private
20617     handleClick : function(e){
20618        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20619            this.setChecked(!this.checked);
20620        }
20621        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20622     }
20623 });/*
20624  * Based on:
20625  * Ext JS Library 1.1.1
20626  * Copyright(c) 2006-2007, Ext JS, LLC.
20627  *
20628  * Originally Released Under LGPL - original licence link has changed is not relivant.
20629  *
20630  * Fork - LGPL
20631  * <script type="text/javascript">
20632  */
20633  
20634 /**
20635  * @class Roo.menu.DateItem
20636  * @extends Roo.menu.Adapter
20637  * A menu item that wraps the {@link Roo.DatPicker} component.
20638  * @constructor
20639  * Creates a new DateItem
20640  * @param {Object} config Configuration options
20641  */
20642 Roo.menu.DateItem = function(config){
20643     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20644     /** The Roo.DatePicker object @type Roo.DatePicker */
20645     this.picker = this.component;
20646     this.addEvents({select: true});
20647     
20648     this.picker.on("render", function(picker){
20649         picker.getEl().swallowEvent("click");
20650         picker.container.addClass("x-menu-date-item");
20651     });
20652
20653     this.picker.on("select", this.onSelect, this);
20654 };
20655
20656 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20657     // private
20658     onSelect : function(picker, date){
20659         this.fireEvent("select", this, date, picker);
20660         Roo.menu.DateItem.superclass.handleClick.call(this);
20661     }
20662 });/*
20663  * Based on:
20664  * Ext JS Library 1.1.1
20665  * Copyright(c) 2006-2007, Ext JS, LLC.
20666  *
20667  * Originally Released Under LGPL - original licence link has changed is not relivant.
20668  *
20669  * Fork - LGPL
20670  * <script type="text/javascript">
20671  */
20672  
20673 /**
20674  * @class Roo.menu.ColorItem
20675  * @extends Roo.menu.Adapter
20676  * A menu item that wraps the {@link Roo.ColorPalette} component.
20677  * @constructor
20678  * Creates a new ColorItem
20679  * @param {Object} config Configuration options
20680  */
20681 Roo.menu.ColorItem = function(config){
20682     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20683     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20684     this.palette = this.component;
20685     this.relayEvents(this.palette, ["select"]);
20686     if(this.selectHandler){
20687         this.on('select', this.selectHandler, this.scope);
20688     }
20689 };
20690 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20691  * Based on:
20692  * Ext JS Library 1.1.1
20693  * Copyright(c) 2006-2007, Ext JS, LLC.
20694  *
20695  * Originally Released Under LGPL - original licence link has changed is not relivant.
20696  *
20697  * Fork - LGPL
20698  * <script type="text/javascript">
20699  */
20700  
20701
20702 /**
20703  * @class Roo.menu.DateMenu
20704  * @extends Roo.menu.Menu
20705  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20706  * @constructor
20707  * Creates a new DateMenu
20708  * @param {Object} config Configuration options
20709  */
20710 Roo.menu.DateMenu = function(config){
20711     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20712     this.plain = true;
20713     var di = new Roo.menu.DateItem(config);
20714     this.add(di);
20715     /**
20716      * The {@link Roo.DatePicker} instance for this DateMenu
20717      * @type DatePicker
20718      */
20719     this.picker = di.picker;
20720     /**
20721      * @event select
20722      * @param {DatePicker} picker
20723      * @param {Date} date
20724      */
20725     this.relayEvents(di, ["select"]);
20726
20727     this.on('beforeshow', function(){
20728         if(this.picker){
20729             this.picker.hideMonthPicker(true);
20730         }
20731     }, this);
20732 };
20733 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20734     cls:'x-date-menu'
20735 });/*
20736  * Based on:
20737  * Ext JS Library 1.1.1
20738  * Copyright(c) 2006-2007, Ext JS, LLC.
20739  *
20740  * Originally Released Under LGPL - original licence link has changed is not relivant.
20741  *
20742  * Fork - LGPL
20743  * <script type="text/javascript">
20744  */
20745  
20746
20747 /**
20748  * @class Roo.menu.ColorMenu
20749  * @extends Roo.menu.Menu
20750  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20751  * @constructor
20752  * Creates a new ColorMenu
20753  * @param {Object} config Configuration options
20754  */
20755 Roo.menu.ColorMenu = function(config){
20756     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20757     this.plain = true;
20758     var ci = new Roo.menu.ColorItem(config);
20759     this.add(ci);
20760     /**
20761      * The {@link Roo.ColorPalette} instance for this ColorMenu
20762      * @type ColorPalette
20763      */
20764     this.palette = ci.palette;
20765     /**
20766      * @event select
20767      * @param {ColorPalette} palette
20768      * @param {String} color
20769      */
20770     this.relayEvents(ci, ["select"]);
20771 };
20772 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20773  * Based on:
20774  * Ext JS Library 1.1.1
20775  * Copyright(c) 2006-2007, Ext JS, LLC.
20776  *
20777  * Originally Released Under LGPL - original licence link has changed is not relivant.
20778  *
20779  * Fork - LGPL
20780  * <script type="text/javascript">
20781  */
20782  
20783 /**
20784  * @class Roo.form.Field
20785  * @extends Roo.BoxComponent
20786  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20787  * @constructor
20788  * Creates a new Field
20789  * @param {Object} config Configuration options
20790  */
20791 Roo.form.Field = function(config){
20792     Roo.form.Field.superclass.constructor.call(this, config);
20793 };
20794
20795 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20796     /**
20797      * @cfg {String} fieldLabel Label to use when rendering a form.
20798      */
20799        /**
20800      * @cfg {String} qtip Mouse over tip
20801      */
20802      
20803     /**
20804      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20805      */
20806     invalidClass : "x-form-invalid",
20807     /**
20808      * @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")
20809      */
20810     invalidText : "The value in this field is invalid",
20811     /**
20812      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20813      */
20814     focusClass : "x-form-focus",
20815     /**
20816      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20817       automatic validation (defaults to "keyup").
20818      */
20819     validationEvent : "keyup",
20820     /**
20821      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20822      */
20823     validateOnBlur : true,
20824     /**
20825      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20826      */
20827     validationDelay : 250,
20828     /**
20829      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20830      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20831      */
20832     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20833     /**
20834      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20835      */
20836     fieldClass : "x-form-field",
20837     /**
20838      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20839      *<pre>
20840 Value         Description
20841 -----------   ----------------------------------------------------------------------
20842 qtip          Display a quick tip when the user hovers over the field
20843 title         Display a default browser title attribute popup
20844 under         Add a block div beneath the field containing the error text
20845 side          Add an error icon to the right of the field with a popup on hover
20846 [element id]  Add the error text directly to the innerHTML of the specified element
20847 </pre>
20848      */
20849     msgTarget : 'qtip',
20850     /**
20851      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20852      */
20853     msgFx : 'normal',
20854
20855     /**
20856      * @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.
20857      */
20858     readOnly : false,
20859
20860     /**
20861      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20862      */
20863     disabled : false,
20864
20865     /**
20866      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20867      */
20868     inputType : undefined,
20869     
20870     /**
20871      * @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).
20872          */
20873         tabIndex : undefined,
20874         
20875     // private
20876     isFormField : true,
20877
20878     // private
20879     hasFocus : false,
20880     /**
20881      * @property {Roo.Element} fieldEl
20882      * Element Containing the rendered Field (with label etc.)
20883      */
20884     /**
20885      * @cfg {Mixed} value A value to initialize this field with.
20886      */
20887     value : undefined,
20888
20889     /**
20890      * @cfg {String} name The field's HTML name attribute.
20891      */
20892     /**
20893      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20894      */
20895
20896         // private ??
20897         initComponent : function(){
20898         Roo.form.Field.superclass.initComponent.call(this);
20899         this.addEvents({
20900             /**
20901              * @event focus
20902              * Fires when this field receives input focus.
20903              * @param {Roo.form.Field} this
20904              */
20905             focus : true,
20906             /**
20907              * @event blur
20908              * Fires when this field loses input focus.
20909              * @param {Roo.form.Field} this
20910              */
20911             blur : true,
20912             /**
20913              * @event specialkey
20914              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20915              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20916              * @param {Roo.form.Field} this
20917              * @param {Roo.EventObject} e The event object
20918              */
20919             specialkey : true,
20920             /**
20921              * @event change
20922              * Fires just before the field blurs if the field value has changed.
20923              * @param {Roo.form.Field} this
20924              * @param {Mixed} newValue The new value
20925              * @param {Mixed} oldValue The original value
20926              */
20927             change : true,
20928             /**
20929              * @event invalid
20930              * Fires after the field has been marked as invalid.
20931              * @param {Roo.form.Field} this
20932              * @param {String} msg The validation message
20933              */
20934             invalid : true,
20935             /**
20936              * @event valid
20937              * Fires after the field has been validated with no errors.
20938              * @param {Roo.form.Field} this
20939              */
20940             valid : true,
20941              /**
20942              * @event keyup
20943              * Fires after the key up
20944              * @param {Roo.form.Field} this
20945              * @param {Roo.EventObject}  e The event Object
20946              */
20947             keyup : true
20948         });
20949     },
20950
20951     /**
20952      * Returns the name attribute of the field if available
20953      * @return {String} name The field name
20954      */
20955     getName: function(){
20956          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20957     },
20958
20959     // private
20960     onRender : function(ct, position){
20961         Roo.form.Field.superclass.onRender.call(this, ct, position);
20962         if(!this.el){
20963             var cfg = this.getAutoCreate();
20964             if(!cfg.name){
20965                 cfg.name = this.name || this.id;
20966             }
20967             if(this.inputType){
20968                 cfg.type = this.inputType;
20969             }
20970             this.el = ct.createChild(cfg, position);
20971         }
20972         var type = this.el.dom.type;
20973         if(type){
20974             if(type == 'password'){
20975                 type = 'text';
20976             }
20977             this.el.addClass('x-form-'+type);
20978         }
20979         if(this.readOnly){
20980             this.el.dom.readOnly = true;
20981         }
20982         if(this.tabIndex !== undefined){
20983             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20984         }
20985
20986         this.el.addClass([this.fieldClass, this.cls]);
20987         this.initValue();
20988     },
20989
20990     /**
20991      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20992      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20993      * @return {Roo.form.Field} this
20994      */
20995     applyTo : function(target){
20996         this.allowDomMove = false;
20997         this.el = Roo.get(target);
20998         this.render(this.el.dom.parentNode);
20999         return this;
21000     },
21001
21002     // private
21003     initValue : function(){
21004         if(this.value !== undefined){
21005             this.setValue(this.value);
21006         }else if(this.el.dom.value.length > 0){
21007             this.setValue(this.el.dom.value);
21008         }
21009     },
21010
21011     /**
21012      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21013      */
21014     isDirty : function() {
21015         if(this.disabled) {
21016             return false;
21017         }
21018         return String(this.getValue()) !== String(this.originalValue);
21019     },
21020
21021     // private
21022     afterRender : function(){
21023         Roo.form.Field.superclass.afterRender.call(this);
21024         this.initEvents();
21025     },
21026
21027     // private
21028     fireKey : function(e){
21029         //Roo.log('field ' + e.getKey());
21030         if(e.isNavKeyPress()){
21031             this.fireEvent("specialkey", this, e);
21032         }
21033     },
21034
21035     /**
21036      * Resets the current field value to the originally loaded value and clears any validation messages
21037      */
21038     reset : function(){
21039         this.setValue(this.originalValue);
21040         this.clearInvalid();
21041     },
21042
21043     // private
21044     initEvents : function(){
21045         // safari killled keypress - so keydown is now used..
21046         this.el.on("keydown" , this.fireKey,  this);
21047         this.el.on("focus", this.onFocus,  this);
21048         this.el.on("blur", this.onBlur,  this);
21049         this.el.relayEvent('keyup', this);
21050
21051         // reference to original value for reset
21052         this.originalValue = this.getValue();
21053     },
21054
21055     // private
21056     onFocus : function(){
21057         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21058             this.el.addClass(this.focusClass);
21059         }
21060         if(!this.hasFocus){
21061             this.hasFocus = true;
21062             this.startValue = this.getValue();
21063             this.fireEvent("focus", this);
21064         }
21065     },
21066
21067     beforeBlur : Roo.emptyFn,
21068
21069     // private
21070     onBlur : function(){
21071         this.beforeBlur();
21072         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21073             this.el.removeClass(this.focusClass);
21074         }
21075         this.hasFocus = false;
21076         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21077             this.validate();
21078         }
21079         var v = this.getValue();
21080         if(String(v) !== String(this.startValue)){
21081             this.fireEvent('change', this, v, this.startValue);
21082         }
21083         this.fireEvent("blur", this);
21084     },
21085
21086     /**
21087      * Returns whether or not the field value is currently valid
21088      * @param {Boolean} preventMark True to disable marking the field invalid
21089      * @return {Boolean} True if the value is valid, else false
21090      */
21091     isValid : function(preventMark){
21092         if(this.disabled){
21093             return true;
21094         }
21095         var restore = this.preventMark;
21096         this.preventMark = preventMark === true;
21097         var v = this.validateValue(this.processValue(this.getRawValue()));
21098         this.preventMark = restore;
21099         return v;
21100     },
21101
21102     /**
21103      * Validates the field value
21104      * @return {Boolean} True if the value is valid, else false
21105      */
21106     validate : function(){
21107         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21108             this.clearInvalid();
21109             return true;
21110         }
21111         return false;
21112     },
21113
21114     processValue : function(value){
21115         return value;
21116     },
21117
21118     // private
21119     // Subclasses should provide the validation implementation by overriding this
21120     validateValue : function(value){
21121         return true;
21122     },
21123
21124     /**
21125      * Mark this field as invalid
21126      * @param {String} msg The validation message
21127      */
21128     markInvalid : function(msg){
21129         if(!this.rendered || this.preventMark){ // not rendered
21130             return;
21131         }
21132         this.el.addClass(this.invalidClass);
21133         msg = msg || this.invalidText;
21134         switch(this.msgTarget){
21135             case 'qtip':
21136                 this.el.dom.qtip = msg;
21137                 this.el.dom.qclass = 'x-form-invalid-tip';
21138                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21139                     Roo.QuickTips.enable();
21140                 }
21141                 break;
21142             case 'title':
21143                 this.el.dom.title = msg;
21144                 break;
21145             case 'under':
21146                 if(!this.errorEl){
21147                     var elp = this.el.findParent('.x-form-element', 5, true);
21148                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21149                     this.errorEl.setWidth(elp.getWidth(true)-20);
21150                 }
21151                 this.errorEl.update(msg);
21152                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21153                 break;
21154             case 'side':
21155                 if(!this.errorIcon){
21156                     var elp = this.el.findParent('.x-form-element', 5, true);
21157                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21158                 }
21159                 this.alignErrorIcon();
21160                 this.errorIcon.dom.qtip = msg;
21161                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21162                 this.errorIcon.show();
21163                 this.on('resize', this.alignErrorIcon, this);
21164                 break;
21165             default:
21166                 var t = Roo.getDom(this.msgTarget);
21167                 t.innerHTML = msg;
21168                 t.style.display = this.msgDisplay;
21169                 break;
21170         }
21171         this.fireEvent('invalid', this, msg);
21172     },
21173
21174     // private
21175     alignErrorIcon : function(){
21176         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21177     },
21178
21179     /**
21180      * Clear any invalid styles/messages for this field
21181      */
21182     clearInvalid : function(){
21183         if(!this.rendered || this.preventMark){ // not rendered
21184             return;
21185         }
21186         this.el.removeClass(this.invalidClass);
21187         switch(this.msgTarget){
21188             case 'qtip':
21189                 this.el.dom.qtip = '';
21190                 break;
21191             case 'title':
21192                 this.el.dom.title = '';
21193                 break;
21194             case 'under':
21195                 if(this.errorEl){
21196                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21197                 }
21198                 break;
21199             case 'side':
21200                 if(this.errorIcon){
21201                     this.errorIcon.dom.qtip = '';
21202                     this.errorIcon.hide();
21203                     this.un('resize', this.alignErrorIcon, this);
21204                 }
21205                 break;
21206             default:
21207                 var t = Roo.getDom(this.msgTarget);
21208                 t.innerHTML = '';
21209                 t.style.display = 'none';
21210                 break;
21211         }
21212         this.fireEvent('valid', this);
21213     },
21214
21215     /**
21216      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21217      * @return {Mixed} value The field value
21218      */
21219     getRawValue : function(){
21220         var v = this.el.getValue();
21221         if(v === this.emptyText){
21222             v = '';
21223         }
21224         return v;
21225     },
21226
21227     /**
21228      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21229      * @return {Mixed} value The field value
21230      */
21231     getValue : function(){
21232         var v = this.el.getValue();
21233         if(v === this.emptyText || v === undefined){
21234             v = '';
21235         }
21236         return v;
21237     },
21238
21239     /**
21240      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21241      * @param {Mixed} value The value to set
21242      */
21243     setRawValue : function(v){
21244         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21245     },
21246
21247     /**
21248      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21249      * @param {Mixed} value The value to set
21250      */
21251     setValue : function(v){
21252         this.value = v;
21253         if(this.rendered){
21254             this.el.dom.value = (v === null || v === undefined ? '' : v);
21255             this.validate();
21256         }
21257     },
21258
21259     adjustSize : function(w, h){
21260         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21261         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21262         return s;
21263     },
21264
21265     adjustWidth : function(tag, w){
21266         tag = tag.toLowerCase();
21267         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21268             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21269                 if(tag == 'input'){
21270                     return w + 2;
21271                 }
21272                 if(tag = 'textarea'){
21273                     return w-2;
21274                 }
21275             }else if(Roo.isOpera){
21276                 if(tag == 'input'){
21277                     return w + 2;
21278                 }
21279                 if(tag = 'textarea'){
21280                     return w-2;
21281                 }
21282             }
21283         }
21284         return w;
21285     }
21286 });
21287
21288
21289 // anything other than normal should be considered experimental
21290 Roo.form.Field.msgFx = {
21291     normal : {
21292         show: function(msgEl, f){
21293             msgEl.setDisplayed('block');
21294         },
21295
21296         hide : function(msgEl, f){
21297             msgEl.setDisplayed(false).update('');
21298         }
21299     },
21300
21301     slide : {
21302         show: function(msgEl, f){
21303             msgEl.slideIn('t', {stopFx:true});
21304         },
21305
21306         hide : function(msgEl, f){
21307             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21308         }
21309     },
21310
21311     slideRight : {
21312         show: function(msgEl, f){
21313             msgEl.fixDisplay();
21314             msgEl.alignTo(f.el, 'tl-tr');
21315             msgEl.slideIn('l', {stopFx:true});
21316         },
21317
21318         hide : function(msgEl, f){
21319             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21320         }
21321     }
21322 };/*
21323  * Based on:
21324  * Ext JS Library 1.1.1
21325  * Copyright(c) 2006-2007, Ext JS, LLC.
21326  *
21327  * Originally Released Under LGPL - original licence link has changed is not relivant.
21328  *
21329  * Fork - LGPL
21330  * <script type="text/javascript">
21331  */
21332  
21333
21334 /**
21335  * @class Roo.form.TextField
21336  * @extends Roo.form.Field
21337  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21338  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21339  * @constructor
21340  * Creates a new TextField
21341  * @param {Object} config Configuration options
21342  */
21343 Roo.form.TextField = function(config){
21344     Roo.form.TextField.superclass.constructor.call(this, config);
21345     this.addEvents({
21346         /**
21347          * @event autosize
21348          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21349          * according to the default logic, but this event provides a hook for the developer to apply additional
21350          * logic at runtime to resize the field if needed.
21351              * @param {Roo.form.Field} this This text field
21352              * @param {Number} width The new field width
21353              */
21354         autosize : true
21355     });
21356 };
21357
21358 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21359     /**
21360      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21361      */
21362     grow : false,
21363     /**
21364      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21365      */
21366     growMin : 30,
21367     /**
21368      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21369      */
21370     growMax : 800,
21371     /**
21372      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21373      */
21374     vtype : null,
21375     /**
21376      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21377      */
21378     maskRe : null,
21379     /**
21380      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21381      */
21382     disableKeyFilter : false,
21383     /**
21384      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21385      */
21386     allowBlank : true,
21387     /**
21388      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21389      */
21390     minLength : 0,
21391     /**
21392      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21393      */
21394     maxLength : Number.MAX_VALUE,
21395     /**
21396      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21397      */
21398     minLengthText : "The minimum length for this field is {0}",
21399     /**
21400      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21401      */
21402     maxLengthText : "The maximum length for this field is {0}",
21403     /**
21404      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21405      */
21406     selectOnFocus : false,
21407     /**
21408      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21409      */
21410     blankText : "This field is required",
21411     /**
21412      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21413      * If available, this function will be called only after the basic validators all return true, and will be passed the
21414      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21415      */
21416     validator : null,
21417     /**
21418      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21419      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21420      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21421      */
21422     regex : null,
21423     /**
21424      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21425      */
21426     regexText : "",
21427     /**
21428      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21429      */
21430     emptyText : null,
21431     /**
21432      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21433      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21434      */
21435     emptyClass : 'x-form-empty-field',
21436
21437     // private
21438     initEvents : function(){
21439         Roo.form.TextField.superclass.initEvents.call(this);
21440         if(this.validationEvent == 'keyup'){
21441             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21442             this.el.on('keyup', this.filterValidation, this);
21443         }
21444         else if(this.validationEvent !== false){
21445             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21446         }
21447         if(this.selectOnFocus || this.emptyText){
21448             this.on("focus", this.preFocus, this);
21449             if(this.emptyText){
21450                 this.on('blur', this.postBlur, this);
21451                 this.applyEmptyText();
21452             }
21453         }
21454         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21455             this.el.on("keypress", this.filterKeys, this);
21456         }
21457         if(this.grow){
21458             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21459             this.el.on("click", this.autoSize,  this);
21460         }
21461     },
21462
21463     processValue : function(value){
21464         if(this.stripCharsRe){
21465             var newValue = value.replace(this.stripCharsRe, '');
21466             if(newValue !== value){
21467                 this.setRawValue(newValue);
21468                 return newValue;
21469             }
21470         }
21471         return value;
21472     },
21473
21474     filterValidation : function(e){
21475         if(!e.isNavKeyPress()){
21476             this.validationTask.delay(this.validationDelay);
21477         }
21478     },
21479
21480     // private
21481     onKeyUp : function(e){
21482         if(!e.isNavKeyPress()){
21483             this.autoSize();
21484         }
21485     },
21486
21487     /**
21488      * Resets the current field value to the originally-loaded value and clears any validation messages.
21489      * Also adds emptyText and emptyClass if the original value was blank.
21490      */
21491     reset : function(){
21492         Roo.form.TextField.superclass.reset.call(this);
21493         this.applyEmptyText();
21494     },
21495
21496     applyEmptyText : function(){
21497         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21498             this.setRawValue(this.emptyText);
21499             this.el.addClass(this.emptyClass);
21500         }
21501     },
21502
21503     // private
21504     preFocus : function(){
21505         if(this.emptyText){
21506             if(this.el.dom.value == this.emptyText){
21507                 this.setRawValue('');
21508             }
21509             this.el.removeClass(this.emptyClass);
21510         }
21511         if(this.selectOnFocus){
21512             this.el.dom.select();
21513         }
21514     },
21515
21516     // private
21517     postBlur : function(){
21518         this.applyEmptyText();
21519     },
21520
21521     // private
21522     filterKeys : function(e){
21523         var k = e.getKey();
21524         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21525             return;
21526         }
21527         var c = e.getCharCode(), cc = String.fromCharCode(c);
21528         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21529             return;
21530         }
21531         if(!this.maskRe.test(cc)){
21532             e.stopEvent();
21533         }
21534     },
21535
21536     setValue : function(v){
21537         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21538             this.el.removeClass(this.emptyClass);
21539         }
21540         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21541         this.applyEmptyText();
21542         this.autoSize();
21543     },
21544
21545     /**
21546      * Validates a value according to the field's validation rules and marks the field as invalid
21547      * if the validation fails
21548      * @param {Mixed} value The value to validate
21549      * @return {Boolean} True if the value is valid, else false
21550      */
21551     validateValue : function(value){
21552         if(value.length < 1 || value === this.emptyText){ // if it's blank
21553              if(this.allowBlank){
21554                 this.clearInvalid();
21555                 return true;
21556              }else{
21557                 this.markInvalid(this.blankText);
21558                 return false;
21559              }
21560         }
21561         if(value.length < this.minLength){
21562             this.markInvalid(String.format(this.minLengthText, this.minLength));
21563             return false;
21564         }
21565         if(value.length > this.maxLength){
21566             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21567             return false;
21568         }
21569         if(this.vtype){
21570             var vt = Roo.form.VTypes;
21571             if(!vt[this.vtype](value, this)){
21572                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21573                 return false;
21574             }
21575         }
21576         if(typeof this.validator == "function"){
21577             var msg = this.validator(value);
21578             if(msg !== true){
21579                 this.markInvalid(msg);
21580                 return false;
21581             }
21582         }
21583         if(this.regex && !this.regex.test(value)){
21584             this.markInvalid(this.regexText);
21585             return false;
21586         }
21587         return true;
21588     },
21589
21590     /**
21591      * Selects text in this field
21592      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21593      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21594      */
21595     selectText : function(start, end){
21596         var v = this.getRawValue();
21597         if(v.length > 0){
21598             start = start === undefined ? 0 : start;
21599             end = end === undefined ? v.length : end;
21600             var d = this.el.dom;
21601             if(d.setSelectionRange){
21602                 d.setSelectionRange(start, end);
21603             }else if(d.createTextRange){
21604                 var range = d.createTextRange();
21605                 range.moveStart("character", start);
21606                 range.moveEnd("character", v.length-end);
21607                 range.select();
21608             }
21609         }
21610     },
21611
21612     /**
21613      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21614      * This only takes effect if grow = true, and fires the autosize event.
21615      */
21616     autoSize : function(){
21617         if(!this.grow || !this.rendered){
21618             return;
21619         }
21620         if(!this.metrics){
21621             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21622         }
21623         var el = this.el;
21624         var v = el.dom.value;
21625         var d = document.createElement('div');
21626         d.appendChild(document.createTextNode(v));
21627         v = d.innerHTML;
21628         d = null;
21629         v += "&#160;";
21630         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21631         this.el.setWidth(w);
21632         this.fireEvent("autosize", this, w);
21633     }
21634 });/*
21635  * Based on:
21636  * Ext JS Library 1.1.1
21637  * Copyright(c) 2006-2007, Ext JS, LLC.
21638  *
21639  * Originally Released Under LGPL - original licence link has changed is not relivant.
21640  *
21641  * Fork - LGPL
21642  * <script type="text/javascript">
21643  */
21644  
21645 /**
21646  * @class Roo.form.Hidden
21647  * @extends Roo.form.TextField
21648  * Simple Hidden element used on forms 
21649  * 
21650  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21651  * 
21652  * @constructor
21653  * Creates a new Hidden form element.
21654  * @param {Object} config Configuration options
21655  */
21656
21657
21658
21659 // easy hidden field...
21660 Roo.form.Hidden = function(config){
21661     Roo.form.Hidden.superclass.constructor.call(this, config);
21662 };
21663   
21664 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21665     fieldLabel:      '',
21666     inputType:      'hidden',
21667     width:          50,
21668     allowBlank:     true,
21669     labelSeparator: '',
21670     hidden:         true,
21671     itemCls :       'x-form-item-display-none'
21672
21673
21674 });
21675
21676
21677 /*
21678  * Based on:
21679  * Ext JS Library 1.1.1
21680  * Copyright(c) 2006-2007, Ext JS, LLC.
21681  *
21682  * Originally Released Under LGPL - original licence link has changed is not relivant.
21683  *
21684  * Fork - LGPL
21685  * <script type="text/javascript">
21686  */
21687  
21688 /**
21689  * @class Roo.form.TriggerField
21690  * @extends Roo.form.TextField
21691  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21692  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21693  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21694  * for which you can provide a custom implementation.  For example:
21695  * <pre><code>
21696 var trigger = new Roo.form.TriggerField();
21697 trigger.onTriggerClick = myTriggerFn;
21698 trigger.applyTo('my-field');
21699 </code></pre>
21700  *
21701  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21702  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21703  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21704  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21705  * @constructor
21706  * Create a new TriggerField.
21707  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21708  * to the base TextField)
21709  */
21710 Roo.form.TriggerField = function(config){
21711     this.mimicing = false;
21712     Roo.form.TriggerField.superclass.constructor.call(this, config);
21713 };
21714
21715 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21716     /**
21717      * @cfg {String} triggerClass A CSS class to apply to the trigger
21718      */
21719     /**
21720      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21721      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21722      */
21723     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21724     /**
21725      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21726      */
21727     hideTrigger:false,
21728
21729     /** @cfg {Boolean} grow @hide */
21730     /** @cfg {Number} growMin @hide */
21731     /** @cfg {Number} growMax @hide */
21732
21733     /**
21734      * @hide 
21735      * @method
21736      */
21737     autoSize: Roo.emptyFn,
21738     // private
21739     monitorTab : true,
21740     // private
21741     deferHeight : true,
21742
21743     
21744     actionMode : 'wrap',
21745     // private
21746     onResize : function(w, h){
21747         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21748         if(typeof w == 'number'){
21749             var x = w - this.trigger.getWidth();
21750             this.el.setWidth(this.adjustWidth('input', x));
21751             this.trigger.setStyle('left', x+'px');
21752         }
21753     },
21754
21755     // private
21756     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21757
21758     // private
21759     getResizeEl : function(){
21760         return this.wrap;
21761     },
21762
21763     // private
21764     getPositionEl : function(){
21765         return this.wrap;
21766     },
21767
21768     // private
21769     alignErrorIcon : function(){
21770         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21771     },
21772
21773     // private
21774     onRender : function(ct, position){
21775         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21776         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21777         this.trigger = this.wrap.createChild(this.triggerConfig ||
21778                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21779         if(this.hideTrigger){
21780             this.trigger.setDisplayed(false);
21781         }
21782         this.initTrigger();
21783         if(!this.width){
21784             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21785         }
21786     },
21787
21788     // private
21789     initTrigger : function(){
21790         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21791         this.trigger.addClassOnOver('x-form-trigger-over');
21792         this.trigger.addClassOnClick('x-form-trigger-click');
21793     },
21794
21795     // private
21796     onDestroy : function(){
21797         if(this.trigger){
21798             this.trigger.removeAllListeners();
21799             this.trigger.remove();
21800         }
21801         if(this.wrap){
21802             this.wrap.remove();
21803         }
21804         Roo.form.TriggerField.superclass.onDestroy.call(this);
21805     },
21806
21807     // private
21808     onFocus : function(){
21809         Roo.form.TriggerField.superclass.onFocus.call(this);
21810         if(!this.mimicing){
21811             this.wrap.addClass('x-trigger-wrap-focus');
21812             this.mimicing = true;
21813             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21814             if(this.monitorTab){
21815                 this.el.on("keydown", this.checkTab, this);
21816             }
21817         }
21818     },
21819
21820     // private
21821     checkTab : function(e){
21822         if(e.getKey() == e.TAB){
21823             this.triggerBlur();
21824         }
21825     },
21826
21827     // private
21828     onBlur : function(){
21829         // do nothing
21830     },
21831
21832     // private
21833     mimicBlur : function(e, t){
21834         if(!this.wrap.contains(t) && this.validateBlur()){
21835             this.triggerBlur();
21836         }
21837     },
21838
21839     // private
21840     triggerBlur : function(){
21841         this.mimicing = false;
21842         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21843         if(this.monitorTab){
21844             this.el.un("keydown", this.checkTab, this);
21845         }
21846         this.wrap.removeClass('x-trigger-wrap-focus');
21847         Roo.form.TriggerField.superclass.onBlur.call(this);
21848     },
21849
21850     // private
21851     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21852     validateBlur : function(e, t){
21853         return true;
21854     },
21855
21856     // private
21857     onDisable : function(){
21858         Roo.form.TriggerField.superclass.onDisable.call(this);
21859         if(this.wrap){
21860             this.wrap.addClass('x-item-disabled');
21861         }
21862     },
21863
21864     // private
21865     onEnable : function(){
21866         Roo.form.TriggerField.superclass.onEnable.call(this);
21867         if(this.wrap){
21868             this.wrap.removeClass('x-item-disabled');
21869         }
21870     },
21871
21872     // private
21873     onShow : function(){
21874         var ae = this.getActionEl();
21875         
21876         if(ae){
21877             ae.dom.style.display = '';
21878             ae.dom.style.visibility = 'visible';
21879         }
21880     },
21881
21882     // private
21883     
21884     onHide : function(){
21885         var ae = this.getActionEl();
21886         ae.dom.style.display = 'none';
21887     },
21888
21889     /**
21890      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21891      * by an implementing function.
21892      * @method
21893      * @param {EventObject} e
21894      */
21895     onTriggerClick : Roo.emptyFn
21896 });
21897
21898 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21899 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21900 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21901 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21902     initComponent : function(){
21903         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21904
21905         this.triggerConfig = {
21906             tag:'span', cls:'x-form-twin-triggers', cn:[
21907             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21908             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21909         ]};
21910     },
21911
21912     getTrigger : function(index){
21913         return this.triggers[index];
21914     },
21915
21916     initTrigger : function(){
21917         var ts = this.trigger.select('.x-form-trigger', true);
21918         this.wrap.setStyle('overflow', 'hidden');
21919         var triggerField = this;
21920         ts.each(function(t, all, index){
21921             t.hide = function(){
21922                 var w = triggerField.wrap.getWidth();
21923                 this.dom.style.display = 'none';
21924                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21925             };
21926             t.show = function(){
21927                 var w = triggerField.wrap.getWidth();
21928                 this.dom.style.display = '';
21929                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21930             };
21931             var triggerIndex = 'Trigger'+(index+1);
21932
21933             if(this['hide'+triggerIndex]){
21934                 t.dom.style.display = 'none';
21935             }
21936             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21937             t.addClassOnOver('x-form-trigger-over');
21938             t.addClassOnClick('x-form-trigger-click');
21939         }, this);
21940         this.triggers = ts.elements;
21941     },
21942
21943     onTrigger1Click : Roo.emptyFn,
21944     onTrigger2Click : Roo.emptyFn
21945 });/*
21946  * Based on:
21947  * Ext JS Library 1.1.1
21948  * Copyright(c) 2006-2007, Ext JS, LLC.
21949  *
21950  * Originally Released Under LGPL - original licence link has changed is not relivant.
21951  *
21952  * Fork - LGPL
21953  * <script type="text/javascript">
21954  */
21955  
21956 /**
21957  * @class Roo.form.TextArea
21958  * @extends Roo.form.TextField
21959  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21960  * support for auto-sizing.
21961  * @constructor
21962  * Creates a new TextArea
21963  * @param {Object} config Configuration options
21964  */
21965 Roo.form.TextArea = function(config){
21966     Roo.form.TextArea.superclass.constructor.call(this, config);
21967     // these are provided exchanges for backwards compat
21968     // minHeight/maxHeight were replaced by growMin/growMax to be
21969     // compatible with TextField growing config values
21970     if(this.minHeight !== undefined){
21971         this.growMin = this.minHeight;
21972     }
21973     if(this.maxHeight !== undefined){
21974         this.growMax = this.maxHeight;
21975     }
21976 };
21977
21978 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21979     /**
21980      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21981      */
21982     growMin : 60,
21983     /**
21984      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21985      */
21986     growMax: 1000,
21987     /**
21988      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21989      * in the field (equivalent to setting overflow: hidden, defaults to false)
21990      */
21991     preventScrollbars: false,
21992     /**
21993      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21994      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
21995      */
21996
21997     // private
21998     onRender : function(ct, position){
21999         if(!this.el){
22000             this.defaultAutoCreate = {
22001                 tag: "textarea",
22002                 style:"width:300px;height:60px;",
22003                 autocomplete: "off"
22004             };
22005         }
22006         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22007         if(this.grow){
22008             this.textSizeEl = Roo.DomHelper.append(document.body, {
22009                 tag: "pre", cls: "x-form-grow-sizer"
22010             });
22011             if(this.preventScrollbars){
22012                 this.el.setStyle("overflow", "hidden");
22013             }
22014             this.el.setHeight(this.growMin);
22015         }
22016     },
22017
22018     onDestroy : function(){
22019         if(this.textSizeEl){
22020             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22021         }
22022         Roo.form.TextArea.superclass.onDestroy.call(this);
22023     },
22024
22025     // private
22026     onKeyUp : function(e){
22027         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22028             this.autoSize();
22029         }
22030     },
22031
22032     /**
22033      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22034      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22035      */
22036     autoSize : function(){
22037         if(!this.grow || !this.textSizeEl){
22038             return;
22039         }
22040         var el = this.el;
22041         var v = el.dom.value;
22042         var ts = this.textSizeEl;
22043
22044         ts.innerHTML = '';
22045         ts.appendChild(document.createTextNode(v));
22046         v = ts.innerHTML;
22047
22048         Roo.fly(ts).setWidth(this.el.getWidth());
22049         if(v.length < 1){
22050             v = "&#160;&#160;";
22051         }else{
22052             if(Roo.isIE){
22053                 v = v.replace(/\n/g, '<p>&#160;</p>');
22054             }
22055             v += "&#160;\n&#160;";
22056         }
22057         ts.innerHTML = v;
22058         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22059         if(h != this.lastHeight){
22060             this.lastHeight = h;
22061             this.el.setHeight(h);
22062             this.fireEvent("autosize", this, h);
22063         }
22064     }
22065 });/*
22066  * Based on:
22067  * Ext JS Library 1.1.1
22068  * Copyright(c) 2006-2007, Ext JS, LLC.
22069  *
22070  * Originally Released Under LGPL - original licence link has changed is not relivant.
22071  *
22072  * Fork - LGPL
22073  * <script type="text/javascript">
22074  */
22075  
22076
22077 /**
22078  * @class Roo.form.NumberField
22079  * @extends Roo.form.TextField
22080  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22081  * @constructor
22082  * Creates a new NumberField
22083  * @param {Object} config Configuration options
22084  */
22085 Roo.form.NumberField = function(config){
22086     Roo.form.NumberField.superclass.constructor.call(this, config);
22087 };
22088
22089 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22090     /**
22091      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22092      */
22093     fieldClass: "x-form-field x-form-num-field",
22094     /**
22095      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22096      */
22097     allowDecimals : true,
22098     /**
22099      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22100      */
22101     decimalSeparator : ".",
22102     /**
22103      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22104      */
22105     decimalPrecision : 2,
22106     /**
22107      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22108      */
22109     allowNegative : true,
22110     /**
22111      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22112      */
22113     minValue : Number.NEGATIVE_INFINITY,
22114     /**
22115      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22116      */
22117     maxValue : Number.MAX_VALUE,
22118     /**
22119      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22120      */
22121     minText : "The minimum value for this field is {0}",
22122     /**
22123      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22124      */
22125     maxText : "The maximum value for this field is {0}",
22126     /**
22127      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22128      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22129      */
22130     nanText : "{0} is not a valid number",
22131
22132     // private
22133     initEvents : function(){
22134         Roo.form.NumberField.superclass.initEvents.call(this);
22135         var allowed = "0123456789";
22136         if(this.allowDecimals){
22137             allowed += this.decimalSeparator;
22138         }
22139         if(this.allowNegative){
22140             allowed += "-";
22141         }
22142         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22143         var keyPress = function(e){
22144             var k = e.getKey();
22145             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22146                 return;
22147             }
22148             var c = e.getCharCode();
22149             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22150                 e.stopEvent();
22151             }
22152         };
22153         this.el.on("keypress", keyPress, this);
22154     },
22155
22156     // private
22157     validateValue : function(value){
22158         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22159             return false;
22160         }
22161         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22162              return true;
22163         }
22164         var num = this.parseValue(value);
22165         if(isNaN(num)){
22166             this.markInvalid(String.format(this.nanText, value));
22167             return false;
22168         }
22169         if(num < this.minValue){
22170             this.markInvalid(String.format(this.minText, this.minValue));
22171             return false;
22172         }
22173         if(num > this.maxValue){
22174             this.markInvalid(String.format(this.maxText, this.maxValue));
22175             return false;
22176         }
22177         return true;
22178     },
22179
22180     getValue : function(){
22181         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22182     },
22183
22184     // private
22185     parseValue : function(value){
22186         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22187         return isNaN(value) ? '' : value;
22188     },
22189
22190     // private
22191     fixPrecision : function(value){
22192         var nan = isNaN(value);
22193         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22194             return nan ? '' : value;
22195         }
22196         return parseFloat(value).toFixed(this.decimalPrecision);
22197     },
22198
22199     setValue : function(v){
22200         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22201     },
22202
22203     // private
22204     decimalPrecisionFcn : function(v){
22205         return Math.floor(v);
22206     },
22207
22208     beforeBlur : function(){
22209         var v = this.parseValue(this.getRawValue());
22210         if(v){
22211             this.setValue(this.fixPrecision(v));
22212         }
22213     }
22214 });/*
22215  * Based on:
22216  * Ext JS Library 1.1.1
22217  * Copyright(c) 2006-2007, Ext JS, LLC.
22218  *
22219  * Originally Released Under LGPL - original licence link has changed is not relivant.
22220  *
22221  * Fork - LGPL
22222  * <script type="text/javascript">
22223  */
22224  
22225 /**
22226  * @class Roo.form.DateField
22227  * @extends Roo.form.TriggerField
22228  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22229 * @constructor
22230 * Create a new DateField
22231 * @param {Object} config
22232  */
22233 Roo.form.DateField = function(config){
22234     Roo.form.DateField.superclass.constructor.call(this, config);
22235     
22236       this.addEvents({
22237          
22238         /**
22239          * @event select
22240          * Fires when a date is selected
22241              * @param {Roo.form.DateField} combo This combo box
22242              * @param {Date} date The date selected
22243              */
22244         'select' : true
22245          
22246     });
22247     
22248     
22249     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22250     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22251     this.ddMatch = null;
22252     if(this.disabledDates){
22253         var dd = this.disabledDates;
22254         var re = "(?:";
22255         for(var i = 0; i < dd.length; i++){
22256             re += dd[i];
22257             if(i != dd.length-1) re += "|";
22258         }
22259         this.ddMatch = new RegExp(re + ")");
22260     }
22261 };
22262
22263 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22264     /**
22265      * @cfg {String} format
22266      * The default date format string which can be overriden for localization support.  The format must be
22267      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22268      */
22269     format : "m/d/y",
22270     /**
22271      * @cfg {String} altFormats
22272      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22273      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22274      */
22275     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22276     /**
22277      * @cfg {Array} disabledDays
22278      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22279      */
22280     disabledDays : null,
22281     /**
22282      * @cfg {String} disabledDaysText
22283      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22284      */
22285     disabledDaysText : "Disabled",
22286     /**
22287      * @cfg {Array} disabledDates
22288      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22289      * expression so they are very powerful. Some examples:
22290      * <ul>
22291      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22292      * <li>["03/08", "09/16"] would disable those days for every year</li>
22293      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22294      * <li>["03/../2006"] would disable every day in March 2006</li>
22295      * <li>["^03"] would disable every day in every March</li>
22296      * </ul>
22297      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22298      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22299      */
22300     disabledDates : null,
22301     /**
22302      * @cfg {String} disabledDatesText
22303      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22304      */
22305     disabledDatesText : "Disabled",
22306     /**
22307      * @cfg {Date/String} minValue
22308      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22309      * valid format (defaults to null).
22310      */
22311     minValue : null,
22312     /**
22313      * @cfg {Date/String} maxValue
22314      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22315      * valid format (defaults to null).
22316      */
22317     maxValue : null,
22318     /**
22319      * @cfg {String} minText
22320      * The error text to display when the date in the cell is before minValue (defaults to
22321      * 'The date in this field must be after {minValue}').
22322      */
22323     minText : "The date in this field must be equal to or after {0}",
22324     /**
22325      * @cfg {String} maxText
22326      * The error text to display when the date in the cell is after maxValue (defaults to
22327      * 'The date in this field must be before {maxValue}').
22328      */
22329     maxText : "The date in this field must be equal to or before {0}",
22330     /**
22331      * @cfg {String} invalidText
22332      * The error text to display when the date in the field is invalid (defaults to
22333      * '{value} is not a valid date - it must be in the format {format}').
22334      */
22335     invalidText : "{0} is not a valid date - it must be in the format {1}",
22336     /**
22337      * @cfg {String} triggerClass
22338      * An additional CSS class used to style the trigger button.  The trigger will always get the
22339      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22340      * which displays a calendar icon).
22341      */
22342     triggerClass : 'x-form-date-trigger',
22343     
22344
22345     /**
22346      * @cfg {bool} useIso
22347      * if enabled, then the date field will use a hidden field to store the 
22348      * real value as iso formated date. default (false)
22349      */ 
22350     useIso : false,
22351     /**
22352      * @cfg {String/Object} autoCreate
22353      * A DomHelper element spec, or true for a default element spec (defaults to
22354      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22355      */ 
22356     // private
22357     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22358     
22359     // private
22360     hiddenField: false,
22361     
22362     onRender : function(ct, position)
22363     {
22364         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22365         if (this.useIso) {
22366             this.el.dom.removeAttribute('name'); 
22367             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22368                     'before', true);
22369             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22370             // prevent input submission
22371             this.hiddenName = this.name;
22372         }
22373             
22374             
22375     },
22376     
22377     // private
22378     validateValue : function(value)
22379     {
22380         value = this.formatDate(value);
22381         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22382             return false;
22383         }
22384         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22385              return true;
22386         }
22387         var svalue = value;
22388         value = this.parseDate(value);
22389         if(!value){
22390             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22391             return false;
22392         }
22393         var time = value.getTime();
22394         if(this.minValue && time < this.minValue.getTime()){
22395             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22396             return false;
22397         }
22398         if(this.maxValue && time > this.maxValue.getTime()){
22399             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22400             return false;
22401         }
22402         if(this.disabledDays){
22403             var day = value.getDay();
22404             for(var i = 0; i < this.disabledDays.length; i++) {
22405                 if(day === this.disabledDays[i]){
22406                     this.markInvalid(this.disabledDaysText);
22407                     return false;
22408                 }
22409             }
22410         }
22411         var fvalue = this.formatDate(value);
22412         if(this.ddMatch && this.ddMatch.test(fvalue)){
22413             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22414             return false;
22415         }
22416         return true;
22417     },
22418
22419     // private
22420     // Provides logic to override the default TriggerField.validateBlur which just returns true
22421     validateBlur : function(){
22422         return !this.menu || !this.menu.isVisible();
22423     },
22424
22425     /**
22426      * Returns the current date value of the date field.
22427      * @return {Date} The date value
22428      */
22429     getValue : function(){
22430         
22431         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22432     },
22433
22434     /**
22435      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22436      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22437      * (the default format used is "m/d/y").
22438      * <br />Usage:
22439      * <pre><code>
22440 //All of these calls set the same date value (May 4, 2006)
22441
22442 //Pass a date object:
22443 var dt = new Date('5/4/06');
22444 dateField.setValue(dt);
22445
22446 //Pass a date string (default format):
22447 dateField.setValue('5/4/06');
22448
22449 //Pass a date string (custom format):
22450 dateField.format = 'Y-m-d';
22451 dateField.setValue('2006-5-4');
22452 </code></pre>
22453      * @param {String/Date} date The date or valid date string
22454      */
22455     setValue : function(date){
22456         if (this.hiddenField) {
22457             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22458         }
22459         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22460     },
22461
22462     // private
22463     parseDate : function(value){
22464         if(!value || value instanceof Date){
22465             return value;
22466         }
22467         var v = Date.parseDate(value, this.format);
22468         if(!v && this.altFormats){
22469             if(!this.altFormatsArray){
22470                 this.altFormatsArray = this.altFormats.split("|");
22471             }
22472             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22473                 v = Date.parseDate(value, this.altFormatsArray[i]);
22474             }
22475         }
22476         return v;
22477     },
22478
22479     // private
22480     formatDate : function(date, fmt){
22481         return (!date || !(date instanceof Date)) ?
22482                date : date.dateFormat(fmt || this.format);
22483     },
22484
22485     // private
22486     menuListeners : {
22487         select: function(m, d){
22488             this.setValue(d);
22489             this.fireEvent('select', this, d);
22490         },
22491         show : function(){ // retain focus styling
22492             this.onFocus();
22493         },
22494         hide : function(){
22495             this.focus.defer(10, this);
22496             var ml = this.menuListeners;
22497             this.menu.un("select", ml.select,  this);
22498             this.menu.un("show", ml.show,  this);
22499             this.menu.un("hide", ml.hide,  this);
22500         }
22501     },
22502
22503     // private
22504     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22505     onTriggerClick : function(){
22506         if(this.disabled){
22507             return;
22508         }
22509         if(this.menu == null){
22510             this.menu = new Roo.menu.DateMenu();
22511         }
22512         Roo.apply(this.menu.picker,  {
22513             showClear: this.allowBlank,
22514             minDate : this.minValue,
22515             maxDate : this.maxValue,
22516             disabledDatesRE : this.ddMatch,
22517             disabledDatesText : this.disabledDatesText,
22518             disabledDays : this.disabledDays,
22519             disabledDaysText : this.disabledDaysText,
22520             format : this.format,
22521             minText : String.format(this.minText, this.formatDate(this.minValue)),
22522             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22523         });
22524         this.menu.on(Roo.apply({}, this.menuListeners, {
22525             scope:this
22526         }));
22527         this.menu.picker.setValue(this.getValue() || new Date());
22528         this.menu.show(this.el, "tl-bl?");
22529     },
22530
22531     beforeBlur : function(){
22532         var v = this.parseDate(this.getRawValue());
22533         if(v){
22534             this.setValue(v);
22535         }
22536     }
22537
22538     /** @cfg {Boolean} grow @hide */
22539     /** @cfg {Number} growMin @hide */
22540     /** @cfg {Number} growMax @hide */
22541     /**
22542      * @hide
22543      * @method autoSize
22544      */
22545 });/*
22546  * Based on:
22547  * Ext JS Library 1.1.1
22548  * Copyright(c) 2006-2007, Ext JS, LLC.
22549  *
22550  * Originally Released Under LGPL - original licence link has changed is not relivant.
22551  *
22552  * Fork - LGPL
22553  * <script type="text/javascript">
22554  */
22555  
22556
22557 /**
22558  * @class Roo.form.ComboBox
22559  * @extends Roo.form.TriggerField
22560  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22561  * @constructor
22562  * Create a new ComboBox.
22563  * @param {Object} config Configuration options
22564  */
22565 Roo.form.ComboBox = function(config){
22566     Roo.form.ComboBox.superclass.constructor.call(this, config);
22567     this.addEvents({
22568         /**
22569          * @event expand
22570          * Fires when the dropdown list is expanded
22571              * @param {Roo.form.ComboBox} combo This combo box
22572              */
22573         'expand' : true,
22574         /**
22575          * @event collapse
22576          * Fires when the dropdown list is collapsed
22577              * @param {Roo.form.ComboBox} combo This combo box
22578              */
22579         'collapse' : true,
22580         /**
22581          * @event beforeselect
22582          * Fires before a list item is selected. Return false to cancel the selection.
22583              * @param {Roo.form.ComboBox} combo This combo box
22584              * @param {Roo.data.Record} record The data record returned from the underlying store
22585              * @param {Number} index The index of the selected item in the dropdown list
22586              */
22587         'beforeselect' : true,
22588         /**
22589          * @event select
22590          * Fires when a list item is selected
22591              * @param {Roo.form.ComboBox} combo This combo box
22592              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22593              * @param {Number} index The index of the selected item in the dropdown list
22594              */
22595         'select' : true,
22596         /**
22597          * @event beforequery
22598          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22599          * The event object passed has these properties:
22600              * @param {Roo.form.ComboBox} combo This combo box
22601              * @param {String} query The query
22602              * @param {Boolean} forceAll true to force "all" query
22603              * @param {Boolean} cancel true to cancel the query
22604              * @param {Object} e The query event object
22605              */
22606         'beforequery': true,
22607          /**
22608          * @event add
22609          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22610              * @param {Roo.form.ComboBox} combo This combo box
22611              */
22612         'add' : true,
22613         /**
22614          * @event edit
22615          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22616              * @param {Roo.form.ComboBox} combo This combo box
22617              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22618              */
22619         'edit' : true
22620         
22621         
22622     });
22623     if(this.transform){
22624         this.allowDomMove = false;
22625         var s = Roo.getDom(this.transform);
22626         if(!this.hiddenName){
22627             this.hiddenName = s.name;
22628         }
22629         if(!this.store){
22630             this.mode = 'local';
22631             var d = [], opts = s.options;
22632             for(var i = 0, len = opts.length;i < len; i++){
22633                 var o = opts[i];
22634                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22635                 if(o.selected) {
22636                     this.value = value;
22637                 }
22638                 d.push([value, o.text]);
22639             }
22640             this.store = new Roo.data.SimpleStore({
22641                 'id': 0,
22642                 fields: ['value', 'text'],
22643                 data : d
22644             });
22645             this.valueField = 'value';
22646             this.displayField = 'text';
22647         }
22648         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22649         if(!this.lazyRender){
22650             this.target = true;
22651             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22652             s.parentNode.removeChild(s); // remove it
22653             this.render(this.el.parentNode);
22654         }else{
22655             s.parentNode.removeChild(s); // remove it
22656         }
22657
22658     }
22659     if (this.store) {
22660         this.store = Roo.factory(this.store, Roo.data);
22661     }
22662     
22663     this.selectedIndex = -1;
22664     if(this.mode == 'local'){
22665         if(config.queryDelay === undefined){
22666             this.queryDelay = 10;
22667         }
22668         if(config.minChars === undefined){
22669             this.minChars = 0;
22670         }
22671     }
22672 };
22673
22674 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22675     /**
22676      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22677      */
22678     /**
22679      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22680      * rendering into an Roo.Editor, defaults to false)
22681      */
22682     /**
22683      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22684      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22685      */
22686     /**
22687      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22688      */
22689     /**
22690      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22691      * the dropdown list (defaults to undefined, with no header element)
22692      */
22693
22694      /**
22695      * @cfg {String/Roo.Template} tpl The template to use to render the output
22696      */
22697      
22698     // private
22699     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22700     /**
22701      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22702      */
22703     listWidth: undefined,
22704     /**
22705      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22706      * mode = 'remote' or 'text' if mode = 'local')
22707      */
22708     displayField: undefined,
22709     /**
22710      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22711      * mode = 'remote' or 'value' if mode = 'local'). 
22712      * Note: use of a valueField requires the user make a selection
22713      * in order for a value to be mapped.
22714      */
22715     valueField: undefined,
22716     /**
22717      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22718      * field's data value (defaults to the underlying DOM element's name)
22719      */
22720     hiddenName: undefined,
22721     /**
22722      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22723      */
22724     listClass: '',
22725     /**
22726      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22727      */
22728     selectedClass: 'x-combo-selected',
22729     /**
22730      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22731      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22732      * which displays a downward arrow icon).
22733      */
22734     triggerClass : 'x-form-arrow-trigger',
22735     /**
22736      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22737      */
22738     shadow:'sides',
22739     /**
22740      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22741      * anchor positions (defaults to 'tl-bl')
22742      */
22743     listAlign: 'tl-bl?',
22744     /**
22745      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22746      */
22747     maxHeight: 300,
22748     /**
22749      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22750      * query specified by the allQuery config option (defaults to 'query')
22751      */
22752     triggerAction: 'query',
22753     /**
22754      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22755      * (defaults to 4, does not apply if editable = false)
22756      */
22757     minChars : 4,
22758     /**
22759      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22760      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22761      */
22762     typeAhead: false,
22763     /**
22764      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22765      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22766      */
22767     queryDelay: 500,
22768     /**
22769      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22770      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22771      */
22772     pageSize: 0,
22773     /**
22774      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22775      * when editable = true (defaults to false)
22776      */
22777     selectOnFocus:false,
22778     /**
22779      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22780      */
22781     queryParam: 'query',
22782     /**
22783      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22784      * when mode = 'remote' (defaults to 'Loading...')
22785      */
22786     loadingText: 'Loading...',
22787     /**
22788      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22789      */
22790     resizable: false,
22791     /**
22792      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22793      */
22794     handleHeight : 8,
22795     /**
22796      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22797      * traditional select (defaults to true)
22798      */
22799     editable: true,
22800     /**
22801      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22802      */
22803     allQuery: '',
22804     /**
22805      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22806      */
22807     mode: 'remote',
22808     /**
22809      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22810      * listWidth has a higher value)
22811      */
22812     minListWidth : 70,
22813     /**
22814      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22815      * allow the user to set arbitrary text into the field (defaults to false)
22816      */
22817     forceSelection:false,
22818     /**
22819      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22820      * if typeAhead = true (defaults to 250)
22821      */
22822     typeAheadDelay : 250,
22823     /**
22824      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22825      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22826      */
22827     valueNotFoundText : undefined,
22828     /**
22829      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22830      */
22831     blockFocus : false,
22832     
22833     /**
22834      * @cfg {Boolean} disableClear Disable showing of clear button.
22835      */
22836     disableClear : false,
22837     /**
22838      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22839      */
22840     alwaysQuery : false,
22841     
22842     //private
22843     addicon : false,
22844     editicon: false,
22845     
22846     
22847     // private
22848     onRender : function(ct, position){
22849         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22850         if(this.hiddenName){
22851             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22852                     'before', true);
22853             this.hiddenField.value =
22854                 this.hiddenValue !== undefined ? this.hiddenValue :
22855                 this.value !== undefined ? this.value : '';
22856
22857             // prevent input submission
22858             this.el.dom.removeAttribute('name');
22859         }
22860         if(Roo.isGecko){
22861             this.el.dom.setAttribute('autocomplete', 'off');
22862         }
22863
22864         var cls = 'x-combo-list';
22865
22866         this.list = new Roo.Layer({
22867             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22868         });
22869
22870         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22871         this.list.setWidth(lw);
22872         this.list.swallowEvent('mousewheel');
22873         this.assetHeight = 0;
22874
22875         if(this.title){
22876             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22877             this.assetHeight += this.header.getHeight();
22878         }
22879
22880         this.innerList = this.list.createChild({cls:cls+'-inner'});
22881         this.innerList.on('mouseover', this.onViewOver, this);
22882         this.innerList.on('mousemove', this.onViewMove, this);
22883         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22884         
22885         if(this.allowBlank && !this.pageSize && !this.disableClear){
22886             this.footer = this.list.createChild({cls:cls+'-ft'});
22887             this.pageTb = new Roo.Toolbar(this.footer);
22888            
22889         }
22890         if(this.pageSize){
22891             this.footer = this.list.createChild({cls:cls+'-ft'});
22892             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22893                     {pageSize: this.pageSize});
22894             
22895         }
22896         
22897         if (this.pageTb && this.allowBlank && !this.disableClear) {
22898             var _this = this;
22899             this.pageTb.add(new Roo.Toolbar.Fill(), {
22900                 cls: 'x-btn-icon x-btn-clear',
22901                 text: '&#160;',
22902                 handler: function()
22903                 {
22904                     _this.collapse();
22905                     _this.clearValue();
22906                     _this.onSelect(false, -1);
22907                 }
22908             });
22909         }
22910         if (this.footer) {
22911             this.assetHeight += this.footer.getHeight();
22912         }
22913         
22914
22915         if(!this.tpl){
22916             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22917         }
22918
22919         this.view = new Roo.View(this.innerList, this.tpl, {
22920             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22921         });
22922
22923         this.view.on('click', this.onViewClick, this);
22924
22925         this.store.on('beforeload', this.onBeforeLoad, this);
22926         this.store.on('load', this.onLoad, this);
22927         this.store.on('loadexception', this.collapse, this);
22928
22929         if(this.resizable){
22930             this.resizer = new Roo.Resizable(this.list,  {
22931                pinned:true, handles:'se'
22932             });
22933             this.resizer.on('resize', function(r, w, h){
22934                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22935                 this.listWidth = w;
22936                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22937                 this.restrictHeight();
22938             }, this);
22939             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22940         }
22941         if(!this.editable){
22942             this.editable = true;
22943             this.setEditable(false);
22944         }  
22945         
22946         
22947         if (typeof(this.events.add.listeners) != 'undefined') {
22948             
22949             this.addicon = this.wrap.createChild(
22950                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22951        
22952             this.addicon.on('click', function(e) {
22953                 this.fireEvent('add', this);
22954             }, this);
22955         }
22956         if (typeof(this.events.edit.listeners) != 'undefined') {
22957             
22958             this.editicon = this.wrap.createChild(
22959                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22960             if (this.addicon) {
22961                 this.editicon.setStyle('margin-left', '40px');
22962             }
22963             this.editicon.on('click', function(e) {
22964                 
22965                 // we fire even  if inothing is selected..
22966                 this.fireEvent('edit', this, this.lastData );
22967                 
22968             }, this);
22969         }
22970         
22971         
22972         
22973     },
22974
22975     // private
22976     initEvents : function(){
22977         Roo.form.ComboBox.superclass.initEvents.call(this);
22978
22979         this.keyNav = new Roo.KeyNav(this.el, {
22980             "up" : function(e){
22981                 this.inKeyMode = true;
22982                 this.selectPrev();
22983             },
22984
22985             "down" : function(e){
22986                 if(!this.isExpanded()){
22987                     this.onTriggerClick();
22988                 }else{
22989                     this.inKeyMode = true;
22990                     this.selectNext();
22991                 }
22992             },
22993
22994             "enter" : function(e){
22995                 this.onViewClick();
22996                 //return true;
22997             },
22998
22999             "esc" : function(e){
23000                 this.collapse();
23001             },
23002
23003             "tab" : function(e){
23004                 this.onViewClick(false);
23005                 return true;
23006             },
23007
23008             scope : this,
23009
23010             doRelay : function(foo, bar, hname){
23011                 if(hname == 'down' || this.scope.isExpanded()){
23012                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23013                 }
23014                 return true;
23015             },
23016
23017             forceKeyDown: true
23018         });
23019         this.queryDelay = Math.max(this.queryDelay || 10,
23020                 this.mode == 'local' ? 10 : 250);
23021         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23022         if(this.typeAhead){
23023             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23024         }
23025         if(this.editable !== false){
23026             this.el.on("keyup", this.onKeyUp, this);
23027         }
23028         if(this.forceSelection){
23029             this.on('blur', this.doForce, this);
23030         }
23031     },
23032
23033     onDestroy : function(){
23034         if(this.view){
23035             this.view.setStore(null);
23036             this.view.el.removeAllListeners();
23037             this.view.el.remove();
23038             this.view.purgeListeners();
23039         }
23040         if(this.list){
23041             this.list.destroy();
23042         }
23043         if(this.store){
23044             this.store.un('beforeload', this.onBeforeLoad, this);
23045             this.store.un('load', this.onLoad, this);
23046             this.store.un('loadexception', this.collapse, this);
23047         }
23048         Roo.form.ComboBox.superclass.onDestroy.call(this);
23049     },
23050
23051     // private
23052     fireKey : function(e){
23053         if(e.isNavKeyPress() && !this.list.isVisible()){
23054             this.fireEvent("specialkey", this, e);
23055         }
23056     },
23057
23058     // private
23059     onResize: function(w, h){
23060         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23061         
23062         if(typeof w != 'number'){
23063             // we do not handle it!?!?
23064             return;
23065         }
23066         var tw = this.trigger.getWidth();
23067         tw += this.addicon ? this.addicon.getWidth() : 0;
23068         tw += this.editicon ? this.editicon.getWidth() : 0;
23069         var x = w - tw;
23070         this.el.setWidth( this.adjustWidth('input', x));
23071             
23072         this.trigger.setStyle('left', x+'px');
23073         
23074         if(this.list && this.listWidth === undefined){
23075             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23076             this.list.setWidth(lw);
23077             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23078         }
23079         
23080     
23081         
23082     },
23083
23084     /**
23085      * Allow or prevent the user from directly editing the field text.  If false is passed,
23086      * the user will only be able to select from the items defined in the dropdown list.  This method
23087      * is the runtime equivalent of setting the 'editable' config option at config time.
23088      * @param {Boolean} value True to allow the user to directly edit the field text
23089      */
23090     setEditable : function(value){
23091         if(value == this.editable){
23092             return;
23093         }
23094         this.editable = value;
23095         if(!value){
23096             this.el.dom.setAttribute('readOnly', true);
23097             this.el.on('mousedown', this.onTriggerClick,  this);
23098             this.el.addClass('x-combo-noedit');
23099         }else{
23100             this.el.dom.setAttribute('readOnly', false);
23101             this.el.un('mousedown', this.onTriggerClick,  this);
23102             this.el.removeClass('x-combo-noedit');
23103         }
23104     },
23105
23106     // private
23107     onBeforeLoad : function(){
23108         if(!this.hasFocus){
23109             return;
23110         }
23111         this.innerList.update(this.loadingText ?
23112                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23113         this.restrictHeight();
23114         this.selectedIndex = -1;
23115     },
23116
23117     // private
23118     onLoad : function(){
23119         if(!this.hasFocus){
23120             return;
23121         }
23122         if(this.store.getCount() > 0){
23123             this.expand();
23124             this.restrictHeight();
23125             if(this.lastQuery == this.allQuery){
23126                 if(this.editable){
23127                     this.el.dom.select();
23128                 }
23129                 if(!this.selectByValue(this.value, true)){
23130                     this.select(0, true);
23131                 }
23132             }else{
23133                 this.selectNext();
23134                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23135                     this.taTask.delay(this.typeAheadDelay);
23136                 }
23137             }
23138         }else{
23139             this.onEmptyResults();
23140         }
23141         //this.el.focus();
23142     },
23143
23144     // private
23145     onTypeAhead : function(){
23146         if(this.store.getCount() > 0){
23147             var r = this.store.getAt(0);
23148             var newValue = r.data[this.displayField];
23149             var len = newValue.length;
23150             var selStart = this.getRawValue().length;
23151             if(selStart != len){
23152                 this.setRawValue(newValue);
23153                 this.selectText(selStart, newValue.length);
23154             }
23155         }
23156     },
23157
23158     // private
23159     onSelect : function(record, index){
23160         if(this.fireEvent('beforeselect', this, record, index) !== false){
23161             this.setFromData(index > -1 ? record.data : false);
23162             this.collapse();
23163             this.fireEvent('select', this, record, index);
23164         }
23165     },
23166
23167     /**
23168      * Returns the currently selected field value or empty string if no value is set.
23169      * @return {String} value The selected value
23170      */
23171     getValue : function(){
23172         if(this.valueField){
23173             return typeof this.value != 'undefined' ? this.value : '';
23174         }else{
23175             return Roo.form.ComboBox.superclass.getValue.call(this);
23176         }
23177     },
23178
23179     /**
23180      * Clears any text/value currently set in the field
23181      */
23182     clearValue : function(){
23183         if(this.hiddenField){
23184             this.hiddenField.value = '';
23185         }
23186         this.value = '';
23187         this.setRawValue('');
23188         this.lastSelectionText = '';
23189         this.applyEmptyText();
23190     },
23191
23192     /**
23193      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23194      * will be displayed in the field.  If the value does not match the data value of an existing item,
23195      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23196      * Otherwise the field will be blank (although the value will still be set).
23197      * @param {String} value The value to match
23198      */
23199     setValue : function(v){
23200         var text = v;
23201         if(this.valueField){
23202             var r = this.findRecord(this.valueField, v);
23203             if(r){
23204                 text = r.data[this.displayField];
23205             }else if(this.valueNotFoundText !== undefined){
23206                 text = this.valueNotFoundText;
23207             }
23208         }
23209         this.lastSelectionText = text;
23210         if(this.hiddenField){
23211             this.hiddenField.value = v;
23212         }
23213         Roo.form.ComboBox.superclass.setValue.call(this, text);
23214         this.value = v;
23215     },
23216     /**
23217      * @property {Object} the last set data for the element
23218      */
23219     
23220     lastData : false,
23221     /**
23222      * Sets the value of the field based on a object which is related to the record format for the store.
23223      * @param {Object} value the value to set as. or false on reset?
23224      */
23225     setFromData : function(o){
23226         var dv = ''; // display value
23227         var vv = ''; // value value..
23228         this.lastData = o;
23229         if (this.displayField) {
23230             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23231         } else {
23232             // this is an error condition!!!
23233             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23234         }
23235         
23236         if(this.valueField){
23237             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23238         }
23239         if(this.hiddenField){
23240             this.hiddenField.value = vv;
23241             
23242             this.lastSelectionText = dv;
23243             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23244             this.value = vv;
23245             return;
23246         }
23247         // no hidden field.. - we store the value in 'value', but still display
23248         // display field!!!!
23249         this.lastSelectionText = dv;
23250         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23251         this.value = vv;
23252         
23253         
23254     },
23255     // private
23256     reset : function(){
23257         // overridden so that last data is reset..
23258         this.setValue(this.originalValue);
23259         this.clearInvalid();
23260         this.lastData = false;
23261     },
23262     // private
23263     findRecord : function(prop, value){
23264         var record;
23265         if(this.store.getCount() > 0){
23266             this.store.each(function(r){
23267                 if(r.data[prop] == value){
23268                     record = r;
23269                     return false;
23270                 }
23271             });
23272         }
23273         return record;
23274     },
23275
23276     // private
23277     onViewMove : function(e, t){
23278         this.inKeyMode = false;
23279     },
23280
23281     // private
23282     onViewOver : function(e, t){
23283         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23284             return;
23285         }
23286         var item = this.view.findItemFromChild(t);
23287         if(item){
23288             var index = this.view.indexOf(item);
23289             this.select(index, false);
23290         }
23291     },
23292
23293     // private
23294     onViewClick : function(doFocus){
23295         var index = this.view.getSelectedIndexes()[0];
23296         var r = this.store.getAt(index);
23297         if(r){
23298             this.onSelect(r, index);
23299         }
23300         if(doFocus !== false && !this.blockFocus){
23301             this.el.focus();
23302         }
23303     },
23304
23305     // private
23306     restrictHeight : function(){
23307         this.innerList.dom.style.height = '';
23308         var inner = this.innerList.dom;
23309         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23310         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23311         this.list.beginUpdate();
23312         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23313         this.list.alignTo(this.el, this.listAlign);
23314         this.list.endUpdate();
23315     },
23316
23317     // private
23318     onEmptyResults : function(){
23319         this.collapse();
23320     },
23321
23322     /**
23323      * Returns true if the dropdown list is expanded, else false.
23324      */
23325     isExpanded : function(){
23326         return this.list.isVisible();
23327     },
23328
23329     /**
23330      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23331      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23332      * @param {String} value The data value of the item to select
23333      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23334      * selected item if it is not currently in view (defaults to true)
23335      * @return {Boolean} True if the value matched an item in the list, else false
23336      */
23337     selectByValue : function(v, scrollIntoView){
23338         if(v !== undefined && v !== null){
23339             var r = this.findRecord(this.valueField || this.displayField, v);
23340             if(r){
23341                 this.select(this.store.indexOf(r), scrollIntoView);
23342                 return true;
23343             }
23344         }
23345         return false;
23346     },
23347
23348     /**
23349      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23350      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23351      * @param {Number} index The zero-based index of the list item to select
23352      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23353      * selected item if it is not currently in view (defaults to true)
23354      */
23355     select : function(index, scrollIntoView){
23356         this.selectedIndex = index;
23357         this.view.select(index);
23358         if(scrollIntoView !== false){
23359             var el = this.view.getNode(index);
23360             if(el){
23361                 this.innerList.scrollChildIntoView(el, false);
23362             }
23363         }
23364     },
23365
23366     // private
23367     selectNext : function(){
23368         var ct = this.store.getCount();
23369         if(ct > 0){
23370             if(this.selectedIndex == -1){
23371                 this.select(0);
23372             }else if(this.selectedIndex < ct-1){
23373                 this.select(this.selectedIndex+1);
23374             }
23375         }
23376     },
23377
23378     // private
23379     selectPrev : function(){
23380         var ct = this.store.getCount();
23381         if(ct > 0){
23382             if(this.selectedIndex == -1){
23383                 this.select(0);
23384             }else if(this.selectedIndex != 0){
23385                 this.select(this.selectedIndex-1);
23386             }
23387         }
23388     },
23389
23390     // private
23391     onKeyUp : function(e){
23392         if(this.editable !== false && !e.isSpecialKey()){
23393             this.lastKey = e.getKey();
23394             this.dqTask.delay(this.queryDelay);
23395         }
23396     },
23397
23398     // private
23399     validateBlur : function(){
23400         return !this.list || !this.list.isVisible();   
23401     },
23402
23403     // private
23404     initQuery : function(){
23405         this.doQuery(this.getRawValue());
23406     },
23407
23408     // private
23409     doForce : function(){
23410         if(this.el.dom.value.length > 0){
23411             this.el.dom.value =
23412                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23413             this.applyEmptyText();
23414         }
23415     },
23416
23417     /**
23418      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23419      * query allowing the query action to be canceled if needed.
23420      * @param {String} query The SQL query to execute
23421      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23422      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23423      * saved in the current store (defaults to false)
23424      */
23425     doQuery : function(q, forceAll){
23426         if(q === undefined || q === null){
23427             q = '';
23428         }
23429         var qe = {
23430             query: q,
23431             forceAll: forceAll,
23432             combo: this,
23433             cancel:false
23434         };
23435         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23436             return false;
23437         }
23438         q = qe.query;
23439         forceAll = qe.forceAll;
23440         if(forceAll === true || (q.length >= this.minChars)){
23441             if(this.lastQuery != q || this.alwaysQuery){
23442                 this.lastQuery = q;
23443                 if(this.mode == 'local'){
23444                     this.selectedIndex = -1;
23445                     if(forceAll){
23446                         this.store.clearFilter();
23447                     }else{
23448                         this.store.filter(this.displayField, q);
23449                     }
23450                     this.onLoad();
23451                 }else{
23452                     this.store.baseParams[this.queryParam] = q;
23453                     this.store.load({
23454                         params: this.getParams(q)
23455                     });
23456                     this.expand();
23457                 }
23458             }else{
23459                 this.selectedIndex = -1;
23460                 this.onLoad();   
23461             }
23462         }
23463     },
23464
23465     // private
23466     getParams : function(q){
23467         var p = {};
23468         //p[this.queryParam] = q;
23469         if(this.pageSize){
23470             p.start = 0;
23471             p.limit = this.pageSize;
23472         }
23473         return p;
23474     },
23475
23476     /**
23477      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23478      */
23479     collapse : function(){
23480         if(!this.isExpanded()){
23481             return;
23482         }
23483         this.list.hide();
23484         Roo.get(document).un('mousedown', this.collapseIf, this);
23485         Roo.get(document).un('mousewheel', this.collapseIf, this);
23486         if (!this.editable) {
23487             Roo.get(document).un('keydown', this.listKeyPress, this);
23488         }
23489         this.fireEvent('collapse', this);
23490     },
23491
23492     // private
23493     collapseIf : function(e){
23494         if(!e.within(this.wrap) && !e.within(this.list)){
23495             this.collapse();
23496         }
23497     },
23498
23499     /**
23500      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23501      */
23502     expand : function(){
23503         if(this.isExpanded() || !this.hasFocus){
23504             return;
23505         }
23506         this.list.alignTo(this.el, this.listAlign);
23507         this.list.show();
23508         Roo.get(document).on('mousedown', this.collapseIf, this);
23509         Roo.get(document).on('mousewheel', this.collapseIf, this);
23510         if (!this.editable) {
23511             Roo.get(document).on('keydown', this.listKeyPress, this);
23512         }
23513         
23514         this.fireEvent('expand', this);
23515     },
23516
23517     // private
23518     // Implements the default empty TriggerField.onTriggerClick function
23519     onTriggerClick : function(){
23520         if(this.disabled){
23521             return;
23522         }
23523         if(this.isExpanded()){
23524             this.collapse();
23525             if (!this.blockFocus) {
23526                 this.el.focus();
23527             }
23528             
23529         }else {
23530             this.hasFocus = true;
23531             if(this.triggerAction == 'all') {
23532                 this.doQuery(this.allQuery, true);
23533             } else {
23534                 this.doQuery(this.getRawValue());
23535             }
23536             if (!this.blockFocus) {
23537                 this.el.focus();
23538             }
23539         }
23540     },
23541     listKeyPress : function(e)
23542     {
23543         //Roo.log('listkeypress');
23544         // scroll to first matching element based on key pres..
23545         if (e.isSpecialKey()) {
23546             return false;
23547         }
23548         var k = String.fromCharCode(e.getKey()).toUpperCase();
23549         //Roo.log(k);
23550         var match  = false;
23551         var csel = this.view.getSelectedNodes();
23552         var cselitem = false;
23553         if (csel.length) {
23554             var ix = this.view.indexOf(csel[0]);
23555             cselitem  = this.store.getAt(ix);
23556             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23557                 cselitem = false;
23558             }
23559             
23560         }
23561         
23562         this.store.each(function(v) { 
23563             if (cselitem) {
23564                 // start at existing selection.
23565                 if (cselitem.id == v.id) {
23566                     cselitem = false;
23567                 }
23568                 return;
23569             }
23570                 
23571             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23572                 match = this.store.indexOf(v);
23573                 return false;
23574             }
23575         }, this);
23576         
23577         if (match === false) {
23578             return true; // no more action?
23579         }
23580         // scroll to?
23581         this.view.select(match);
23582         var sn = Roo.get(this.view.getSelectedNodes()[0])
23583         sn.scrollIntoView(sn.dom.parentNode, false);
23584     }
23585
23586     /** 
23587     * @cfg {Boolean} grow 
23588     * @hide 
23589     */
23590     /** 
23591     * @cfg {Number} growMin 
23592     * @hide 
23593     */
23594     /** 
23595     * @cfg {Number} growMax 
23596     * @hide 
23597     */
23598     /**
23599      * @hide
23600      * @method autoSize
23601      */
23602 });/*
23603  * Based on:
23604  * Ext JS Library 1.1.1
23605  * Copyright(c) 2006-2007, Ext JS, LLC.
23606  *
23607  * Originally Released Under LGPL - original licence link has changed is not relivant.
23608  *
23609  * Fork - LGPL
23610  * <script type="text/javascript">
23611  */
23612 /**
23613  * @class Roo.form.Checkbox
23614  * @extends Roo.form.Field
23615  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23616  * @constructor
23617  * Creates a new Checkbox
23618  * @param {Object} config Configuration options
23619  */
23620 Roo.form.Checkbox = function(config){
23621     Roo.form.Checkbox.superclass.constructor.call(this, config);
23622     this.addEvents({
23623         /**
23624          * @event check
23625          * Fires when the checkbox is checked or unchecked.
23626              * @param {Roo.form.Checkbox} this This checkbox
23627              * @param {Boolean} checked The new checked value
23628              */
23629         check : true
23630     });
23631 };
23632
23633 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23634     /**
23635      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23636      */
23637     focusClass : undefined,
23638     /**
23639      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23640      */
23641     fieldClass: "x-form-field",
23642     /**
23643      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23644      */
23645     checked: false,
23646     /**
23647      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23648      * {tag: "input", type: "checkbox", autocomplete: "off"})
23649      */
23650     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23651     /**
23652      * @cfg {String} boxLabel The text that appears beside the checkbox
23653      */
23654     boxLabel : "",
23655     /**
23656      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23657      */  
23658     inputValue : '1',
23659     /**
23660      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23661      */
23662      valueOff: '0', // value when not checked..
23663
23664     actionMode : 'viewEl', 
23665     //
23666     // private
23667     itemCls : 'x-menu-check-item x-form-item',
23668     groupClass : 'x-menu-group-item',
23669     inputType : 'hidden',
23670     
23671     
23672     inSetChecked: false, // check that we are not calling self...
23673     
23674     inputElement: false, // real input element?
23675     basedOn: false, // ????
23676     
23677     isFormField: true, // not sure where this is needed!!!!
23678
23679     onResize : function(){
23680         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23681         if(!this.boxLabel){
23682             this.el.alignTo(this.wrap, 'c-c');
23683         }
23684     },
23685
23686     initEvents : function(){
23687         Roo.form.Checkbox.superclass.initEvents.call(this);
23688         this.el.on("click", this.onClick,  this);
23689         this.el.on("change", this.onClick,  this);
23690     },
23691
23692
23693     getResizeEl : function(){
23694         return this.wrap;
23695     },
23696
23697     getPositionEl : function(){
23698         return this.wrap;
23699     },
23700
23701     // private
23702     onRender : function(ct, position){
23703         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23704         /*
23705         if(this.inputValue !== undefined){
23706             this.el.dom.value = this.inputValue;
23707         }
23708         */
23709         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23710         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23711         var viewEl = this.wrap.createChild({ 
23712             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23713         this.viewEl = viewEl;   
23714         this.wrap.on('click', this.onClick,  this); 
23715         
23716         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23717         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23718         
23719         
23720         
23721         if(this.boxLabel){
23722             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23723         //    viewEl.on('click', this.onClick,  this); 
23724         }
23725         //if(this.checked){
23726             this.setChecked(this.checked);
23727         //}else{
23728             //this.checked = this.el.dom;
23729         //}
23730
23731     },
23732
23733     // private
23734     initValue : Roo.emptyFn,
23735
23736     /**
23737      * Returns the checked state of the checkbox.
23738      * @return {Boolean} True if checked, else false
23739      */
23740     getValue : function(){
23741         if(this.el){
23742             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23743         }
23744         return this.valueOff;
23745         
23746     },
23747
23748         // private
23749     onClick : function(){ 
23750         this.setChecked(!this.checked);
23751
23752         //if(this.el.dom.checked != this.checked){
23753         //    this.setValue(this.el.dom.checked);
23754        // }
23755     },
23756
23757     /**
23758      * Sets the checked state of the checkbox.
23759      * On is always based on a string comparison between inputValue and the param.
23760      * @param {Boolean/String} value - the value to set 
23761      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23762      */
23763     setValue : function(v,suppressEvent){
23764         
23765         
23766         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23767         //if(this.el && this.el.dom){
23768         //    this.el.dom.checked = this.checked;
23769         //    this.el.dom.defaultChecked = this.checked;
23770         //}
23771         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23772         //this.fireEvent("check", this, this.checked);
23773     },
23774     // private..
23775     setChecked : function(state,suppressEvent)
23776     {
23777         if (this.inSetChecked) {
23778             this.checked = state;
23779             return;
23780         }
23781         
23782     
23783         if(this.wrap){
23784             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23785         }
23786         this.checked = state;
23787         if(suppressEvent !== true){
23788             this.fireEvent('check', this, state);
23789         }
23790         this.inSetChecked = true;
23791         this.el.dom.value = state ? this.inputValue : this.valueOff;
23792         this.inSetChecked = false;
23793         
23794     },
23795     // handle setting of hidden value by some other method!!?!?
23796     setFromHidden: function()
23797     {
23798         if(!this.el){
23799             return;
23800         }
23801         //console.log("SET FROM HIDDEN");
23802         //alert('setFrom hidden');
23803         this.setValue(this.el.dom.value);
23804     },
23805     
23806     onDestroy : function()
23807     {
23808         if(this.viewEl){
23809             Roo.get(this.viewEl).remove();
23810         }
23811          
23812         Roo.form.Checkbox.superclass.onDestroy.call(this);
23813     }
23814
23815 });/*
23816  * Based on:
23817  * Ext JS Library 1.1.1
23818  * Copyright(c) 2006-2007, Ext JS, LLC.
23819  *
23820  * Originally Released Under LGPL - original licence link has changed is not relivant.
23821  *
23822  * Fork - LGPL
23823  * <script type="text/javascript">
23824  */
23825  
23826 /**
23827  * @class Roo.form.Radio
23828  * @extends Roo.form.Checkbox
23829  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23830  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23831  * @constructor
23832  * Creates a new Radio
23833  * @param {Object} config Configuration options
23834  */
23835 Roo.form.Radio = function(){
23836     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23837 };
23838 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23839     inputType: 'radio',
23840
23841     /**
23842      * If this radio is part of a group, it will return the selected value
23843      * @return {String}
23844      */
23845     getGroupValue : function(){
23846         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23847     }
23848 });//<script type="text/javascript">
23849
23850 /*
23851  * Ext JS Library 1.1.1
23852  * Copyright(c) 2006-2007, Ext JS, LLC.
23853  * licensing@extjs.com
23854  * 
23855  * http://www.extjs.com/license
23856  */
23857  
23858  /*
23859   * 
23860   * Known bugs:
23861   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23862   * - IE ? - no idea how much works there.
23863   * 
23864   * 
23865   * 
23866   */
23867  
23868
23869 /**
23870  * @class Ext.form.HtmlEditor
23871  * @extends Ext.form.Field
23872  * Provides a lightweight HTML Editor component.
23873  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23874  * 
23875  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23876  * supported by this editor.</b><br/><br/>
23877  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23878  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23879  */
23880 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23881       /**
23882      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23883      */
23884     toolbars : false,
23885     /**
23886      * @cfg {String} createLinkText The default text for the create link prompt
23887      */
23888     createLinkText : 'Please enter the URL for the link:',
23889     /**
23890      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23891      */
23892     defaultLinkValue : 'http:/'+'/',
23893    
23894     
23895     // id of frame..
23896     frameId: false,
23897     
23898     // private properties
23899     validationEvent : false,
23900     deferHeight: true,
23901     initialized : false,
23902     activated : false,
23903     sourceEditMode : false,
23904     onFocus : Roo.emptyFn,
23905     iframePad:3,
23906     hideMode:'offsets',
23907     defaultAutoCreate : {
23908         tag: "textarea",
23909         style:"width:500px;height:300px;",
23910         autocomplete: "off"
23911     },
23912
23913     // private
23914     initComponent : function(){
23915         this.addEvents({
23916             /**
23917              * @event initialize
23918              * Fires when the editor is fully initialized (including the iframe)
23919              * @param {HtmlEditor} this
23920              */
23921             initialize: true,
23922             /**
23923              * @event activate
23924              * Fires when the editor is first receives the focus. Any insertion must wait
23925              * until after this event.
23926              * @param {HtmlEditor} this
23927              */
23928             activate: true,
23929              /**
23930              * @event beforesync
23931              * Fires before the textarea is updated with content from the editor iframe. Return false
23932              * to cancel the sync.
23933              * @param {HtmlEditor} this
23934              * @param {String} html
23935              */
23936             beforesync: true,
23937              /**
23938              * @event beforepush
23939              * Fires before the iframe editor is updated with content from the textarea. Return false
23940              * to cancel the push.
23941              * @param {HtmlEditor} this
23942              * @param {String} html
23943              */
23944             beforepush: true,
23945              /**
23946              * @event sync
23947              * Fires when the textarea is updated with content from the editor iframe.
23948              * @param {HtmlEditor} this
23949              * @param {String} html
23950              */
23951             sync: true,
23952              /**
23953              * @event push
23954              * Fires when the iframe editor is updated with content from the textarea.
23955              * @param {HtmlEditor} this
23956              * @param {String} html
23957              */
23958             push: true,
23959              /**
23960              * @event editmodechange
23961              * Fires when the editor switches edit modes
23962              * @param {HtmlEditor} this
23963              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23964              */
23965             editmodechange: true,
23966             /**
23967              * @event editorevent
23968              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23969              * @param {HtmlEditor} this
23970              */
23971             editorevent: true
23972         })
23973     },
23974
23975     /**
23976      * Protected method that will not generally be called directly. It
23977      * is called when the editor creates its toolbar. Override this method if you need to
23978      * add custom toolbar buttons.
23979      * @param {HtmlEditor} editor
23980      */
23981     createToolbar : function(editor){
23982         if (!editor.toolbars || !editor.toolbars.length) {
23983             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23984         }
23985         
23986         for (var i =0 ; i < editor.toolbars.length;i++) {
23987             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23988             editor.toolbars[i].init(editor);
23989         }
23990          
23991         
23992     },
23993
23994     /**
23995      * Protected method that will not generally be called directly. It
23996      * is called when the editor initializes the iframe with HTML contents. Override this method if you
23997      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
23998      */
23999     getDocMarkup : function(){
24000         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24001     },
24002
24003     // private
24004     onRender : function(ct, position){
24005         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24006         this.el.dom.style.border = '0 none';
24007         this.el.dom.setAttribute('tabIndex', -1);
24008         this.el.addClass('x-hidden');
24009         if(Roo.isIE){ // fix IE 1px bogus margin
24010             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24011         }
24012         this.wrap = this.el.wrap({
24013             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24014         });
24015
24016         this.frameId = Roo.id();
24017         this.createToolbar(this);
24018         
24019         
24020         
24021         
24022       
24023         
24024         var iframe = this.wrap.createChild({
24025             tag: 'iframe',
24026             id: this.frameId,
24027             name: this.frameId,
24028             frameBorder : 'no',
24029             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24030         });
24031         
24032        // console.log(iframe);
24033         //this.wrap.dom.appendChild(iframe);
24034
24035         this.iframe = iframe.dom;
24036
24037          this.assignDocWin();
24038         
24039         this.doc.designMode = 'on';
24040        
24041         this.doc.open();
24042         this.doc.write(this.getDocMarkup());
24043         this.doc.close();
24044
24045         
24046         var task = { // must defer to wait for browser to be ready
24047             run : function(){
24048                 //console.log("run task?" + this.doc.readyState);
24049                 this.assignDocWin();
24050                 if(this.doc.body || this.doc.readyState == 'complete'){
24051                     try {
24052                         this.doc.designMode="on";
24053                     } catch (e) {
24054                         return;
24055                     }
24056                     Roo.TaskMgr.stop(task);
24057                     this.initEditor.defer(10, this);
24058                 }
24059             },
24060             interval : 10,
24061             duration:10000,
24062             scope: this
24063         };
24064         Roo.TaskMgr.start(task);
24065
24066         if(!this.width){
24067             this.setSize(this.el.getSize());
24068         }
24069     },
24070
24071     // private
24072     onResize : function(w, h){
24073         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24074         if(this.el && this.iframe){
24075             if(typeof w == 'number'){
24076                 var aw = w - this.wrap.getFrameWidth('lr');
24077                 this.el.setWidth(this.adjustWidth('textarea', aw));
24078                 this.iframe.style.width = aw + 'px';
24079             }
24080             if(typeof h == 'number'){
24081                 var tbh = 0;
24082                 for (var i =0; i < this.toolbars.length;i++) {
24083                     // fixme - ask toolbars for heights?
24084                     tbh += this.toolbars[i].tb.el.getHeight();
24085                 }
24086                 
24087                 
24088                 
24089                 
24090                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24091                 this.el.setHeight(this.adjustWidth('textarea', ah));
24092                 this.iframe.style.height = ah + 'px';
24093                 if(this.doc){
24094                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24095                 }
24096             }
24097         }
24098     },
24099
24100     /**
24101      * Toggles the editor between standard and source edit mode.
24102      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24103      */
24104     toggleSourceEdit : function(sourceEditMode){
24105         
24106         this.sourceEditMode = sourceEditMode === true;
24107         
24108         if(this.sourceEditMode){
24109           
24110             this.syncValue();
24111             this.iframe.className = 'x-hidden';
24112             this.el.removeClass('x-hidden');
24113             this.el.dom.removeAttribute('tabIndex');
24114             this.el.focus();
24115         }else{
24116              
24117             this.pushValue();
24118             this.iframe.className = '';
24119             this.el.addClass('x-hidden');
24120             this.el.dom.setAttribute('tabIndex', -1);
24121             this.deferFocus();
24122         }
24123         this.setSize(this.wrap.getSize());
24124         this.fireEvent('editmodechange', this, this.sourceEditMode);
24125     },
24126
24127     // private used internally
24128     createLink : function(){
24129         var url = prompt(this.createLinkText, this.defaultLinkValue);
24130         if(url && url != 'http:/'+'/'){
24131             this.relayCmd('createlink', url);
24132         }
24133     },
24134
24135     // private (for BoxComponent)
24136     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24137
24138     // private (for BoxComponent)
24139     getResizeEl : function(){
24140         return this.wrap;
24141     },
24142
24143     // private (for BoxComponent)
24144     getPositionEl : function(){
24145         return this.wrap;
24146     },
24147
24148     // private
24149     initEvents : function(){
24150         this.originalValue = this.getValue();
24151     },
24152
24153     /**
24154      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24155      * @method
24156      */
24157     markInvalid : Roo.emptyFn,
24158     /**
24159      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24160      * @method
24161      */
24162     clearInvalid : Roo.emptyFn,
24163
24164     setValue : function(v){
24165         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24166         this.pushValue();
24167     },
24168
24169     /**
24170      * Protected method that will not generally be called directly. If you need/want
24171      * custom HTML cleanup, this is the method you should override.
24172      * @param {String} html The HTML to be cleaned
24173      * return {String} The cleaned HTML
24174      */
24175     cleanHtml : function(html){
24176         html = String(html);
24177         if(html.length > 5){
24178             if(Roo.isSafari){ // strip safari nonsense
24179                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24180             }
24181         }
24182         if(html == '&nbsp;'){
24183             html = '';
24184         }
24185         return html;
24186     },
24187
24188     /**
24189      * Protected method that will not generally be called directly. Syncs the contents
24190      * of the editor iframe with the textarea.
24191      */
24192     syncValue : function(){
24193         if(this.initialized){
24194             var bd = (this.doc.body || this.doc.documentElement);
24195             var html = bd.innerHTML;
24196             if(Roo.isSafari){
24197                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24198                 var m = bs.match(/text-align:(.*?);/i);
24199                 if(m && m[1]){
24200                     html = '<div style="'+m[0]+'">' + html + '</div>';
24201                 }
24202             }
24203             html = this.cleanHtml(html);
24204             if(this.fireEvent('beforesync', this, html) !== false){
24205                 this.el.dom.value = html;
24206                 this.fireEvent('sync', this, html);
24207             }
24208         }
24209     },
24210
24211     /**
24212      * Protected method that will not generally be called directly. Pushes the value of the textarea
24213      * into the iframe editor.
24214      */
24215     pushValue : function(){
24216         if(this.initialized){
24217             var v = this.el.dom.value;
24218             if(v.length < 1){
24219                 v = '&#160;';
24220             }
24221             if(this.fireEvent('beforepush', this, v) !== false){
24222                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24223                 this.fireEvent('push', this, v);
24224             }
24225         }
24226     },
24227
24228     // private
24229     deferFocus : function(){
24230         this.focus.defer(10, this);
24231     },
24232
24233     // doc'ed in Field
24234     focus : function(){
24235         if(this.win && !this.sourceEditMode){
24236             this.win.focus();
24237         }else{
24238             this.el.focus();
24239         }
24240     },
24241     
24242     assignDocWin: function()
24243     {
24244         var iframe = this.iframe;
24245         
24246          if(Roo.isIE){
24247             this.doc = iframe.contentWindow.document;
24248             this.win = iframe.contentWindow;
24249         } else {
24250             if (!Roo.get(this.frameId)) {
24251                 return;
24252             }
24253             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24254             this.win = Roo.get(this.frameId).dom.contentWindow;
24255         }
24256     },
24257     
24258     // private
24259     initEditor : function(){
24260         //console.log("INIT EDITOR");
24261         this.assignDocWin();
24262         
24263         
24264         
24265         this.doc.designMode="on";
24266         this.doc.open();
24267         this.doc.write(this.getDocMarkup());
24268         this.doc.close();
24269         
24270         var dbody = (this.doc.body || this.doc.documentElement);
24271         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24272         // this copies styles from the containing element into thsi one..
24273         // not sure why we need all of this..
24274         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24275         ss['background-attachment'] = 'fixed'; // w3c
24276         dbody.bgProperties = 'fixed'; // ie
24277         Roo.DomHelper.applyStyles(dbody, ss);
24278         Roo.EventManager.on(this.doc, {
24279             'mousedown': this.onEditorEvent,
24280             'dblclick': this.onEditorEvent,
24281             'click': this.onEditorEvent,
24282             'keyup': this.onEditorEvent,
24283             buffer:100,
24284             scope: this
24285         });
24286         if(Roo.isGecko){
24287             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24288         }
24289         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24290             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24291         }
24292         this.initialized = true;
24293
24294         this.fireEvent('initialize', this);
24295         this.pushValue();
24296     },
24297
24298     // private
24299     onDestroy : function(){
24300         
24301         
24302         
24303         if(this.rendered){
24304             
24305             for (var i =0; i < this.toolbars.length;i++) {
24306                 // fixme - ask toolbars for heights?
24307                 this.toolbars[i].onDestroy();
24308             }
24309             
24310             this.wrap.dom.innerHTML = '';
24311             this.wrap.remove();
24312         }
24313     },
24314
24315     // private
24316     onFirstFocus : function(){
24317         
24318         this.assignDocWin();
24319         
24320         
24321         this.activated = true;
24322         for (var i =0; i < this.toolbars.length;i++) {
24323             this.toolbars[i].onFirstFocus();
24324         }
24325        
24326         if(Roo.isGecko){ // prevent silly gecko errors
24327             this.win.focus();
24328             var s = this.win.getSelection();
24329             if(!s.focusNode || s.focusNode.nodeType != 3){
24330                 var r = s.getRangeAt(0);
24331                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24332                 r.collapse(true);
24333                 this.deferFocus();
24334             }
24335             try{
24336                 this.execCmd('useCSS', true);
24337                 this.execCmd('styleWithCSS', false);
24338             }catch(e){}
24339         }
24340         this.fireEvent('activate', this);
24341     },
24342
24343     // private
24344     adjustFont: function(btn){
24345         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24346         //if(Roo.isSafari){ // safari
24347         //    adjust *= 2;
24348        // }
24349         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24350         if(Roo.isSafari){ // safari
24351             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24352             v =  (v < 10) ? 10 : v;
24353             v =  (v > 48) ? 48 : v;
24354             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24355             
24356         }
24357         
24358         
24359         v = Math.max(1, v+adjust);
24360         
24361         this.execCmd('FontSize', v  );
24362     },
24363
24364     onEditorEvent : function(e){
24365         this.fireEvent('editorevent', this, e);
24366       //  this.updateToolbar();
24367         this.syncValue();
24368     },
24369
24370     insertTag : function(tg)
24371     {
24372         // could be a bit smarter... -> wrap the current selected tRoo..
24373         
24374         this.execCmd("formatblock",   tg);
24375         
24376     },
24377     
24378     insertText : function(txt)
24379     {
24380         
24381         
24382         range = this.createRange();
24383         range.deleteContents();
24384                //alert(Sender.getAttribute('label'));
24385                
24386         range.insertNode(this.doc.createTextNode(txt));
24387     } ,
24388     
24389     // private
24390     relayBtnCmd : function(btn){
24391         this.relayCmd(btn.cmd);
24392     },
24393
24394     /**
24395      * Executes a Midas editor command on the editor document and performs necessary focus and
24396      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24397      * @param {String} cmd The Midas command
24398      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24399      */
24400     relayCmd : function(cmd, value){
24401         this.win.focus();
24402         this.execCmd(cmd, value);
24403         this.fireEvent('editorevent', this);
24404         //this.updateToolbar();
24405         this.deferFocus();
24406     },
24407
24408     /**
24409      * Executes a Midas editor command directly on the editor document.
24410      * For visual commands, you should use {@link #relayCmd} instead.
24411      * <b>This should only be called after the editor is initialized.</b>
24412      * @param {String} cmd The Midas command
24413      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24414      */
24415     execCmd : function(cmd, value){
24416         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24417         this.syncValue();
24418     },
24419
24420    
24421     /**
24422      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24423      * to insert tRoo.
24424      * @param {String} text
24425      */
24426     insertAtCursor : function(text){
24427         if(!this.activated){
24428             return;
24429         }
24430         if(Roo.isIE){
24431             this.win.focus();
24432             var r = this.doc.selection.createRange();
24433             if(r){
24434                 r.collapse(true);
24435                 r.pasteHTML(text);
24436                 this.syncValue();
24437                 this.deferFocus();
24438             }
24439         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24440             this.win.focus();
24441             this.execCmd('InsertHTML', text);
24442             this.deferFocus();
24443         }
24444     },
24445  // private
24446     mozKeyPress : function(e){
24447         if(e.ctrlKey){
24448             var c = e.getCharCode(), cmd;
24449           
24450             if(c > 0){
24451                 c = String.fromCharCode(c).toLowerCase();
24452                 switch(c){
24453                     case 'b':
24454                         cmd = 'bold';
24455                     break;
24456                     case 'i':
24457                         cmd = 'italic';
24458                     break;
24459                     case 'u':
24460                         cmd = 'underline';
24461                     case 'v':
24462                         this.cleanUpPaste.defer(100, this);
24463                         return;
24464                     break;
24465                 }
24466                 if(cmd){
24467                     this.win.focus();
24468                     this.execCmd(cmd);
24469                     this.deferFocus();
24470                     e.preventDefault();
24471                 }
24472                 
24473             }
24474         }
24475     },
24476
24477     // private
24478     fixKeys : function(){ // load time branching for fastest keydown performance
24479         if(Roo.isIE){
24480             return function(e){
24481                 var k = e.getKey(), r;
24482                 if(k == e.TAB){
24483                     e.stopEvent();
24484                     r = this.doc.selection.createRange();
24485                     if(r){
24486                         r.collapse(true);
24487                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24488                         this.deferFocus();
24489                     }
24490                     return;
24491                 }
24492                 
24493                 if(k == e.ENTER){
24494                     r = this.doc.selection.createRange();
24495                     if(r){
24496                         var target = r.parentElement();
24497                         if(!target || target.tagName.toLowerCase() != 'li'){
24498                             e.stopEvent();
24499                             r.pasteHTML('<br />');
24500                             r.collapse(false);
24501                             r.select();
24502                         }
24503                     }
24504                 }
24505                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24506                     this.cleanUpPaste.defer(100, this);
24507                     return;
24508                 }
24509                 
24510                 
24511             };
24512         }else if(Roo.isOpera){
24513             return function(e){
24514                 var k = e.getKey();
24515                 if(k == e.TAB){
24516                     e.stopEvent();
24517                     this.win.focus();
24518                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24519                     this.deferFocus();
24520                 }
24521                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24522                     this.cleanUpPaste.defer(100, this);
24523                     return;
24524                 }
24525                 
24526             };
24527         }else if(Roo.isSafari){
24528             return function(e){
24529                 var k = e.getKey();
24530                 
24531                 if(k == e.TAB){
24532                     e.stopEvent();
24533                     this.execCmd('InsertText','\t');
24534                     this.deferFocus();
24535                     return;
24536                 }
24537                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24538                     this.cleanUpPaste.defer(100, this);
24539                     return;
24540                 }
24541                 
24542              };
24543         }
24544     }(),
24545     
24546     getAllAncestors: function()
24547     {
24548         var p = this.getSelectedNode();
24549         var a = [];
24550         if (!p) {
24551             a.push(p); // push blank onto stack..
24552             p = this.getParentElement();
24553         }
24554         
24555         
24556         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24557             a.push(p);
24558             p = p.parentNode;
24559         }
24560         a.push(this.doc.body);
24561         return a;
24562     },
24563     lastSel : false,
24564     lastSelNode : false,
24565     
24566     
24567     getSelection : function() 
24568     {
24569         this.assignDocWin();
24570         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24571     },
24572     
24573     getSelectedNode: function() 
24574     {
24575         // this may only work on Gecko!!!
24576         
24577         // should we cache this!!!!
24578         
24579         
24580         
24581          
24582         var range = this.createRange(this.getSelection());
24583         
24584         if (Roo.isIE) {
24585             var parent = range.parentElement();
24586             while (true) {
24587                 var testRange = range.duplicate();
24588                 testRange.moveToElementText(parent);
24589                 if (testRange.inRange(range)) {
24590                     break;
24591                 }
24592                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24593                     break;
24594                 }
24595                 parent = parent.parentElement;
24596             }
24597             return parent;
24598         }
24599         
24600         
24601         var ar = range.endContainer.childNodes;
24602         if (!ar.length) {
24603             ar = range.commonAncestorContainer.childNodes;
24604             //alert(ar.length);
24605         }
24606         var nodes = [];
24607         var other_nodes = [];
24608         var has_other_nodes = false;
24609         for (var i=0;i<ar.length;i++) {
24610             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24611                 continue;
24612             }
24613             // fullly contained node.
24614             
24615             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24616                 nodes.push(ar[i]);
24617                 continue;
24618             }
24619             
24620             // probably selected..
24621             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24622                 other_nodes.push(ar[i]);
24623                 continue;
24624             }
24625             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24626                 continue;
24627             }
24628             
24629             
24630             has_other_nodes = true;
24631         }
24632         if (!nodes.length && other_nodes.length) {
24633             nodes= other_nodes;
24634         }
24635         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24636             return false;
24637         }
24638         
24639         return nodes[0];
24640     },
24641     createRange: function(sel)
24642     {
24643         // this has strange effects when using with 
24644         // top toolbar - not sure if it's a great idea.
24645         //this.editor.contentWindow.focus();
24646         if (typeof sel != "undefined") {
24647             try {
24648                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24649             } catch(e) {
24650                 return this.doc.createRange();
24651             }
24652         } else {
24653             return this.doc.createRange();
24654         }
24655     },
24656     getParentElement: function()
24657     {
24658         
24659         this.assignDocWin();
24660         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24661         
24662         var range = this.createRange(sel);
24663          
24664         try {
24665             var p = range.commonAncestorContainer;
24666             while (p.nodeType == 3) { // text node
24667                 p = p.parentNode;
24668             }
24669             return p;
24670         } catch (e) {
24671             return null;
24672         }
24673     
24674     },
24675     
24676     
24677     
24678     // BC Hacks - cause I cant work out what i was trying to do..
24679     rangeIntersectsNode : function(range, node)
24680     {
24681         var nodeRange = node.ownerDocument.createRange();
24682         try {
24683             nodeRange.selectNode(node);
24684         }
24685         catch (e) {
24686             nodeRange.selectNodeContents(node);
24687         }
24688
24689         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24690                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24691     },
24692     rangeCompareNode : function(range, node) {
24693         var nodeRange = node.ownerDocument.createRange();
24694         try {
24695             nodeRange.selectNode(node);
24696         } catch (e) {
24697             nodeRange.selectNodeContents(node);
24698         }
24699         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24700         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24701
24702         if (nodeIsBefore && !nodeIsAfter)
24703             return 0;
24704         if (!nodeIsBefore && nodeIsAfter)
24705             return 1;
24706         if (nodeIsBefore && nodeIsAfter)
24707             return 2;
24708
24709         return 3;
24710     },
24711
24712     // private? - in a new class?
24713     cleanUpPaste :  function()
24714     {
24715         // cleans up the whole document..
24716       //  console.log('cleanuppaste');
24717         this.cleanUpChildren(this.doc.body)
24718         
24719         
24720     },
24721     cleanUpChildren : function (n)
24722     {
24723         if (!n.childNodes.length) {
24724             return;
24725         }
24726         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24727            this.cleanUpChild(n.childNodes[i]);
24728         }
24729     },
24730     
24731     
24732         
24733     
24734     cleanUpChild : function (node)
24735     {
24736         //console.log(node);
24737         if (node.nodeName == "#text") {
24738             // clean up silly Windows -- stuff?
24739             return; 
24740         }
24741         if (node.nodeName == "#comment") {
24742             node.parentNode.removeChild(node);
24743             // clean up silly Windows -- stuff?
24744             return; 
24745         }
24746         
24747         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24748             // remove node.
24749             node.parentNode.removeChild(node);
24750             return;
24751             
24752         }
24753         if (!node.attributes || !node.attributes.length) {
24754             this.cleanUpChildren(node);
24755             return;
24756         }
24757         
24758         function cleanAttr(n,v)
24759         {
24760             
24761             if (v.match(/^\./) || v.match(/^\//)) {
24762                 return;
24763             }
24764             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24765                 return;
24766             }
24767             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24768             node.removeAttribute(n);
24769             
24770         }
24771         
24772         function cleanStyle(n,v)
24773         {
24774             if (v.match(/expression/)) { //XSS?? should we even bother..
24775                 node.removeAttribute(n);
24776                 return;
24777             }
24778             
24779             
24780             var parts = v.split(/;/);
24781             Roo.each(parts, function(p) {
24782                 p = p.replace(/\s+/g,'');
24783                 if (!p.length) {
24784                     return;
24785                 }
24786                 var l = p.split(':').shift().replace(/\s+/g,'');
24787                 
24788                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24789                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24790                     node.removeAttribute(n);
24791                     return false;
24792                 }
24793             });
24794             
24795             
24796         }
24797         
24798         
24799         for (var i = node.attributes.length-1; i > -1 ; i--) {
24800             var a = node.attributes[i];
24801             //console.log(a);
24802             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24803                 node.removeAttribute(a.name);
24804                 return;
24805             }
24806             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24807                 cleanAttr(a.name,a.value); // fixme..
24808                 return;
24809             }
24810             if (a.name == 'style') {
24811                 cleanStyle(a.name,a.value);
24812             }
24813             /// clean up MS crap..
24814             if (a.name == 'class') {
24815                 if (a.value.match(/^Mso/)) {
24816                     node.className = '';
24817                 }
24818             }
24819             
24820             // style cleanup!?
24821             // class cleanup?
24822             
24823         }
24824         
24825         
24826         this.cleanUpChildren(node);
24827         
24828         
24829     }
24830     
24831     
24832     // hide stuff that is not compatible
24833     /**
24834      * @event blur
24835      * @hide
24836      */
24837     /**
24838      * @event change
24839      * @hide
24840      */
24841     /**
24842      * @event focus
24843      * @hide
24844      */
24845     /**
24846      * @event specialkey
24847      * @hide
24848      */
24849     /**
24850      * @cfg {String} fieldClass @hide
24851      */
24852     /**
24853      * @cfg {String} focusClass @hide
24854      */
24855     /**
24856      * @cfg {String} autoCreate @hide
24857      */
24858     /**
24859      * @cfg {String} inputType @hide
24860      */
24861     /**
24862      * @cfg {String} invalidClass @hide
24863      */
24864     /**
24865      * @cfg {String} invalidText @hide
24866      */
24867     /**
24868      * @cfg {String} msgFx @hide
24869      */
24870     /**
24871      * @cfg {String} validateOnBlur @hide
24872      */
24873 });
24874
24875 Roo.form.HtmlEditor.white = [
24876         'area', 'br', 'img', 'input', 'hr', 'wbr',
24877         
24878        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24879        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24880        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24881        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24882        'table',   'ul',         'xmp', 
24883        
24884        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24885       'thead',   'tr', 
24886      
24887       'dir', 'menu', 'ol', 'ul', 'dl',
24888        
24889       'embed',  'object'
24890 ];
24891
24892
24893 Roo.form.HtmlEditor.black = [
24894     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24895         'applet', // 
24896         'base',   'basefont', 'bgsound', 'blink',  'body', 
24897         'frame',  'frameset', 'head',    'html',   'ilayer', 
24898         'iframe', 'layer',  'link',     'meta',    'object',   
24899         'script', 'style' ,'title',  'xml' // clean later..
24900 ];
24901 Roo.form.HtmlEditor.clean = [
24902     'script', 'style', 'title', 'xml'
24903 ];
24904
24905 // attributes..
24906
24907 Roo.form.HtmlEditor.ablack = [
24908     'on'
24909 ];
24910     
24911 Roo.form.HtmlEditor.aclean = [ 
24912     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24913 ];
24914
24915 // protocols..
24916 Roo.form.HtmlEditor.pwhite= [
24917         'http',  'https',  'mailto'
24918 ];
24919
24920 Roo.form.HtmlEditor.cwhite= [
24921         'text-align',
24922         'font-size'
24923 ];
24924