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  * @extends Roo.util.Observable
30  * Defines the interface and base operation of items that that can be
31  * dragged or can be drop targets.  It was designed to be extended, overriding
32  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
33  * Up to three html elements can be associated with a DragDrop instance:
34  * <ul>
35  * <li>linked element: the element that is passed into the constructor.
36  * This is the element which defines the boundaries for interaction with
37  * other DragDrop objects.</li>
38  * <li>handle element(s): The drag operation only occurs if the element that
39  * was clicked matches a handle element.  By default this is the linked
40  * element, but there are times that you will want only a portion of the
41  * linked element to initiate the drag operation, and the setHandleElId()
42  * method provides a way to define this.</li>
43  * <li>drag element: this represents the element that would be moved along
44  * with the cursor during a drag operation.  By default, this is the linked
45  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
46  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
47  * </li>
48  * </ul>
49  * This class should not be instantiated until the onload event to ensure that
50  * the associated elements are available.
51  * The following would define a DragDrop obj that would interact with any
52  * other DragDrop obj in the "group1" group:
53  * <pre>
54  *  dd = new Roo.dd.DragDrop("div1", "group1");
55  * </pre>
56  * Since none of the event handlers have been implemented, nothing would
57  * actually happen if you were to run the code above.  Normally you would
58  * override this class or one of the default implementations, but you can
59  * also override the methods you want on an instance of the class...
60  * <pre>
61  *  dd.onDragDrop = function(e, id) {
62  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
63  *  }
64  * </pre>
65  * @constructor
66  * @param {String} id of the element that is linked to this instance
67  * @param {String} sGroup the group of related DragDrop objects
68  * @param {object} config an object containing configurable attributes
69  *                Valid properties for DragDrop:
70  *                    padding, isTarget, maintainOffset, primaryButtonOnly
71  */
72 Roo.dd.DragDrop = function(id, sGroup, config) {
73     if (id) {
74         this.init(id, sGroup, config);
75     }
76     
77 };
78
79 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
80
81     /**
82      * The id of the element associated with this object.  This is what we
83      * refer to as the "linked element" because the size and position of
84      * this element is used to determine when the drag and drop objects have
85      * interacted.
86      * @property id
87      * @type String
88      */
89     id: null,
90
91     /**
92      * Configuration attributes passed into the constructor
93      * @property config
94      * @type object
95      */
96     config: null,
97
98     /**
99      * The id of the element that will be dragged.  By default this is same
100      * as the linked element , but could be changed to another element. Ex:
101      * Roo.dd.DDProxy
102      * @property dragElId
103      * @type String
104      * @private
105      */
106     dragElId: null,
107
108     /**
109      * the id of the element that initiates the drag operation.  By default
110      * this is the linked element, but could be changed to be a child of this
111      * element.  This lets us do things like only starting the drag when the
112      * header element within the linked html element is clicked.
113      * @property handleElId
114      * @type String
115      * @private
116      */
117     handleElId: null,
118
119     /**
120      * An associative array of HTML tags that will be ignored if clicked.
121      * @property invalidHandleTypes
122      * @type {string: string}
123      */
124     invalidHandleTypes: null,
125
126     /**
127      * An associative array of ids for elements that will be ignored if clicked
128      * @property invalidHandleIds
129      * @type {string: string}
130      */
131     invalidHandleIds: null,
132
133     /**
134      * An indexted array of css class names for elements that will be ignored
135      * if clicked.
136      * @property invalidHandleClasses
137      * @type string[]
138      */
139     invalidHandleClasses: null,
140
141     /**
142      * The linked element's absolute X position at the time the drag was
143      * started
144      * @property startPageX
145      * @type int
146      * @private
147      */
148     startPageX: 0,
149
150     /**
151      * The linked element's absolute X position at the time the drag was
152      * started
153      * @property startPageY
154      * @type int
155      * @private
156      */
157     startPageY: 0,
158
159     /**
160      * The group defines a logical collection of DragDrop objects that are
161      * related.  Instances only get events when interacting with other
162      * DragDrop object in the same group.  This lets us define multiple
163      * groups using a single DragDrop subclass if we want.
164      * @property groups
165      * @type {string: string}
166      */
167     groups: null,
168
169     /**
170      * Individual drag/drop instances can be locked.  This will prevent
171      * onmousedown start drag.
172      * @property locked
173      * @type boolean
174      * @private
175      */
176     locked: false,
177
178     /**
179      * Lock this instance
180      * @method lock
181      */
182     lock: function() { this.locked = true; },
183
184     /**
185      * Unlock this instace
186      * @method unlock
187      */
188     unlock: function() { this.locked = false; },
189
190     /**
191      * By default, all insances can be a drop target.  This can be disabled by
192      * setting isTarget to false.
193      * @method isTarget
194      * @type boolean
195      */
196     isTarget: true,
197
198     /**
199      * The padding configured for this drag and drop object for calculating
200      * the drop zone intersection with this object.
201      * @method padding
202      * @type int[]
203      */
204     padding: null,
205
206     /**
207      * Cached reference to the linked element
208      * @property _domRef
209      * @private
210      */
211     _domRef: null,
212
213     /**
214      * Internal typeof flag
215      * @property __ygDragDrop
216      * @private
217      */
218     __ygDragDrop: true,
219
220     /**
221      * Set to true when horizontal contraints are applied
222      * @property constrainX
223      * @type boolean
224      * @private
225      */
226     constrainX: false,
227
228     /**
229      * Set to true when vertical contraints are applied
230      * @property constrainY
231      * @type boolean
232      * @private
233      */
234     constrainY: false,
235
236     /**
237      * The left constraint
238      * @property minX
239      * @type int
240      * @private
241      */
242     minX: 0,
243
244     /**
245      * The right constraint
246      * @property maxX
247      * @type int
248      * @private
249      */
250     maxX: 0,
251
252     /**
253      * The up constraint
254      * @property minY
255      * @type int
256      * @type int
257      * @private
258      */
259     minY: 0,
260
261     /**
262      * The down constraint
263      * @property maxY
264      * @type int
265      * @private
266      */
267     maxY: 0,
268
269     /**
270      * Maintain offsets when we resetconstraints.  Set to true when you want
271      * the position of the element relative to its parent to stay the same
272      * when the page changes
273      *
274      * @property maintainOffset
275      * @type boolean
276      */
277     maintainOffset: false,
278
279     /**
280      * Array of pixel locations the element will snap to if we specified a
281      * horizontal graduation/interval.  This array is generated automatically
282      * when you define a tick interval.
283      * @property xTicks
284      * @type int[]
285      */
286     xTicks: null,
287
288     /**
289      * Array of pixel locations the element will snap to if we specified a
290      * vertical graduation/interval.  This array is generated automatically
291      * when you define a tick interval.
292      * @property yTicks
293      * @type int[]
294      */
295     yTicks: null,
296
297     /**
298      * By default the drag and drop instance will only respond to the primary
299      * button click (left button for a right-handed mouse).  Set to true to
300      * allow drag and drop to start with any mouse click that is propogated
301      * by the browser
302      * @property primaryButtonOnly
303      * @type boolean
304      */
305     primaryButtonOnly: true,
306
307     /**
308      * The availabe property is false until the linked dom element is accessible.
309      * @property available
310      * @type boolean
311      */
312     available: false,
313
314     /**
315      * By default, drags can only be initiated if the mousedown occurs in the
316      * region the linked element is.  This is done in part to work around a
317      * bug in some browsers that mis-report the mousedown if the previous
318      * mouseup happened outside of the window.  This property is set to true
319      * if outer handles are defined.
320      *
321      * @property hasOuterHandles
322      * @type boolean
323      * @default false
324      */
325     hasOuterHandles: false,
326
327     /**
328      * Code that executes immediately before the startDrag event
329      * @method b4StartDrag
330      * @private
331      */
332     b4StartDrag: function(x, y) { },
333
334     /**
335      * Abstract method called after a drag/drop object is clicked
336      * and the drag or mousedown time thresholds have beeen met.
337      * @method startDrag
338      * @param {int} X click location
339      * @param {int} Y click location
340      */
341     startDrag: function(x, y) { /* override this */ },
342
343     /**
344      * Code that executes immediately before the onDrag event
345      * @method b4Drag
346      * @private
347      */
348     b4Drag: function(e) { },
349
350     /**
351      * Abstract method called during the onMouseMove event while dragging an
352      * object.
353      * @method onDrag
354      * @param {Event} e the mousemove event
355      */
356     onDrag: function(e) { /* override this */ },
357
358     /**
359      * Abstract method called when this element fist begins hovering over
360      * another DragDrop obj
361      * @method onDragEnter
362      * @param {Event} e the mousemove event
363      * @param {String|DragDrop[]} id In POINT mode, the element
364      * id this is hovering over.  In INTERSECT mode, an array of one or more
365      * dragdrop items being hovered over.
366      */
367     onDragEnter: function(e, id) { /* override this */ },
368
369     /**
370      * Code that executes immediately before the onDragOver event
371      * @method b4DragOver
372      * @private
373      */
374     b4DragOver: function(e) { },
375
376     /**
377      * Abstract method called when this element is hovering over another
378      * DragDrop obj
379      * @method onDragOver
380      * @param {Event} e the mousemove event
381      * @param {String|DragDrop[]} id In POINT mode, the element
382      * id this is hovering over.  In INTERSECT mode, an array of dd items
383      * being hovered over.
384      */
385     onDragOver: function(e, id) { /* override this */ },
386
387     /**
388      * Code that executes immediately before the onDragOut event
389      * @method b4DragOut
390      * @private
391      */
392     b4DragOut: function(e) { },
393
394     /**
395      * Abstract method called when we are no longer hovering over an element
396      * @method onDragOut
397      * @param {Event} e the mousemove event
398      * @param {String|DragDrop[]} id In POINT mode, the element
399      * id this was hovering over.  In INTERSECT mode, an array of dd items
400      * that the mouse is no longer over.
401      */
402     onDragOut: function(e, id) { /* override this */ },
403
404     /**
405      * Code that executes immediately before the onDragDrop event
406      * @method b4DragDrop
407      * @private
408      */
409     b4DragDrop: function(e) { },
410
411     /**
412      * Abstract method called when this item is dropped on another DragDrop
413      * obj
414      * @method onDragDrop
415      * @param {Event} e the mouseup event
416      * @param {String|DragDrop[]} id In POINT mode, the element
417      * id this was dropped on.  In INTERSECT mode, an array of dd items this
418      * was dropped on.
419      */
420     onDragDrop: function(e, id) { /* override this */ },
421
422     /**
423      * Abstract method called when this item is dropped on an area with no
424      * drop target
425      * @method onInvalidDrop
426      * @param {Event} e the mouseup event
427      */
428     onInvalidDrop: function(e) { /* override this */ },
429
430     /**
431      * Code that executes immediately before the endDrag event
432      * @method b4EndDrag
433      * @private
434      */
435     b4EndDrag: function(e) { },
436
437     /**
438      * Fired when we are done dragging the object
439      * @method endDrag
440      * @param {Event} e the mouseup event
441      */
442     endDrag: function(e) { /* override this */ },
443
444     /**
445      * Code executed immediately before the onMouseDown event
446      * @method b4MouseDown
447      * @param {Event} e the mousedown event
448      * @private
449      */
450     b4MouseDown: function(e) {  },
451
452     /**
453      * Event handler that fires when a drag/drop obj gets a mousedown
454      * @method onMouseDown
455      * @param {Event} e the mousedown event
456      */
457     onMouseDown: function(e) { /* override this */ },
458
459     /**
460      * Event handler that fires when a drag/drop obj gets a mouseup
461      * @method onMouseUp
462      * @param {Event} e the mouseup event
463      */
464     onMouseUp: function(e) { /* override this */ },
465
466     /**
467      * Override the onAvailable method to do what is needed after the initial
468      * position was determined.
469      * @method onAvailable
470      */
471     onAvailable: function () {
472     },
473
474     /*
475      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
476      * @type Object
477      */
478     defaultPadding : {left:0, right:0, top:0, bottom:0},
479
480     /*
481      * Initializes the drag drop object's constraints to restrict movement to a certain element.
482  *
483  * Usage:
484  <pre><code>
485  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
486                 { dragElId: "existingProxyDiv" });
487  dd.startDrag = function(){
488      this.constrainTo("parent-id");
489  };
490  </code></pre>
491  * Or you can initalize it using the {@link Roo.Element} object:
492  <pre><code>
493  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
494      startDrag : function(){
495          this.constrainTo("parent-id");
496      }
497  });
498  </code></pre>
499      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
500      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
501      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
502      * an object containing the sides to pad. For example: {right:10, bottom:10}
503      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
504      */
505     constrainTo : function(constrainTo, pad, inContent){
506         if(typeof pad == "number"){
507             pad = {left: pad, right:pad, top:pad, bottom:pad};
508         }
509         pad = pad || this.defaultPadding;
510         var b = Roo.get(this.getEl()).getBox();
511         var ce = Roo.get(constrainTo);
512         var s = ce.getScroll();
513         var c, cd = ce.dom;
514         if(cd == document.body){
515             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
516         }else{
517             xy = ce.getXY();
518             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
519         }
520
521
522         var topSpace = b.y - c.y;
523         var leftSpace = b.x - c.x;
524
525         this.resetConstraints();
526         this.setXConstraint(leftSpace - (pad.left||0), // left
527                 c.width - leftSpace - b.width - (pad.right||0) //right
528         );
529         this.setYConstraint(topSpace - (pad.top||0), //top
530                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
531         );
532     },
533
534     /**
535      * Returns a reference to the linked element
536      * @method getEl
537      * @return {HTMLElement} the html element
538      */
539     getEl: function() {
540         if (!this._domRef) {
541             this._domRef = Roo.getDom(this.id);
542         }
543
544         return this._domRef;
545     },
546
547     /**
548      * Returns a reference to the actual element to drag.  By default this is
549      * the same as the html element, but it can be assigned to another
550      * element. An example of this can be found in Roo.dd.DDProxy
551      * @method getDragEl
552      * @return {HTMLElement} the html element
553      */
554     getDragEl: function() {
555         return Roo.getDom(this.dragElId);
556     },
557
558     /**
559      * Sets up the DragDrop object.  Must be called in the constructor of any
560      * Roo.dd.DragDrop subclass
561      * @method init
562      * @param id the id of the linked element
563      * @param {String} sGroup the group of related items
564      * @param {object} config configuration attributes
565      */
566     init: function(id, sGroup, config) {
567         this.initTarget(id, sGroup, config);
568         Event.on(this.id, "mousedown", this.handleMouseDown, this);
569         // Event.on(this.id, "selectstart", Event.preventDefault);
570     },
571
572     /**
573      * Initializes Targeting functionality only... the object does not
574      * get a mousedown handler.
575      * @method initTarget
576      * @param id the id of the linked element
577      * @param {String} sGroup the group of related items
578      * @param {object} config configuration attributes
579      */
580     initTarget: function(id, sGroup, config) {
581
582         // configuration attributes
583         this.config = config || {};
584
585         // create a local reference to the drag and drop manager
586         this.DDM = Roo.dd.DDM;
587         // initialize the groups array
588         this.groups = {};
589
590         // assume that we have an element reference instead of an id if the
591         // parameter is not a string
592         if (typeof id !== "string") {
593             id = Roo.id(id);
594         }
595
596         // set the id
597         this.id = id;
598
599         // add to an interaction group
600         this.addToGroup((sGroup) ? sGroup : "default");
601
602         // We don't want to register this as the handle with the manager
603         // so we just set the id rather than calling the setter.
604         this.handleElId = id;
605
606         // the linked element is the element that gets dragged by default
607         this.setDragElId(id);
608
609         // by default, clicked anchors will not start drag operations.
610         this.invalidHandleTypes = { A: "A" };
611         this.invalidHandleIds = {};
612         this.invalidHandleClasses = [];
613
614         this.applyConfig();
615
616         this.handleOnAvailable();
617     },
618
619     /**
620      * Applies the configuration parameters that were passed into the constructor.
621      * This is supposed to happen at each level through the inheritance chain.  So
622      * a DDProxy implentation will execute apply config on DDProxy, DD, and
623      * DragDrop in order to get all of the parameters that are available in
624      * each object.
625      * @method applyConfig
626      */
627     applyConfig: function() {
628
629         // configurable properties:
630         //    padding, isTarget, maintainOffset, primaryButtonOnly
631         this.padding           = this.config.padding || [0, 0, 0, 0];
632         this.isTarget          = (this.config.isTarget !== false);
633         this.maintainOffset    = (this.config.maintainOffset);
634         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
635
636     },
637
638     /**
639      * Executed when the linked element is available
640      * @method handleOnAvailable
641      * @private
642      */
643     handleOnAvailable: function() {
644         this.available = true;
645         this.resetConstraints();
646         this.onAvailable();
647     },
648
649      /**
650      * Configures the padding for the target zone in px.  Effectively expands
651      * (or reduces) the virtual object size for targeting calculations.
652      * Supports css-style shorthand; if only one parameter is passed, all sides
653      * will have that padding, and if only two are passed, the top and bottom
654      * will have the first param, the left and right the second.
655      * @method setPadding
656      * @param {int} iTop    Top pad
657      * @param {int} iRight  Right pad
658      * @param {int} iBot    Bot pad
659      * @param {int} iLeft   Left pad
660      */
661     setPadding: function(iTop, iRight, iBot, iLeft) {
662         // this.padding = [iLeft, iRight, iTop, iBot];
663         if (!iRight && 0 !== iRight) {
664             this.padding = [iTop, iTop, iTop, iTop];
665         } else if (!iBot && 0 !== iBot) {
666             this.padding = [iTop, iRight, iTop, iRight];
667         } else {
668             this.padding = [iTop, iRight, iBot, iLeft];
669         }
670     },
671
672     /**
673      * Stores the initial placement of the linked element.
674      * @method setInitialPosition
675      * @param {int} diffX   the X offset, default 0
676      * @param {int} diffY   the Y offset, default 0
677      */
678     setInitPosition: function(diffX, diffY) {
679         var el = this.getEl();
680
681         if (!this.DDM.verifyEl(el)) {
682             return;
683         }
684
685         var dx = diffX || 0;
686         var dy = diffY || 0;
687
688         var p = Dom.getXY( el );
689
690         this.initPageX = p[0] - dx;
691         this.initPageY = p[1] - dy;
692
693         this.lastPageX = p[0];
694         this.lastPageY = p[1];
695
696
697         this.setStartPosition(p);
698     },
699
700     /**
701      * Sets the start position of the element.  This is set when the obj
702      * is initialized, the reset when a drag is started.
703      * @method setStartPosition
704      * @param pos current position (from previous lookup)
705      * @private
706      */
707     setStartPosition: function(pos) {
708         var p = pos || Dom.getXY( this.getEl() );
709         this.deltaSetXY = null;
710
711         this.startPageX = p[0];
712         this.startPageY = p[1];
713     },
714
715     /**
716      * Add this instance to a group of related drag/drop objects.  All
717      * instances belong to at least one group, and can belong to as many
718      * groups as needed.
719      * @method addToGroup
720      * @param sGroup {string} the name of the group
721      */
722     addToGroup: function(sGroup) {
723         this.groups[sGroup] = true;
724         this.DDM.regDragDrop(this, sGroup);
725     },
726
727     /**
728      * Remove's this instance from the supplied interaction group
729      * @method removeFromGroup
730      * @param {string}  sGroup  The group to drop
731      */
732     removeFromGroup: function(sGroup) {
733         if (this.groups[sGroup]) {
734             delete this.groups[sGroup];
735         }
736
737         this.DDM.removeDDFromGroup(this, sGroup);
738     },
739
740     /**
741      * Allows you to specify that an element other than the linked element
742      * will be moved with the cursor during a drag
743      * @method setDragElId
744      * @param id {string} the id of the element that will be used to initiate the drag
745      */
746     setDragElId: function(id) {
747         this.dragElId = id;
748     },
749
750     /**
751      * Allows you to specify a child of the linked element that should be
752      * used to initiate the drag operation.  An example of this would be if
753      * you have a content div with text and links.  Clicking anywhere in the
754      * content area would normally start the drag operation.  Use this method
755      * to specify that an element inside of the content div is the element
756      * that starts the drag operation.
757      * @method setHandleElId
758      * @param id {string} the id of the element that will be used to
759      * initiate the drag.
760      */
761     setHandleElId: function(id) {
762         if (typeof id !== "string") {
763             id = Roo.id(id);
764         }
765         this.handleElId = id;
766         this.DDM.regHandle(this.id, id);
767     },
768
769     /**
770      * Allows you to set an element outside of the linked element as a drag
771      * handle
772      * @method setOuterHandleElId
773      * @param id the id of the element that will be used to initiate the drag
774      */
775     setOuterHandleElId: function(id) {
776         if (typeof id !== "string") {
777             id = Roo.id(id);
778         }
779         Event.on(id, "mousedown",
780                 this.handleMouseDown, this);
781         this.setHandleElId(id);
782
783         this.hasOuterHandles = true;
784     },
785
786     /**
787      * Remove all drag and drop hooks for this element
788      * @method unreg
789      */
790     unreg: function() {
791         Event.un(this.id, "mousedown",
792                 this.handleMouseDown);
793         this._domRef = null;
794         this.DDM._remove(this);
795     },
796
797     destroy : function(){
798         this.unreg();
799     },
800
801     /**
802      * Returns true if this instance is locked, or the drag drop mgr is locked
803      * (meaning that all drag/drop is disabled on the page.)
804      * @method isLocked
805      * @return {boolean} true if this obj or all drag/drop is locked, else
806      * false
807      */
808     isLocked: function() {
809         return (this.DDM.isLocked() || this.locked);
810     },
811
812     /**
813      * Fired when this object is clicked
814      * @method handleMouseDown
815      * @param {Event} e
816      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
817      * @private
818      */
819     handleMouseDown: function(e, oDD){
820         if (this.primaryButtonOnly && e.button != 0) {
821             return;
822         }
823
824         if (this.isLocked()) {
825             return;
826         }
827
828         this.DDM.refreshCache(this.groups);
829
830         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
831         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
832         } else {
833             if (this.clickValidator(e)) {
834
835                 // set the initial element position
836                 this.setStartPosition();
837
838
839                 this.b4MouseDown(e);
840                 this.onMouseDown(e);
841
842                 this.DDM.handleMouseDown(e, this);
843
844                 this.DDM.stopEvent(e);
845             } else {
846
847
848             }
849         }
850     },
851
852     clickValidator: function(e) {
853         var target = e.getTarget();
854         return ( this.isValidHandleChild(target) &&
855                     (this.id == this.handleElId ||
856                         this.DDM.handleWasClicked(target, this.id)) );
857     },
858
859     /**
860      * Allows you to specify a tag name that should not start a drag operation
861      * when clicked.  This is designed to facilitate embedding links within a
862      * drag handle that do something other than start the drag.
863      * @method addInvalidHandleType
864      * @param {string} tagName the type of element to exclude
865      */
866     addInvalidHandleType: function(tagName) {
867         var type = tagName.toUpperCase();
868         this.invalidHandleTypes[type] = type;
869     },
870
871     /**
872      * Lets you to specify an element id for a child of a drag handle
873      * that should not initiate a drag
874      * @method addInvalidHandleId
875      * @param {string} id the element id of the element you wish to ignore
876      */
877     addInvalidHandleId: function(id) {
878         if (typeof id !== "string") {
879             id = Roo.id(id);
880         }
881         this.invalidHandleIds[id] = id;
882     },
883
884     /**
885      * Lets you specify a css class of elements that will not initiate a drag
886      * @method addInvalidHandleClass
887      * @param {string} cssClass the class of the elements you wish to ignore
888      */
889     addInvalidHandleClass: function(cssClass) {
890         this.invalidHandleClasses.push(cssClass);
891     },
892
893     /**
894      * Unsets an excluded tag name set by addInvalidHandleType
895      * @method removeInvalidHandleType
896      * @param {string} tagName the type of element to unexclude
897      */
898     removeInvalidHandleType: function(tagName) {
899         var type = tagName.toUpperCase();
900         // this.invalidHandleTypes[type] = null;
901         delete this.invalidHandleTypes[type];
902     },
903
904     /**
905      * Unsets an invalid handle id
906      * @method removeInvalidHandleId
907      * @param {string} id the id of the element to re-enable
908      */
909     removeInvalidHandleId: function(id) {
910         if (typeof id !== "string") {
911             id = Roo.id(id);
912         }
913         delete this.invalidHandleIds[id];
914     },
915
916     /**
917      * Unsets an invalid css class
918      * @method removeInvalidHandleClass
919      * @param {string} cssClass the class of the element(s) you wish to
920      * re-enable
921      */
922     removeInvalidHandleClass: function(cssClass) {
923         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
924             if (this.invalidHandleClasses[i] == cssClass) {
925                 delete this.invalidHandleClasses[i];
926             }
927         }
928     },
929
930     /**
931      * Checks the tag exclusion list to see if this click should be ignored
932      * @method isValidHandleChild
933      * @param {HTMLElement} node the HTMLElement to evaluate
934      * @return {boolean} true if this is a valid tag type, false if not
935      */
936     isValidHandleChild: function(node) {
937
938         var valid = true;
939         // var n = (node.nodeName == "#text") ? node.parentNode : node;
940         var nodeName;
941         try {
942             nodeName = node.nodeName.toUpperCase();
943         } catch(e) {
944             nodeName = node.nodeName;
945         }
946         valid = valid && !this.invalidHandleTypes[nodeName];
947         valid = valid && !this.invalidHandleIds[node.id];
948
949         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
950             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
951         }
952
953
954         return valid;
955
956     },
957
958     /**
959      * Create the array of horizontal tick marks if an interval was specified
960      * in setXConstraint().
961      * @method setXTicks
962      * @private
963      */
964     setXTicks: function(iStartX, iTickSize) {
965         this.xTicks = [];
966         this.xTickSize = iTickSize;
967
968         var tickMap = {};
969
970         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
971             if (!tickMap[i]) {
972                 this.xTicks[this.xTicks.length] = i;
973                 tickMap[i] = true;
974             }
975         }
976
977         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
978             if (!tickMap[i]) {
979                 this.xTicks[this.xTicks.length] = i;
980                 tickMap[i] = true;
981             }
982         }
983
984         this.xTicks.sort(this.DDM.numericSort) ;
985     },
986
987     /**
988      * Create the array of vertical tick marks if an interval was specified in
989      * setYConstraint().
990      * @method setYTicks
991      * @private
992      */
993     setYTicks: function(iStartY, iTickSize) {
994         this.yTicks = [];
995         this.yTickSize = iTickSize;
996
997         var tickMap = {};
998
999         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
1000             if (!tickMap[i]) {
1001                 this.yTicks[this.yTicks.length] = i;
1002                 tickMap[i] = true;
1003             }
1004         }
1005
1006         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
1007             if (!tickMap[i]) {
1008                 this.yTicks[this.yTicks.length] = i;
1009                 tickMap[i] = true;
1010             }
1011         }
1012
1013         this.yTicks.sort(this.DDM.numericSort) ;
1014     },
1015
1016     /**
1017      * By default, the element can be dragged any place on the screen.  Use
1018      * this method to limit the horizontal travel of the element.  Pass in
1019      * 0,0 for the parameters if you want to lock the drag to the y axis.
1020      * @method setXConstraint
1021      * @param {int} iLeft the number of pixels the element can move to the left
1022      * @param {int} iRight the number of pixels the element can move to the
1023      * right
1024      * @param {int} iTickSize optional parameter for specifying that the
1025      * element
1026      * should move iTickSize pixels at a time.
1027      */
1028     setXConstraint: function(iLeft, iRight, iTickSize) {
1029         this.leftConstraint = iLeft;
1030         this.rightConstraint = iRight;
1031
1032         this.minX = this.initPageX - iLeft;
1033         this.maxX = this.initPageX + iRight;
1034         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
1035
1036         this.constrainX = true;
1037     },
1038
1039     /**
1040      * Clears any constraints applied to this instance.  Also clears ticks
1041      * since they can't exist independent of a constraint at this time.
1042      * @method clearConstraints
1043      */
1044     clearConstraints: function() {
1045         this.constrainX = false;
1046         this.constrainY = false;
1047         this.clearTicks();
1048     },
1049
1050     /**
1051      * Clears any tick interval defined for this instance
1052      * @method clearTicks
1053      */
1054     clearTicks: function() {
1055         this.xTicks = null;
1056         this.yTicks = null;
1057         this.xTickSize = 0;
1058         this.yTickSize = 0;
1059     },
1060
1061     /**
1062      * By default, the element can be dragged any place on the screen.  Set
1063      * this to limit the vertical travel of the element.  Pass in 0,0 for the
1064      * parameters if you want to lock the drag to the x axis.
1065      * @method setYConstraint
1066      * @param {int} iUp the number of pixels the element can move up
1067      * @param {int} iDown the number of pixels the element can move down
1068      * @param {int} iTickSize optional parameter for specifying that the
1069      * element should move iTickSize pixels at a time.
1070      */
1071     setYConstraint: function(iUp, iDown, iTickSize) {
1072         this.topConstraint = iUp;
1073         this.bottomConstraint = iDown;
1074
1075         this.minY = this.initPageY - iUp;
1076         this.maxY = this.initPageY + iDown;
1077         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
1078
1079         this.constrainY = true;
1080
1081     },
1082
1083     /**
1084      * resetConstraints must be called if you manually reposition a dd element.
1085      * @method resetConstraints
1086      * @param {boolean} maintainOffset
1087      */
1088     resetConstraints: function() {
1089
1090
1091         // Maintain offsets if necessary
1092         if (this.initPageX || this.initPageX === 0) {
1093             // figure out how much this thing has moved
1094             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
1095             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
1096
1097             this.setInitPosition(dx, dy);
1098
1099         // This is the first time we have detected the element's position
1100         } else {
1101             this.setInitPosition();
1102         }
1103
1104         if (this.constrainX) {
1105             this.setXConstraint( this.leftConstraint,
1106                                  this.rightConstraint,
1107                                  this.xTickSize        );
1108         }
1109
1110         if (this.constrainY) {
1111             this.setYConstraint( this.topConstraint,
1112                                  this.bottomConstraint,
1113                                  this.yTickSize         );
1114         }
1115     },
1116
1117     /**
1118      * Normally the drag element is moved pixel by pixel, but we can specify
1119      * that it move a number of pixels at a time.  This method resolves the
1120      * location when we have it set up like this.
1121      * @method getTick
1122      * @param {int} val where we want to place the object
1123      * @param {int[]} tickArray sorted array of valid points
1124      * @return {int} the closest tick
1125      * @private
1126      */
1127     getTick: function(val, tickArray) {
1128
1129         if (!tickArray) {
1130             // If tick interval is not defined, it is effectively 1 pixel,
1131             // so we return the value passed to us.
1132             return val;
1133         } else if (tickArray[0] >= val) {
1134             // The value is lower than the first tick, so we return the first
1135             // tick.
1136             return tickArray[0];
1137         } else {
1138             for (var i=0, len=tickArray.length; i<len; ++i) {
1139                 var next = i + 1;
1140                 if (tickArray[next] && tickArray[next] >= val) {
1141                     var diff1 = val - tickArray[i];
1142                     var diff2 = tickArray[next] - val;
1143                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
1144                 }
1145             }
1146
1147             // The value is larger than the last tick, so we return the last
1148             // tick.
1149             return tickArray[tickArray.length - 1];
1150         }
1151     },
1152
1153     /**
1154      * toString method
1155      * @method toString
1156      * @return {string} string representation of the dd obj
1157      */
1158     toString: function() {
1159         return ("DragDrop " + this.id);
1160     }
1161
1162 });
1163
1164 })();
1165 /*
1166  * Based on:
1167  * Ext JS Library 1.1.1
1168  * Copyright(c) 2006-2007, Ext JS, LLC.
1169  *
1170  * Originally Released Under LGPL - original licence link has changed is not relivant.
1171  *
1172  * Fork - LGPL
1173  * <script type="text/javascript">
1174  */
1175
1176
1177 /**
1178  * The drag and drop utility provides a framework for building drag and drop
1179  * applications.  In addition to enabling drag and drop for specific elements,
1180  * the drag and drop elements are tracked by the manager class, and the
1181  * interactions between the various elements are tracked during the drag and
1182  * the implementing code is notified about these important moments.
1183  */
1184
1185 // Only load the library once.  Rewriting the manager class would orphan
1186 // existing drag and drop instances.
1187 if (!Roo.dd.DragDropMgr) {
1188
1189 /**
1190  * @class Roo.dd.DragDropMgr
1191  * DragDropMgr is a singleton that tracks the element interaction for
1192  * all DragDrop items in the window.  Generally, you will not call
1193  * this class directly, but it does have helper methods that could
1194  * be useful in your DragDrop implementations.
1195  * @singleton
1196  */
1197 Roo.dd.DragDropMgr = function() {
1198
1199     var Event = Roo.EventManager;
1200
1201     return {
1202
1203         /**
1204          * Two dimensional Array of registered DragDrop objects.  The first
1205          * dimension is the DragDrop item group, the second the DragDrop
1206          * object.
1207          * @property ids
1208          * @type {string: string}
1209          * @private
1210          * @static
1211          */
1212         ids: {},
1213
1214         /**
1215          * Array of element ids defined as drag handles.  Used to determine
1216          * if the element that generated the mousedown event is actually the
1217          * handle and not the html element itself.
1218          * @property handleIds
1219          * @type {string: string}
1220          * @private
1221          * @static
1222          */
1223         handleIds: {},
1224
1225         /**
1226          * the DragDrop object that is currently being dragged
1227          * @property dragCurrent
1228          * @type DragDrop
1229          * @private
1230          * @static
1231          **/
1232         dragCurrent: null,
1233
1234         /**
1235          * the DragDrop object(s) that are being hovered over
1236          * @property dragOvers
1237          * @type Array
1238          * @private
1239          * @static
1240          */
1241         dragOvers: {},
1242
1243         /**
1244          * the X distance between the cursor and the object being dragged
1245          * @property deltaX
1246          * @type int
1247          * @private
1248          * @static
1249          */
1250         deltaX: 0,
1251
1252         /**
1253          * the Y distance between the cursor and the object being dragged
1254          * @property deltaY
1255          * @type int
1256          * @private
1257          * @static
1258          */
1259         deltaY: 0,
1260
1261         /**
1262          * Flag to determine if we should prevent the default behavior of the
1263          * events we define. By default this is true, but this can be set to
1264          * false if you need the default behavior (not recommended)
1265          * @property preventDefault
1266          * @type boolean
1267          * @static
1268          */
1269         preventDefault: true,
1270
1271         /**
1272          * Flag to determine if we should stop the propagation of the events
1273          * we generate. This is true by default but you may want to set it to
1274          * false if the html element contains other features that require the
1275          * mouse click.
1276          * @property stopPropagation
1277          * @type boolean
1278          * @static
1279          */
1280         stopPropagation: true,
1281
1282         /**
1283          * Internal flag that is set to true when drag and drop has been
1284          * intialized
1285          * @property initialized
1286          * @private
1287          * @static
1288          */
1289         initalized: false,
1290
1291         /**
1292          * All drag and drop can be disabled.
1293          * @property locked
1294          * @private
1295          * @static
1296          */
1297         locked: false,
1298
1299         /**
1300          * Called the first time an element is registered.
1301          * @method init
1302          * @private
1303          * @static
1304          */
1305         init: function() {
1306             this.initialized = true;
1307         },
1308
1309         /**
1310          * In point mode, drag and drop interaction is defined by the
1311          * location of the cursor during the drag/drop
1312          * @property POINT
1313          * @type int
1314          * @static
1315          */
1316         POINT: 0,
1317
1318         /**
1319          * In intersect mode, drag and drop interactio nis defined by the
1320          * overlap of two or more drag and drop objects.
1321          * @property INTERSECT
1322          * @type int
1323          * @static
1324          */
1325         INTERSECT: 1,
1326
1327         /**
1328          * The current drag and drop mode.  Default: POINT
1329          * @property mode
1330          * @type int
1331          * @static
1332          */
1333         mode: 0,
1334
1335         /**
1336          * Runs method on all drag and drop objects
1337          * @method _execOnAll
1338          * @private
1339          * @static
1340          */
1341         _execOnAll: function(sMethod, args) {
1342             for (var i in this.ids) {
1343                 for (var j in this.ids[i]) {
1344                     var oDD = this.ids[i][j];
1345                     if (! this.isTypeOfDD(oDD)) {
1346                         continue;
1347                     }
1348                     oDD[sMethod].apply(oDD, args);
1349                 }
1350             }
1351         },
1352
1353         /**
1354          * Drag and drop initialization.  Sets up the global event handlers
1355          * @method _onLoad
1356          * @private
1357          * @static
1358          */
1359         _onLoad: function() {
1360
1361             this.init();
1362
1363
1364             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
1365             Event.on(document, "mousemove", this.handleMouseMove, this, true);
1366             Event.on(window,   "unload",    this._onUnload, this, true);
1367             Event.on(window,   "resize",    this._onResize, this, true);
1368             // Event.on(window,   "mouseout",    this._test);
1369
1370         },
1371
1372         /**
1373          * Reset constraints on all drag and drop objs
1374          * @method _onResize
1375          * @private
1376          * @static
1377          */
1378         _onResize: function(e) {
1379             this._execOnAll("resetConstraints", []);
1380         },
1381
1382         /**
1383          * Lock all drag and drop functionality
1384          * @method lock
1385          * @static
1386          */
1387         lock: function() { this.locked = true; },
1388
1389         /**
1390          * Unlock all drag and drop functionality
1391          * @method unlock
1392          * @static
1393          */
1394         unlock: function() { this.locked = false; },
1395
1396         /**
1397          * Is drag and drop locked?
1398          * @method isLocked
1399          * @return {boolean} True if drag and drop is locked, false otherwise.
1400          * @static
1401          */
1402         isLocked: function() { return this.locked; },
1403
1404         /**
1405          * Location cache that is set for all drag drop objects when a drag is
1406          * initiated, cleared when the drag is finished.
1407          * @property locationCache
1408          * @private
1409          * @static
1410          */
1411         locationCache: {},
1412
1413         /**
1414          * Set useCache to false if you want to force object the lookup of each
1415          * drag and drop linked element constantly during a drag.
1416          * @property useCache
1417          * @type boolean
1418          * @static
1419          */
1420         useCache: true,
1421
1422         /**
1423          * The number of pixels that the mouse needs to move after the
1424          * mousedown before the drag is initiated.  Default=3;
1425          * @property clickPixelThresh
1426          * @type int
1427          * @static
1428          */
1429         clickPixelThresh: 3,
1430
1431         /**
1432          * The number of milliseconds after the mousedown event to initiate the
1433          * drag if we don't get a mouseup event. Default=1000
1434          * @property clickTimeThresh
1435          * @type int
1436          * @static
1437          */
1438         clickTimeThresh: 350,
1439
1440         /**
1441          * Flag that indicates that either the drag pixel threshold or the
1442          * mousdown time threshold has been met
1443          * @property dragThreshMet
1444          * @type boolean
1445          * @private
1446          * @static
1447          */
1448         dragThreshMet: false,
1449
1450         /**
1451          * Timeout used for the click time threshold
1452          * @property clickTimeout
1453          * @type Object
1454          * @private
1455          * @static
1456          */
1457         clickTimeout: null,
1458
1459         /**
1460          * The X position of the mousedown event stored for later use when a
1461          * drag threshold is met.
1462          * @property startX
1463          * @type int
1464          * @private
1465          * @static
1466          */
1467         startX: 0,
1468
1469         /**
1470          * The Y position of the mousedown event stored for later use when a
1471          * drag threshold is met.
1472          * @property startY
1473          * @type int
1474          * @private
1475          * @static
1476          */
1477         startY: 0,
1478
1479         /**
1480          * Each DragDrop instance must be registered with the DragDropMgr.
1481          * This is executed in DragDrop.init()
1482          * @method regDragDrop
1483          * @param {DragDrop} oDD the DragDrop object to register
1484          * @param {String} sGroup the name of the group this element belongs to
1485          * @static
1486          */
1487         regDragDrop: function(oDD, sGroup) {
1488             if (!this.initialized) { this.init(); }
1489
1490             if (!this.ids[sGroup]) {
1491                 this.ids[sGroup] = {};
1492             }
1493             this.ids[sGroup][oDD.id] = oDD;
1494         },
1495
1496         /**
1497          * Removes the supplied dd instance from the supplied group. Executed
1498          * by DragDrop.removeFromGroup, so don't call this function directly.
1499          * @method removeDDFromGroup
1500          * @private
1501          * @static
1502          */
1503         removeDDFromGroup: function(oDD, sGroup) {
1504             if (!this.ids[sGroup]) {
1505                 this.ids[sGroup] = {};
1506             }
1507
1508             var obj = this.ids[sGroup];
1509             if (obj && obj[oDD.id]) {
1510                 delete obj[oDD.id];
1511             }
1512         },
1513
1514         /**
1515          * Unregisters a drag and drop item.  This is executed in
1516          * DragDrop.unreg, use that method instead of calling this directly.
1517          * @method _remove
1518          * @private
1519          * @static
1520          */
1521         _remove: function(oDD) {
1522             for (var g in oDD.groups) {
1523                 if (g && this.ids[g][oDD.id]) {
1524                     delete this.ids[g][oDD.id];
1525                 }
1526             }
1527             delete this.handleIds[oDD.id];
1528         },
1529
1530         /**
1531          * Each DragDrop handle element must be registered.  This is done
1532          * automatically when executing DragDrop.setHandleElId()
1533          * @method regHandle
1534          * @param {String} sDDId the DragDrop id this element is a handle for
1535          * @param {String} sHandleId the id of the element that is the drag
1536          * handle
1537          * @static
1538          */
1539         regHandle: function(sDDId, sHandleId) {
1540             if (!this.handleIds[sDDId]) {
1541                 this.handleIds[sDDId] = {};
1542             }
1543             this.handleIds[sDDId][sHandleId] = sHandleId;
1544         },
1545
1546         /**
1547          * Utility function to determine if a given element has been
1548          * registered as a drag drop item.
1549          * @method isDragDrop
1550          * @param {String} id the element id to check
1551          * @return {boolean} true if this element is a DragDrop item,
1552          * false otherwise
1553          * @static
1554          */
1555         isDragDrop: function(id) {
1556             return ( this.getDDById(id) ) ? true : false;
1557         },
1558
1559         /**
1560          * Returns the drag and drop instances that are in all groups the
1561          * passed in instance belongs to.
1562          * @method getRelated
1563          * @param {DragDrop} p_oDD the obj to get related data for
1564          * @param {boolean} bTargetsOnly if true, only return targetable objs
1565          * @return {DragDrop[]} the related instances
1566          * @static
1567          */
1568         getRelated: function(p_oDD, bTargetsOnly) {
1569             var oDDs = [];
1570             for (var i in p_oDD.groups) {
1571                 for (j in this.ids[i]) {
1572                     var dd = this.ids[i][j];
1573                     if (! this.isTypeOfDD(dd)) {
1574                         continue;
1575                     }
1576                     if (!bTargetsOnly || dd.isTarget) {
1577                         oDDs[oDDs.length] = dd;
1578                     }
1579                 }
1580             }
1581
1582             return oDDs;
1583         },
1584
1585         /**
1586          * Returns true if the specified dd target is a legal target for
1587          * the specifice drag obj
1588          * @method isLegalTarget
1589          * @param {DragDrop} the drag obj
1590          * @param {DragDrop} the target
1591          * @return {boolean} true if the target is a legal target for the
1592          * dd obj
1593          * @static
1594          */
1595         isLegalTarget: function (oDD, oTargetDD) {
1596             var targets = this.getRelated(oDD, true);
1597             for (var i=0, len=targets.length;i<len;++i) {
1598                 if (targets[i].id == oTargetDD.id) {
1599                     return true;
1600                 }
1601             }
1602
1603             return false;
1604         },
1605
1606         /**
1607          * My goal is to be able to transparently determine if an object is
1608          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
1609          * returns "object", oDD.constructor.toString() always returns
1610          * "DragDrop" and not the name of the subclass.  So for now it just
1611          * evaluates a well-known variable in DragDrop.
1612          * @method isTypeOfDD
1613          * @param {Object} the object to evaluate
1614          * @return {boolean} true if typeof oDD = DragDrop
1615          * @static
1616          */
1617         isTypeOfDD: function (oDD) {
1618             return (oDD && oDD.__ygDragDrop);
1619         },
1620
1621         /**
1622          * Utility function to determine if a given element has been
1623          * registered as a drag drop handle for the given Drag Drop object.
1624          * @method isHandle
1625          * @param {String} id the element id to check
1626          * @return {boolean} true if this element is a DragDrop handle, false
1627          * otherwise
1628          * @static
1629          */
1630         isHandle: function(sDDId, sHandleId) {
1631             return ( this.handleIds[sDDId] &&
1632                             this.handleIds[sDDId][sHandleId] );
1633         },
1634
1635         /**
1636          * Returns the DragDrop instance for a given id
1637          * @method getDDById
1638          * @param {String} id the id of the DragDrop object
1639          * @return {DragDrop} the drag drop object, null if it is not found
1640          * @static
1641          */
1642         getDDById: function(id) {
1643             for (var i in this.ids) {
1644                 if (this.ids[i][id]) {
1645                     return this.ids[i][id];
1646                 }
1647             }
1648             return null;
1649         },
1650
1651         /**
1652          * Fired after a registered DragDrop object gets the mousedown event.
1653          * Sets up the events required to track the object being dragged
1654          * @method handleMouseDown
1655          * @param {Event} e the event
1656          * @param oDD the DragDrop object being dragged
1657          * @private
1658          * @static
1659          */
1660         handleMouseDown: function(e, oDD) {
1661             if(Roo.QuickTips){
1662                 Roo.QuickTips.disable();
1663             }
1664             this.currentTarget = e.getTarget();
1665
1666             this.dragCurrent = oDD;
1667
1668             var el = oDD.getEl();
1669
1670             // track start position
1671             this.startX = e.getPageX();
1672             this.startY = e.getPageY();
1673
1674             this.deltaX = this.startX - el.offsetLeft;
1675             this.deltaY = this.startY - el.offsetTop;
1676
1677             this.dragThreshMet = false;
1678
1679             this.clickTimeout = setTimeout(
1680                     function() {
1681                         var DDM = Roo.dd.DDM;
1682                         DDM.startDrag(DDM.startX, DDM.startY);
1683                     },
1684                     this.clickTimeThresh );
1685         },
1686
1687         /**
1688          * Fired when either the drag pixel threshol or the mousedown hold
1689          * time threshold has been met.
1690          * @method startDrag
1691          * @param x {int} the X position of the original mousedown
1692          * @param y {int} the Y position of the original mousedown
1693          * @static
1694          */
1695         startDrag: function(x, y) {
1696             clearTimeout(this.clickTimeout);
1697             if (this.dragCurrent) {
1698                 this.dragCurrent.b4StartDrag(x, y);
1699                 this.dragCurrent.startDrag(x, y);
1700             }
1701             this.dragThreshMet = true;
1702         },
1703
1704         /**
1705          * Internal function to handle the mouseup event.  Will be invoked
1706          * from the context of the document.
1707          * @method handleMouseUp
1708          * @param {Event} e the event
1709          * @private
1710          * @static
1711          */
1712         handleMouseUp: function(e) {
1713
1714             if(Roo.QuickTips){
1715                 Roo.QuickTips.enable();
1716             }
1717             if (! this.dragCurrent) {
1718                 return;
1719             }
1720
1721             clearTimeout(this.clickTimeout);
1722
1723             if (this.dragThreshMet) {
1724                 this.fireEvents(e, true);
1725             } else {
1726             }
1727
1728             this.stopDrag(e);
1729
1730             this.stopEvent(e);
1731         },
1732
1733         /**
1734          * Utility to stop event propagation and event default, if these
1735          * features are turned on.
1736          * @method stopEvent
1737          * @param {Event} e the event as returned by this.getEvent()
1738          * @static
1739          */
1740         stopEvent: function(e){
1741             if(this.stopPropagation) {
1742                 e.stopPropagation();
1743             }
1744
1745             if (this.preventDefault) {
1746                 e.preventDefault();
1747             }
1748         },
1749
1750         /**
1751          * Internal function to clean up event handlers after the drag
1752          * operation is complete
1753          * @method stopDrag
1754          * @param {Event} e the event
1755          * @private
1756          * @static
1757          */
1758         stopDrag: function(e) {
1759             // Fire the drag end event for the item that was dragged
1760             if (this.dragCurrent) {
1761                 if (this.dragThreshMet) {
1762                     this.dragCurrent.b4EndDrag(e);
1763                     this.dragCurrent.endDrag(e);
1764                 }
1765
1766                 this.dragCurrent.onMouseUp(e);
1767             }
1768
1769             this.dragCurrent = null;
1770             this.dragOvers = {};
1771         },
1772
1773         /**
1774          * Internal function to handle the mousemove event.  Will be invoked
1775          * from the context of the html element.
1776          *
1777          * @TODO figure out what we can do about mouse events lost when the
1778          * user drags objects beyond the window boundary.  Currently we can
1779          * detect this in internet explorer by verifying that the mouse is
1780          * down during the mousemove event.  Firefox doesn't give us the
1781          * button state on the mousemove event.
1782          * @method handleMouseMove
1783          * @param {Event} e the event
1784          * @private
1785          * @static
1786          */
1787         handleMouseMove: function(e) {
1788             if (! this.dragCurrent) {
1789                 return true;
1790             }
1791
1792             // var button = e.which || e.button;
1793
1794             // check for IE mouseup outside of page boundary
1795             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
1796                 this.stopEvent(e);
1797                 return this.handleMouseUp(e);
1798             }
1799
1800             if (!this.dragThreshMet) {
1801                 var diffX = Math.abs(this.startX - e.getPageX());
1802                 var diffY = Math.abs(this.startY - e.getPageY());
1803                 if (diffX > this.clickPixelThresh ||
1804                             diffY > this.clickPixelThresh) {
1805                     this.startDrag(this.startX, this.startY);
1806                 }
1807             }
1808
1809             if (this.dragThreshMet) {
1810                 this.dragCurrent.b4Drag(e);
1811                 this.dragCurrent.onDrag(e);
1812                 if(!this.dragCurrent.moveOnly){
1813                     this.fireEvents(e, false);
1814                 }
1815             }
1816
1817             this.stopEvent(e);
1818
1819             return true;
1820         },
1821
1822         /**
1823          * Iterates over all of the DragDrop elements to find ones we are
1824          * hovering over or dropping on
1825          * @method fireEvents
1826          * @param {Event} e the event
1827          * @param {boolean} isDrop is this a drop op or a mouseover op?
1828          * @private
1829          * @static
1830          */
1831         fireEvents: function(e, isDrop) {
1832             var dc = this.dragCurrent;
1833
1834             // If the user did the mouse up outside of the window, we could
1835             // get here even though we have ended the drag.
1836             if (!dc || dc.isLocked()) {
1837                 return;
1838             }
1839
1840             var pt = e.getPoint();
1841
1842             // cache the previous dragOver array
1843             var oldOvers = [];
1844
1845             var outEvts   = [];
1846             var overEvts  = [];
1847             var dropEvts  = [];
1848             var enterEvts = [];
1849
1850             // Check to see if the object(s) we were hovering over is no longer
1851             // being hovered over so we can fire the onDragOut event
1852             for (var i in this.dragOvers) {
1853
1854                 var ddo = this.dragOvers[i];
1855
1856                 if (! this.isTypeOfDD(ddo)) {
1857                     continue;
1858                 }
1859
1860                 if (! this.isOverTarget(pt, ddo, this.mode)) {
1861                     outEvts.push( ddo );
1862                 }
1863
1864                 oldOvers[i] = true;
1865                 delete this.dragOvers[i];
1866             }
1867
1868             for (var sGroup in dc.groups) {
1869
1870                 if ("string" != typeof sGroup) {
1871                     continue;
1872                 }
1873
1874                 for (i in this.ids[sGroup]) {
1875                     var oDD = this.ids[sGroup][i];
1876                     if (! this.isTypeOfDD(oDD)) {
1877                         continue;
1878                     }
1879
1880                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
1881                         if (this.isOverTarget(pt, oDD, this.mode)) {
1882                             // look for drop interactions
1883                             if (isDrop) {
1884                                 dropEvts.push( oDD );
1885                             // look for drag enter and drag over interactions
1886                             } else {
1887
1888                                 // initial drag over: dragEnter fires
1889                                 if (!oldOvers[oDD.id]) {
1890                                     enterEvts.push( oDD );
1891                                 // subsequent drag overs: dragOver fires
1892                                 } else {
1893                                     overEvts.push( oDD );
1894                                 }
1895
1896                                 this.dragOvers[oDD.id] = oDD;
1897                             }
1898                         }
1899                     }
1900                 }
1901             }
1902
1903             if (this.mode) {
1904                 if (outEvts.length) {
1905                     dc.b4DragOut(e, outEvts);
1906                     dc.onDragOut(e, outEvts);
1907                 }
1908
1909                 if (enterEvts.length) {
1910                     dc.onDragEnter(e, enterEvts);
1911                 }
1912
1913                 if (overEvts.length) {
1914                     dc.b4DragOver(e, overEvts);
1915                     dc.onDragOver(e, overEvts);
1916                 }
1917
1918                 if (dropEvts.length) {
1919                     dc.b4DragDrop(e, dropEvts);
1920                     dc.onDragDrop(e, dropEvts);
1921                 }
1922
1923             } else {
1924                 // fire dragout events
1925                 var len = 0;
1926                 for (i=0, len=outEvts.length; i<len; ++i) {
1927                     dc.b4DragOut(e, outEvts[i].id);
1928                     dc.onDragOut(e, outEvts[i].id);
1929                 }
1930
1931                 // fire enter events
1932                 for (i=0,len=enterEvts.length; i<len; ++i) {
1933                     // dc.b4DragEnter(e, oDD.id);
1934                     dc.onDragEnter(e, enterEvts[i].id);
1935                 }
1936
1937                 // fire over events
1938                 for (i=0,len=overEvts.length; i<len; ++i) {
1939                     dc.b4DragOver(e, overEvts[i].id);
1940                     dc.onDragOver(e, overEvts[i].id);
1941                 }
1942
1943                 // fire drop events
1944                 for (i=0, len=dropEvts.length; i<len; ++i) {
1945                     dc.b4DragDrop(e, dropEvts[i].id);
1946                     dc.onDragDrop(e, dropEvts[i].id);
1947                 }
1948
1949             }
1950
1951             // notify about a drop that did not find a target
1952             if (isDrop && !dropEvts.length) {
1953                 dc.onInvalidDrop(e);
1954             }
1955
1956         },
1957
1958         /**
1959          * Helper function for getting the best match from the list of drag
1960          * and drop objects returned by the drag and drop events when we are
1961          * in INTERSECT mode.  It returns either the first object that the
1962          * cursor is over, or the object that has the greatest overlap with
1963          * the dragged element.
1964          * @method getBestMatch
1965          * @param  {DragDrop[]} dds The array of drag and drop objects
1966          * targeted
1967          * @return {DragDrop}       The best single match
1968          * @static
1969          */
1970         getBestMatch: function(dds) {
1971             var winner = null;
1972             // Return null if the input is not what we expect
1973             //if (!dds || !dds.length || dds.length == 0) {
1974                // winner = null;
1975             // If there is only one item, it wins
1976             //} else if (dds.length == 1) {
1977
1978             var len = dds.length;
1979
1980             if (len == 1) {
1981                 winner = dds[0];
1982             } else {
1983                 // Loop through the targeted items
1984                 for (var i=0; i<len; ++i) {
1985                     var dd = dds[i];
1986                     // If the cursor is over the object, it wins.  If the
1987                     // cursor is over multiple matches, the first one we come
1988                     // to wins.
1989                     if (dd.cursorIsOver) {
1990                         winner = dd;
1991                         break;
1992                     // Otherwise the object with the most overlap wins
1993                     } else {
1994                         if (!winner ||
1995                             winner.overlap.getArea() < dd.overlap.getArea()) {
1996                             winner = dd;
1997                         }
1998                     }
1999                 }
2000             }
2001
2002             return winner;
2003         },
2004
2005         /**
2006          * Refreshes the cache of the top-left and bottom-right points of the
2007          * drag and drop objects in the specified group(s).  This is in the
2008          * format that is stored in the drag and drop instance, so typical
2009          * usage is:
2010          * <code>
2011          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
2012          * </code>
2013          * Alternatively:
2014          * <code>
2015          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
2016          * </code>
2017          * @TODO this really should be an indexed array.  Alternatively this
2018          * method could accept both.
2019          * @method refreshCache
2020          * @param {Object} groups an associative array of groups to refresh
2021          * @static
2022          */
2023         refreshCache: function(groups) {
2024             for (var sGroup in groups) {
2025                 if ("string" != typeof sGroup) {
2026                     continue;
2027                 }
2028                 for (var i in this.ids[sGroup]) {
2029                     var oDD = this.ids[sGroup][i];
2030
2031                     if (this.isTypeOfDD(oDD)) {
2032                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
2033                         var loc = this.getLocation(oDD);
2034                         if (loc) {
2035                             this.locationCache[oDD.id] = loc;
2036                         } else {
2037                             delete this.locationCache[oDD.id];
2038                             // this will unregister the drag and drop object if
2039                             // the element is not in a usable state
2040                             // oDD.unreg();
2041                         }
2042                     }
2043                 }
2044             }
2045         },
2046
2047         /**
2048          * This checks to make sure an element exists and is in the DOM.  The
2049          * main purpose is to handle cases where innerHTML is used to remove
2050          * drag and drop objects from the DOM.  IE provides an 'unspecified
2051          * error' when trying to access the offsetParent of such an element
2052          * @method verifyEl
2053          * @param {HTMLElement} el the element to check
2054          * @return {boolean} true if the element looks usable
2055          * @static
2056          */
2057         verifyEl: function(el) {
2058             if (el) {
2059                 var parent;
2060                 if(Roo.isIE){
2061                     try{
2062                         parent = el.offsetParent;
2063                     }catch(e){}
2064                 }else{
2065                     parent = el.offsetParent;
2066                 }
2067                 if (parent) {
2068                     return true;
2069                 }
2070             }
2071
2072             return false;
2073         },
2074
2075         /**
2076          * Returns a Region object containing the drag and drop element's position
2077          * and size, including the padding configured for it
2078          * @method getLocation
2079          * @param {DragDrop} oDD the drag and drop object to get the
2080          *                       location for
2081          * @return {Roo.lib.Region} a Region object representing the total area
2082          *                             the element occupies, including any padding
2083          *                             the instance is configured for.
2084          * @static
2085          */
2086         getLocation: function(oDD) {
2087             if (! this.isTypeOfDD(oDD)) {
2088                 return null;
2089             }
2090
2091             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
2092
2093             try {
2094                 pos= Roo.lib.Dom.getXY(el);
2095             } catch (e) { }
2096
2097             if (!pos) {
2098                 return null;
2099             }
2100
2101             x1 = pos[0];
2102             x2 = x1 + el.offsetWidth;
2103             y1 = pos[1];
2104             y2 = y1 + el.offsetHeight;
2105
2106             t = y1 - oDD.padding[0];
2107             r = x2 + oDD.padding[1];
2108             b = y2 + oDD.padding[2];
2109             l = x1 - oDD.padding[3];
2110
2111             return new Roo.lib.Region( t, r, b, l );
2112         },
2113
2114         /**
2115          * Checks the cursor location to see if it over the target
2116          * @method isOverTarget
2117          * @param {Roo.lib.Point} pt The point to evaluate
2118          * @param {DragDrop} oTarget the DragDrop object we are inspecting
2119          * @return {boolean} true if the mouse is over the target
2120          * @private
2121          * @static
2122          */
2123         isOverTarget: function(pt, oTarget, intersect) {
2124             // use cache if available
2125             var loc = this.locationCache[oTarget.id];
2126             if (!loc || !this.useCache) {
2127                 loc = this.getLocation(oTarget);
2128                 this.locationCache[oTarget.id] = loc;
2129
2130             }
2131
2132             if (!loc) {
2133                 return false;
2134             }
2135
2136             oTarget.cursorIsOver = loc.contains( pt );
2137
2138             // DragDrop is using this as a sanity check for the initial mousedown
2139             // in this case we are done.  In POINT mode, if the drag obj has no
2140             // contraints, we are also done. Otherwise we need to evaluate the
2141             // location of the target as related to the actual location of the
2142             // dragged element.
2143             var dc = this.dragCurrent;
2144             if (!dc || !dc.getTargetCoord ||
2145                     (!intersect && !dc.constrainX && !dc.constrainY)) {
2146                 return oTarget.cursorIsOver;
2147             }
2148
2149             oTarget.overlap = null;
2150
2151             // Get the current location of the drag element, this is the
2152             // location of the mouse event less the delta that represents
2153             // where the original mousedown happened on the element.  We
2154             // need to consider constraints and ticks as well.
2155             var pos = dc.getTargetCoord(pt.x, pt.y);
2156
2157             var el = dc.getDragEl();
2158             var curRegion = new Roo.lib.Region( pos.y,
2159                                                    pos.x + el.offsetWidth,
2160                                                    pos.y + el.offsetHeight,
2161                                                    pos.x );
2162
2163             var overlap = curRegion.intersect(loc);
2164
2165             if (overlap) {
2166                 oTarget.overlap = overlap;
2167                 return (intersect) ? true : oTarget.cursorIsOver;
2168             } else {
2169                 return false;
2170             }
2171         },
2172
2173         /**
2174          * unload event handler
2175          * @method _onUnload
2176          * @private
2177          * @static
2178          */
2179         _onUnload: function(e, me) {
2180             Roo.dd.DragDropMgr.unregAll();
2181         },
2182
2183         /**
2184          * Cleans up the drag and drop events and objects.
2185          * @method unregAll
2186          * @private
2187          * @static
2188          */
2189         unregAll: function() {
2190
2191             if (this.dragCurrent) {
2192                 this.stopDrag();
2193                 this.dragCurrent = null;
2194             }
2195
2196             this._execOnAll("unreg", []);
2197
2198             for (i in this.elementCache) {
2199                 delete this.elementCache[i];
2200             }
2201
2202             this.elementCache = {};
2203             this.ids = {};
2204         },
2205
2206         /**
2207          * A cache of DOM elements
2208          * @property elementCache
2209          * @private
2210          * @static
2211          */
2212         elementCache: {},
2213
2214         /**
2215          * Get the wrapper for the DOM element specified
2216          * @method getElWrapper
2217          * @param {String} id the id of the element to get
2218          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
2219          * @private
2220          * @deprecated This wrapper isn't that useful
2221          * @static
2222          */
2223         getElWrapper: function(id) {
2224             var oWrapper = this.elementCache[id];
2225             if (!oWrapper || !oWrapper.el) {
2226                 oWrapper = this.elementCache[id] =
2227                     new this.ElementWrapper(Roo.getDom(id));
2228             }
2229             return oWrapper;
2230         },
2231
2232         /**
2233          * Returns the actual DOM element
2234          * @method getElement
2235          * @param {String} id the id of the elment to get
2236          * @return {Object} The element
2237          * @deprecated use Roo.getDom instead
2238          * @static
2239          */
2240         getElement: function(id) {
2241             return Roo.getDom(id);
2242         },
2243
2244         /**
2245          * Returns the style property for the DOM element (i.e.,
2246          * document.getElById(id).style)
2247          * @method getCss
2248          * @param {String} id the id of the elment to get
2249          * @return {Object} The style property of the element
2250          * @deprecated use Roo.getDom instead
2251          * @static
2252          */
2253         getCss: function(id) {
2254             var el = Roo.getDom(id);
2255             return (el) ? el.style : null;
2256         },
2257
2258         /**
2259          * Inner class for cached elements
2260          * @class DragDropMgr.ElementWrapper
2261          * @for DragDropMgr
2262          * @private
2263          * @deprecated
2264          */
2265         ElementWrapper: function(el) {
2266                 /**
2267                  * The element
2268                  * @property el
2269                  */
2270                 this.el = el || null;
2271                 /**
2272                  * The element id
2273                  * @property id
2274                  */
2275                 this.id = this.el && el.id;
2276                 /**
2277                  * A reference to the style property
2278                  * @property css
2279                  */
2280                 this.css = this.el && el.style;
2281             },
2282
2283         /**
2284          * Returns the X position of an html element
2285          * @method getPosX
2286          * @param el the element for which to get the position
2287          * @return {int} the X coordinate
2288          * @for DragDropMgr
2289          * @deprecated use Roo.lib.Dom.getX instead
2290          * @static
2291          */
2292         getPosX: function(el) {
2293             return Roo.lib.Dom.getX(el);
2294         },
2295
2296         /**
2297          * Returns the Y position of an html element
2298          * @method getPosY
2299          * @param el the element for which to get the position
2300          * @return {int} the Y coordinate
2301          * @deprecated use Roo.lib.Dom.getY instead
2302          * @static
2303          */
2304         getPosY: function(el) {
2305             return Roo.lib.Dom.getY(el);
2306         },
2307
2308         /**
2309          * Swap two nodes.  In IE, we use the native method, for others we
2310          * emulate the IE behavior
2311          * @method swapNode
2312          * @param n1 the first node to swap
2313          * @param n2 the other node to swap
2314          * @static
2315          */
2316         swapNode: function(n1, n2) {
2317             if (n1.swapNode) {
2318                 n1.swapNode(n2);
2319             } else {
2320                 var p = n2.parentNode;
2321                 var s = n2.nextSibling;
2322
2323                 if (s == n1) {
2324                     p.insertBefore(n1, n2);
2325                 } else if (n2 == n1.nextSibling) {
2326                     p.insertBefore(n2, n1);
2327                 } else {
2328                     n1.parentNode.replaceChild(n2, n1);
2329                     p.insertBefore(n1, s);
2330                 }
2331             }
2332         },
2333
2334         /**
2335          * Returns the current scroll position
2336          * @method getScroll
2337          * @private
2338          * @static
2339          */
2340         getScroll: function () {
2341             var t, l, dde=document.documentElement, db=document.body;
2342             if (dde && (dde.scrollTop || dde.scrollLeft)) {
2343                 t = dde.scrollTop;
2344                 l = dde.scrollLeft;
2345             } else if (db) {
2346                 t = db.scrollTop;
2347                 l = db.scrollLeft;
2348             } else {
2349
2350             }
2351             return { top: t, left: l };
2352         },
2353
2354         /**
2355          * Returns the specified element style property
2356          * @method getStyle
2357          * @param {HTMLElement} el          the element
2358          * @param {string}      styleProp   the style property
2359          * @return {string} The value of the style property
2360          * @deprecated use Roo.lib.Dom.getStyle
2361          * @static
2362          */
2363         getStyle: function(el, styleProp) {
2364             return Roo.fly(el).getStyle(styleProp);
2365         },
2366
2367         /**
2368          * Gets the scrollTop
2369          * @method getScrollTop
2370          * @return {int} the document's scrollTop
2371          * @static
2372          */
2373         getScrollTop: function () { return this.getScroll().top; },
2374
2375         /**
2376          * Gets the scrollLeft
2377          * @method getScrollLeft
2378          * @return {int} the document's scrollTop
2379          * @static
2380          */
2381         getScrollLeft: function () { return this.getScroll().left; },
2382
2383         /**
2384          * Sets the x/y position of an element to the location of the
2385          * target element.
2386          * @method moveToEl
2387          * @param {HTMLElement} moveEl      The element to move
2388          * @param {HTMLElement} targetEl    The position reference element
2389          * @static
2390          */
2391         moveToEl: function (moveEl, targetEl) {
2392             var aCoord = Roo.lib.Dom.getXY(targetEl);
2393             Roo.lib.Dom.setXY(moveEl, aCoord);
2394         },
2395
2396         /**
2397          * Numeric array sort function
2398          * @method numericSort
2399          * @static
2400          */
2401         numericSort: function(a, b) { return (a - b); },
2402
2403         /**
2404          * Internal counter
2405          * @property _timeoutCount
2406          * @private
2407          * @static
2408          */
2409         _timeoutCount: 0,
2410
2411         /**
2412          * Trying to make the load order less important.  Without this we get
2413          * an error if this file is loaded before the Event Utility.
2414          * @method _addListeners
2415          * @private
2416          * @static
2417          */
2418         _addListeners: function() {
2419             var DDM = Roo.dd.DDM;
2420             if ( Roo.lib.Event && document ) {
2421                 DDM._onLoad();
2422             } else {
2423                 if (DDM._timeoutCount > 2000) {
2424                 } else {
2425                     setTimeout(DDM._addListeners, 10);
2426                     if (document && document.body) {
2427                         DDM._timeoutCount += 1;
2428                     }
2429                 }
2430             }
2431         },
2432
2433         /**
2434          * Recursively searches the immediate parent and all child nodes for
2435          * the handle element in order to determine wheter or not it was
2436          * clicked.
2437          * @method handleWasClicked
2438          * @param node the html element to inspect
2439          * @static
2440          */
2441         handleWasClicked: function(node, id) {
2442             if (this.isHandle(id, node.id)) {
2443                 return true;
2444             } else {
2445                 // check to see if this is a text node child of the one we want
2446                 var p = node.parentNode;
2447
2448                 while (p) {
2449                     if (this.isHandle(id, p.id)) {
2450                         return true;
2451                     } else {
2452                         p = p.parentNode;
2453                     }
2454                 }
2455             }
2456
2457             return false;
2458         }
2459
2460     };
2461
2462 }();
2463
2464 // shorter alias, save a few bytes
2465 Roo.dd.DDM = Roo.dd.DragDropMgr;
2466 Roo.dd.DDM._addListeners();
2467
2468 }/*
2469  * Based on:
2470  * Ext JS Library 1.1.1
2471  * Copyright(c) 2006-2007, Ext JS, LLC.
2472  *
2473  * Originally Released Under LGPL - original licence link has changed is not relivant.
2474  *
2475  * Fork - LGPL
2476  * <script type="text/javascript">
2477  */
2478
2479 /**
2480  * @class Roo.dd.DD
2481  * A DragDrop implementation where the linked element follows the
2482  * mouse cursor during a drag.
2483  * @extends Roo.dd.DragDrop
2484  * @constructor
2485  * @param {String} id the id of the linked element
2486  * @param {String} sGroup the group of related DragDrop items
2487  * @param {object} config an object containing configurable attributes
2488  *                Valid properties for DD:
2489  *                    scroll
2490  */
2491 Roo.dd.DD = function(id, sGroup, config) {
2492     if (id) {
2493         this.init(id, sGroup, config);
2494     }
2495 };
2496
2497 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
2498
2499     /**
2500      * When set to true, the utility automatically tries to scroll the browser
2501      * window wehn a drag and drop element is dragged near the viewport boundary.
2502      * Defaults to true.
2503      * @property scroll
2504      * @type boolean
2505      */
2506     scroll: true,
2507
2508     /**
2509      * Sets the pointer offset to the distance between the linked element's top
2510      * left corner and the location the element was clicked
2511      * @method autoOffset
2512      * @param {int} iPageX the X coordinate of the click
2513      * @param {int} iPageY the Y coordinate of the click
2514      */
2515     autoOffset: function(iPageX, iPageY) {
2516         var x = iPageX - this.startPageX;
2517         var y = iPageY - this.startPageY;
2518         this.setDelta(x, y);
2519     },
2520
2521     /**
2522      * Sets the pointer offset.  You can call this directly to force the
2523      * offset to be in a particular location (e.g., pass in 0,0 to set it
2524      * to the center of the object)
2525      * @method setDelta
2526      * @param {int} iDeltaX the distance from the left
2527      * @param {int} iDeltaY the distance from the top
2528      */
2529     setDelta: function(iDeltaX, iDeltaY) {
2530         this.deltaX = iDeltaX;
2531         this.deltaY = iDeltaY;
2532     },
2533
2534     /**
2535      * Sets the drag element to the location of the mousedown or click event,
2536      * maintaining the cursor location relative to the location on the element
2537      * that was clicked.  Override this if you want to place the element in a
2538      * location other than where the cursor is.
2539      * @method setDragElPos
2540      * @param {int} iPageX the X coordinate of the mousedown or drag event
2541      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2542      */
2543     setDragElPos: function(iPageX, iPageY) {
2544         // the first time we do this, we are going to check to make sure
2545         // the element has css positioning
2546
2547         var el = this.getDragEl();
2548         this.alignElWithMouse(el, iPageX, iPageY);
2549     },
2550
2551     /**
2552      * Sets the element to the location of the mousedown or click event,
2553      * maintaining the cursor location relative to the location on the element
2554      * that was clicked.  Override this if you want to place the element in a
2555      * location other than where the cursor is.
2556      * @method alignElWithMouse
2557      * @param {HTMLElement} el the element to move
2558      * @param {int} iPageX the X coordinate of the mousedown or drag event
2559      * @param {int} iPageY the Y coordinate of the mousedown or drag event
2560      */
2561     alignElWithMouse: function(el, iPageX, iPageY) {
2562         var oCoord = this.getTargetCoord(iPageX, iPageY);
2563         var fly = el.dom ? el : Roo.fly(el);
2564         if (!this.deltaSetXY) {
2565             var aCoord = [oCoord.x, oCoord.y];
2566             fly.setXY(aCoord);
2567             var newLeft = fly.getLeft(true);
2568             var newTop  = fly.getTop(true);
2569             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
2570         } else {
2571             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
2572         }
2573
2574         this.cachePosition(oCoord.x, oCoord.y);
2575         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
2576         return oCoord;
2577     },
2578
2579     /**
2580      * Saves the most recent position so that we can reset the constraints and
2581      * tick marks on-demand.  We need to know this so that we can calculate the
2582      * number of pixels the element is offset from its original position.
2583      * @method cachePosition
2584      * @param iPageX the current x position (optional, this just makes it so we
2585      * don't have to look it up again)
2586      * @param iPageY the current y position (optional, this just makes it so we
2587      * don't have to look it up again)
2588      */
2589     cachePosition: function(iPageX, iPageY) {
2590         if (iPageX) {
2591             this.lastPageX = iPageX;
2592             this.lastPageY = iPageY;
2593         } else {
2594             var aCoord = Roo.lib.Dom.getXY(this.getEl());
2595             this.lastPageX = aCoord[0];
2596             this.lastPageY = aCoord[1];
2597         }
2598     },
2599
2600     /**
2601      * Auto-scroll the window if the dragged object has been moved beyond the
2602      * visible window boundary.
2603      * @method autoScroll
2604      * @param {int} x the drag element's x position
2605      * @param {int} y the drag element's y position
2606      * @param {int} h the height of the drag element
2607      * @param {int} w the width of the drag element
2608      * @private
2609      */
2610     autoScroll: function(x, y, h, w) {
2611
2612         if (this.scroll) {
2613             // The client height
2614             var clientH = Roo.lib.Dom.getViewWidth();
2615
2616             // The client width
2617             var clientW = Roo.lib.Dom.getViewHeight();
2618
2619             // The amt scrolled down
2620             var st = this.DDM.getScrollTop();
2621
2622             // The amt scrolled right
2623             var sl = this.DDM.getScrollLeft();
2624
2625             // Location of the bottom of the element
2626             var bot = h + y;
2627
2628             // Location of the right of the element
2629             var right = w + x;
2630
2631             // The distance from the cursor to the bottom of the visible area,
2632             // adjusted so that we don't scroll if the cursor is beyond the
2633             // element drag constraints
2634             var toBot = (clientH + st - y - this.deltaY);
2635
2636             // The distance from the cursor to the right of the visible area
2637             var toRight = (clientW + sl - x - this.deltaX);
2638
2639
2640             // How close to the edge the cursor must be before we scroll
2641             // var thresh = (document.all) ? 100 : 40;
2642             var thresh = 40;
2643
2644             // How many pixels to scroll per autoscroll op.  This helps to reduce
2645             // clunky scrolling. IE is more sensitive about this ... it needs this
2646             // value to be higher.
2647             var scrAmt = (document.all) ? 80 : 30;
2648
2649             // Scroll down if we are near the bottom of the visible page and the
2650             // obj extends below the crease
2651             if ( bot > clientH && toBot < thresh ) {
2652                 window.scrollTo(sl, st + scrAmt);
2653             }
2654
2655             // Scroll up if the window is scrolled down and the top of the object
2656             // goes above the top border
2657             if ( y < st && st > 0 && y - st < thresh ) {
2658                 window.scrollTo(sl, st - scrAmt);
2659             }
2660
2661             // Scroll right if the obj is beyond the right border and the cursor is
2662             // near the border.
2663             if ( right > clientW && toRight < thresh ) {
2664                 window.scrollTo(sl + scrAmt, st);
2665             }
2666
2667             // Scroll left if the window has been scrolled to the right and the obj
2668             // extends past the left border
2669             if ( x < sl && sl > 0 && x - sl < thresh ) {
2670                 window.scrollTo(sl - scrAmt, st);
2671             }
2672         }
2673     },
2674
2675     /**
2676      * Finds the location the element should be placed if we want to move
2677      * it to where the mouse location less the click offset would place us.
2678      * @method getTargetCoord
2679      * @param {int} iPageX the X coordinate of the click
2680      * @param {int} iPageY the Y coordinate of the click
2681      * @return an object that contains the coordinates (Object.x and Object.y)
2682      * @private
2683      */
2684     getTargetCoord: function(iPageX, iPageY) {
2685
2686
2687         var x = iPageX - this.deltaX;
2688         var y = iPageY - this.deltaY;
2689
2690         if (this.constrainX) {
2691             if (x < this.minX) { x = this.minX; }
2692             if (x > this.maxX) { x = this.maxX; }
2693         }
2694
2695         if (this.constrainY) {
2696             if (y < this.minY) { y = this.minY; }
2697             if (y > this.maxY) { y = this.maxY; }
2698         }
2699
2700         x = this.getTick(x, this.xTicks);
2701         y = this.getTick(y, this.yTicks);
2702
2703
2704         return {x:x, y:y};
2705     },
2706
2707     /*
2708      * Sets up config options specific to this class. Overrides
2709      * Roo.dd.DragDrop, but all versions of this method through the
2710      * inheritance chain are called
2711      */
2712     applyConfig: function() {
2713         Roo.dd.DD.superclass.applyConfig.call(this);
2714         this.scroll = (this.config.scroll !== false);
2715     },
2716
2717     /*
2718      * Event that fires prior to the onMouseDown event.  Overrides
2719      * Roo.dd.DragDrop.
2720      */
2721     b4MouseDown: function(e) {
2722         // this.resetConstraints();
2723         this.autoOffset(e.getPageX(),
2724                             e.getPageY());
2725     },
2726
2727     /*
2728      * Event that fires prior to the onDrag event.  Overrides
2729      * Roo.dd.DragDrop.
2730      */
2731     b4Drag: function(e) {
2732         this.setDragElPos(e.getPageX(),
2733                             e.getPageY());
2734     },
2735
2736     toString: function() {
2737         return ("DD " + this.id);
2738     }
2739
2740     //////////////////////////////////////////////////////////////////////////
2741     // Debugging ygDragDrop events that can be overridden
2742     //////////////////////////////////////////////////////////////////////////
2743     /*
2744     startDrag: function(x, y) {
2745     },
2746
2747     onDrag: function(e) {
2748     },
2749
2750     onDragEnter: function(e, id) {
2751     },
2752
2753     onDragOver: function(e, id) {
2754     },
2755
2756     onDragOut: function(e, id) {
2757     },
2758
2759     onDragDrop: function(e, id) {
2760     },
2761
2762     endDrag: function(e) {
2763     }
2764
2765     */
2766
2767 });/*
2768  * Based on:
2769  * Ext JS Library 1.1.1
2770  * Copyright(c) 2006-2007, Ext JS, LLC.
2771  *
2772  * Originally Released Under LGPL - original licence link has changed is not relivant.
2773  *
2774  * Fork - LGPL
2775  * <script type="text/javascript">
2776  */
2777
2778 /**
2779  * @class Roo.dd.DDProxy
2780  * A DragDrop implementation that inserts an empty, bordered div into
2781  * the document that follows the cursor during drag operations.  At the time of
2782  * the click, the frame div is resized to the dimensions of the linked html
2783  * element, and moved to the exact location of the linked element.
2784  *
2785  * References to the "frame" element refer to the single proxy element that
2786  * was created to be dragged in place of all DDProxy elements on the
2787  * page.
2788  *
2789  * @extends Roo.dd.DD
2790  * @constructor
2791  * @param {String} id the id of the linked html element
2792  * @param {String} sGroup the group of related DragDrop objects
2793  * @param {object} config an object containing configurable attributes
2794  *                Valid properties for DDProxy in addition to those in DragDrop:
2795  *                   resizeFrame, centerFrame, dragElId
2796  */
2797 Roo.dd.DDProxy = function(id, sGroup, config) {
2798     if (id) {
2799         this.init(id, sGroup, config);
2800         this.initFrame();
2801     }
2802 };
2803
2804 /**
2805  * The default drag frame div id
2806  * @property Roo.dd.DDProxy.dragElId
2807  * @type String
2808  * @static
2809  */
2810 Roo.dd.DDProxy.dragElId = "ygddfdiv";
2811
2812 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
2813
2814     /**
2815      * By default we resize the drag frame to be the same size as the element
2816      * we want to drag (this is to get the frame effect).  We can turn it off
2817      * if we want a different behavior.
2818      * @property resizeFrame
2819      * @type boolean
2820      */
2821     resizeFrame: true,
2822
2823     /**
2824      * By default the frame is positioned exactly where the drag element is, so
2825      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
2826      * you do not have constraints on the obj is to have the drag frame centered
2827      * around the cursor.  Set centerFrame to true for this effect.
2828      * @property centerFrame
2829      * @type boolean
2830      */
2831     centerFrame: false,
2832
2833     /**
2834      * Creates the proxy element if it does not yet exist
2835      * @method createFrame
2836      */
2837     createFrame: function() {
2838         var self = this;
2839         var body = document.body;
2840
2841         if (!body || !body.firstChild) {
2842             setTimeout( function() { self.createFrame(); }, 50 );
2843             return;
2844         }
2845
2846         var div = this.getDragEl();
2847
2848         if (!div) {
2849             div    = document.createElement("div");
2850             div.id = this.dragElId;
2851             var s  = div.style;
2852
2853             s.position   = "absolute";
2854             s.visibility = "hidden";
2855             s.cursor     = "move";
2856             s.border     = "2px solid #aaa";
2857             s.zIndex     = 999;
2858
2859             // appendChild can blow up IE if invoked prior to the window load event
2860             // while rendering a table.  It is possible there are other scenarios
2861             // that would cause this to happen as well.
2862             body.insertBefore(div, body.firstChild);
2863         }
2864     },
2865
2866     /**
2867      * Initialization for the drag frame element.  Must be called in the
2868      * constructor of all subclasses
2869      * @method initFrame
2870      */
2871     initFrame: function() {
2872         this.createFrame();
2873     },
2874
2875     applyConfig: function() {
2876         Roo.dd.DDProxy.superclass.applyConfig.call(this);
2877
2878         this.resizeFrame = (this.config.resizeFrame !== false);
2879         this.centerFrame = (this.config.centerFrame);
2880         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
2881     },
2882
2883     /**
2884      * Resizes the drag frame to the dimensions of the clicked object, positions
2885      * it over the object, and finally displays it
2886      * @method showFrame
2887      * @param {int} iPageX X click position
2888      * @param {int} iPageY Y click position
2889      * @private
2890      */
2891     showFrame: function(iPageX, iPageY) {
2892         var el = this.getEl();
2893         var dragEl = this.getDragEl();
2894         var s = dragEl.style;
2895
2896         this._resizeProxy();
2897
2898         if (this.centerFrame) {
2899             this.setDelta( Math.round(parseInt(s.width,  10)/2),
2900                            Math.round(parseInt(s.height, 10)/2) );
2901         }
2902
2903         this.setDragElPos(iPageX, iPageY);
2904
2905         Roo.fly(dragEl).show();
2906     },
2907
2908     /**
2909      * The proxy is automatically resized to the dimensions of the linked
2910      * element when a drag is initiated, unless resizeFrame is set to false
2911      * @method _resizeProxy
2912      * @private
2913      */
2914     _resizeProxy: function() {
2915         if (this.resizeFrame) {
2916             var el = this.getEl();
2917             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
2918         }
2919     },
2920
2921     // overrides Roo.dd.DragDrop
2922     b4MouseDown: function(e) {
2923         var x = e.getPageX();
2924         var y = e.getPageY();
2925         this.autoOffset(x, y);
2926         this.setDragElPos(x, y);
2927     },
2928
2929     // overrides Roo.dd.DragDrop
2930     b4StartDrag: function(x, y) {
2931         // show the drag frame
2932         this.showFrame(x, y);
2933     },
2934
2935     // overrides Roo.dd.DragDrop
2936     b4EndDrag: function(e) {
2937         Roo.fly(this.getDragEl()).hide();
2938     },
2939
2940     // overrides Roo.dd.DragDrop
2941     // By default we try to move the element to the last location of the frame.
2942     // This is so that the default behavior mirrors that of Roo.dd.DD.
2943     endDrag: function(e) {
2944
2945         var lel = this.getEl();
2946         var del = this.getDragEl();
2947
2948         // Show the drag frame briefly so we can get its position
2949         del.style.visibility = "";
2950
2951         this.beforeMove();
2952         // Hide the linked element before the move to get around a Safari
2953         // rendering bug.
2954         lel.style.visibility = "hidden";
2955         Roo.dd.DDM.moveToEl(lel, del);
2956         del.style.visibility = "hidden";
2957         lel.style.visibility = "";
2958
2959         this.afterDrag();
2960     },
2961
2962     beforeMove : function(){
2963
2964     },
2965
2966     afterDrag : function(){
2967
2968     },
2969
2970     toString: function() {
2971         return ("DDProxy " + this.id);
2972     }
2973
2974 });
2975 /*
2976  * Based on:
2977  * Ext JS Library 1.1.1
2978  * Copyright(c) 2006-2007, Ext JS, LLC.
2979  *
2980  * Originally Released Under LGPL - original licence link has changed is not relivant.
2981  *
2982  * Fork - LGPL
2983  * <script type="text/javascript">
2984  */
2985
2986  /**
2987  * @class Roo.dd.DDTarget
2988  * A DragDrop implementation that does not move, but can be a drop
2989  * target.  You would get the same result by simply omitting implementation
2990  * for the event callbacks, but this way we reduce the processing cost of the
2991  * event listener and the callbacks.
2992  * @extends Roo.dd.DragDrop
2993  * @constructor
2994  * @param {String} id the id of the element that is a drop target
2995  * @param {String} sGroup the group of related DragDrop objects
2996  * @param {object} config an object containing configurable attributes
2997  *                 Valid properties for DDTarget in addition to those in
2998  *                 DragDrop:
2999  *                    none
3000  */
3001 Roo.dd.DDTarget = function(id, sGroup, config) {
3002     if (id) {
3003         this.initTarget(id, sGroup, config);
3004     }
3005     if (config.listeners || config.events) { 
3006        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
3007             listeners : config.listeners || {}, 
3008             events : config.events || {} 
3009         });    
3010     }
3011 };
3012
3013 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
3014 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
3015     toString: function() {
3016         return ("DDTarget " + this.id);
3017     }
3018 });
3019 /*
3020  * Based on:
3021  * Ext JS Library 1.1.1
3022  * Copyright(c) 2006-2007, Ext JS, LLC.
3023  *
3024  * Originally Released Under LGPL - original licence link has changed is not relivant.
3025  *
3026  * Fork - LGPL
3027  * <script type="text/javascript">
3028  */
3029  
3030
3031 /**
3032  * @class Roo.dd.ScrollManager
3033  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
3034  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
3035  * @singleton
3036  */
3037 Roo.dd.ScrollManager = function(){
3038     var ddm = Roo.dd.DragDropMgr;
3039     var els = {};
3040     var dragEl = null;
3041     var proc = {};
3042     
3043     var onStop = function(e){
3044         dragEl = null;
3045         clearProc();
3046     };
3047     
3048     var triggerRefresh = function(){
3049         if(ddm.dragCurrent){
3050              ddm.refreshCache(ddm.dragCurrent.groups);
3051         }
3052     };
3053     
3054     var doScroll = function(){
3055         if(ddm.dragCurrent){
3056             var dds = Roo.dd.ScrollManager;
3057             if(!dds.animate){
3058                 if(proc.el.scroll(proc.dir, dds.increment)){
3059                     triggerRefresh();
3060                 }
3061             }else{
3062                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3063             }
3064         }
3065     };
3066     
3067     var clearProc = function(){
3068         if(proc.id){
3069             clearInterval(proc.id);
3070         }
3071         proc.id = 0;
3072         proc.el = null;
3073         proc.dir = "";
3074     };
3075     
3076     var startProc = function(el, dir){
3077         clearProc();
3078         proc.el = el;
3079         proc.dir = dir;
3080         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3081     };
3082     
3083     var onFire = function(e, isDrop){
3084         if(isDrop || !ddm.dragCurrent){ return; }
3085         var dds = Roo.dd.ScrollManager;
3086         if(!dragEl || dragEl != ddm.dragCurrent){
3087             dragEl = ddm.dragCurrent;
3088             // refresh regions on drag start
3089             dds.refreshCache();
3090         }
3091         
3092         var xy = Roo.lib.Event.getXY(e);
3093         var pt = new Roo.lib.Point(xy[0], xy[1]);
3094         for(var id in els){
3095             var el = els[id], r = el._region;
3096             if(r && r.contains(pt) && el.isScrollable()){
3097                 if(r.bottom - pt.y <= dds.thresh){
3098                     if(proc.el != el){
3099                         startProc(el, "down");
3100                     }
3101                     return;
3102                 }else if(r.right - pt.x <= dds.thresh){
3103                     if(proc.el != el){
3104                         startProc(el, "left");
3105                     }
3106                     return;
3107                 }else if(pt.y - r.top <= dds.thresh){
3108                     if(proc.el != el){
3109                         startProc(el, "up");
3110                     }
3111                     return;
3112                 }else if(pt.x - r.left <= dds.thresh){
3113                     if(proc.el != el){
3114                         startProc(el, "right");
3115                     }
3116                     return;
3117                 }
3118             }
3119         }
3120         clearProc();
3121     };
3122     
3123     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3124     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3125     
3126     return {
3127         /**
3128          * Registers new overflow element(s) to auto scroll
3129          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3130          */
3131         register : function(el){
3132             if(el instanceof Array){
3133                 for(var i = 0, len = el.length; i < len; i++) {
3134                         this.register(el[i]);
3135                 }
3136             }else{
3137                 el = Roo.get(el);
3138                 els[el.id] = el;
3139             }
3140         },
3141         
3142         /**
3143          * Unregisters overflow element(s) so they are no longer scrolled
3144          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3145          */
3146         unregister : function(el){
3147             if(el instanceof Array){
3148                 for(var i = 0, len = el.length; i < len; i++) {
3149                         this.unregister(el[i]);
3150                 }
3151             }else{
3152                 el = Roo.get(el);
3153                 delete els[el.id];
3154             }
3155         },
3156         
3157         /**
3158          * The number of pixels from the edge of a container the pointer needs to be to 
3159          * trigger scrolling (defaults to 25)
3160          * @type Number
3161          */
3162         thresh : 25,
3163         
3164         /**
3165          * The number of pixels to scroll in each scroll increment (defaults to 50)
3166          * @type Number
3167          */
3168         increment : 100,
3169         
3170         /**
3171          * The frequency of scrolls in milliseconds (defaults to 500)
3172          * @type Number
3173          */
3174         frequency : 500,
3175         
3176         /**
3177          * True to animate the scroll (defaults to true)
3178          * @type Boolean
3179          */
3180         animate: true,
3181         
3182         /**
3183          * The animation duration in seconds - 
3184          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3185          * @type Number
3186          */
3187         animDuration: .4,
3188         
3189         /**
3190          * Manually trigger a cache refresh.
3191          */
3192         refreshCache : function(){
3193             for(var id in els){
3194                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3195                     els[id]._region = els[id].getRegion();
3196                 }
3197             }
3198         }
3199     };
3200 }();/*
3201  * Based on:
3202  * Ext JS Library 1.1.1
3203  * Copyright(c) 2006-2007, Ext JS, LLC.
3204  *
3205  * Originally Released Under LGPL - original licence link has changed is not relivant.
3206  *
3207  * Fork - LGPL
3208  * <script type="text/javascript">
3209  */
3210  
3211
3212 /**
3213  * @class Roo.dd.Registry
3214  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3215  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3216  * @singleton
3217  */
3218 Roo.dd.Registry = function(){
3219     var elements = {}; 
3220     var handles = {}; 
3221     var autoIdSeed = 0;
3222
3223     var getId = function(el, autogen){
3224         if(typeof el == "string"){
3225             return el;
3226         }
3227         var id = el.id;
3228         if(!id && autogen !== false){
3229             id = "roodd-" + (++autoIdSeed);
3230             el.id = id;
3231         }
3232         return id;
3233     };
3234     
3235     return {
3236     /**
3237      * Register a drag drop element
3238      * @param {String|HTMLElement} element The id or DOM node to register
3239      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3240      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3241      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3242      * populated in the data object (if applicable):
3243      * <pre>
3244 Value      Description<br />
3245 ---------  ------------------------------------------<br />
3246 handles    Array of DOM nodes that trigger dragging<br />
3247            for the element being registered<br />
3248 isHandle   True if the element passed in triggers<br />
3249            dragging itself, else false
3250 </pre>
3251      */
3252         register : function(el, data){
3253             data = data || {};
3254             if(typeof el == "string"){
3255                 el = document.getElementById(el);
3256             }
3257             data.ddel = el;
3258             elements[getId(el)] = data;
3259             if(data.isHandle !== false){
3260                 handles[data.ddel.id] = data;
3261             }
3262             if(data.handles){
3263                 var hs = data.handles;
3264                 for(var i = 0, len = hs.length; i < len; i++){
3265                         handles[getId(hs[i])] = data;
3266                 }
3267             }
3268         },
3269
3270     /**
3271      * Unregister a drag drop element
3272      * @param {String|HTMLElement}  element The id or DOM node to unregister
3273      */
3274         unregister : function(el){
3275             var id = getId(el, false);
3276             var data = elements[id];
3277             if(data){
3278                 delete elements[id];
3279                 if(data.handles){
3280                     var hs = data.handles;
3281                     for(var i = 0, len = hs.length; i < len; i++){
3282                         delete handles[getId(hs[i], false)];
3283                     }
3284                 }
3285             }
3286         },
3287
3288     /**
3289      * Returns the handle registered for a DOM Node by id
3290      * @param {String|HTMLElement} id The DOM node or id to look up
3291      * @return {Object} handle The custom handle data
3292      */
3293         getHandle : function(id){
3294             if(typeof id != "string"){ // must be element?
3295                 id = id.id;
3296             }
3297             return handles[id];
3298         },
3299
3300     /**
3301      * Returns the handle that is registered for the DOM node that is the target of the event
3302      * @param {Event} e The event
3303      * @return {Object} handle The custom handle data
3304      */
3305         getHandleFromEvent : function(e){
3306             var t = Roo.lib.Event.getTarget(e);
3307             return t ? handles[t.id] : null;
3308         },
3309
3310     /**
3311      * Returns a custom data object that is registered for a DOM node by id
3312      * @param {String|HTMLElement} id The DOM node or id to look up
3313      * @return {Object} data The custom data
3314      */
3315         getTarget : function(id){
3316             if(typeof id != "string"){ // must be element?
3317                 id = id.id;
3318             }
3319             return elements[id];
3320         },
3321
3322     /**
3323      * Returns a custom data object that is registered for the DOM node that is the target of the event
3324      * @param {Event} e The event
3325      * @return {Object} data The custom data
3326      */
3327         getTargetFromEvent : function(e){
3328             var t = Roo.lib.Event.getTarget(e);
3329             return t ? elements[t.id] || handles[t.id] : null;
3330         }
3331     };
3332 }();/*
3333  * Based on:
3334  * Ext JS Library 1.1.1
3335  * Copyright(c) 2006-2007, Ext JS, LLC.
3336  *
3337  * Originally Released Under LGPL - original licence link has changed is not relivant.
3338  *
3339  * Fork - LGPL
3340  * <script type="text/javascript">
3341  */
3342  
3343
3344 /**
3345  * @class Roo.dd.StatusProxy
3346  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3347  * default drag proxy used by all Roo.dd components.
3348  * @constructor
3349  * @param {Object} config
3350  */
3351 Roo.dd.StatusProxy = function(config){
3352     Roo.apply(this, config);
3353     this.id = this.id || Roo.id();
3354     this.el = new Roo.Layer({
3355         dh: {
3356             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3357                 {tag: "div", cls: "x-dd-drop-icon"},
3358                 {tag: "div", cls: "x-dd-drag-ghost"}
3359             ]
3360         }, 
3361         shadow: !config || config.shadow !== false
3362     });
3363     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3364     this.dropStatus = this.dropNotAllowed;
3365 };
3366
3367 Roo.dd.StatusProxy.prototype = {
3368     /**
3369      * @cfg {String} dropAllowed
3370      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3371      */
3372     dropAllowed : "x-dd-drop-ok",
3373     /**
3374      * @cfg {String} dropNotAllowed
3375      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3376      */
3377     dropNotAllowed : "x-dd-drop-nodrop",
3378
3379     /**
3380      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3381      * over the current target element.
3382      * @param {String} cssClass The css class for the new drop status indicator image
3383      */
3384     setStatus : function(cssClass){
3385         cssClass = cssClass || this.dropNotAllowed;
3386         if(this.dropStatus != cssClass){
3387             this.el.replaceClass(this.dropStatus, cssClass);
3388             this.dropStatus = cssClass;
3389         }
3390     },
3391
3392     /**
3393      * Resets the status indicator to the default dropNotAllowed value
3394      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3395      */
3396     reset : function(clearGhost){
3397         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3398         this.dropStatus = this.dropNotAllowed;
3399         if(clearGhost){
3400             this.ghost.update("");
3401         }
3402     },
3403
3404     /**
3405      * Updates the contents of the ghost element
3406      * @param {String} html The html that will replace the current innerHTML of the ghost element
3407      */
3408     update : function(html){
3409         if(typeof html == "string"){
3410             this.ghost.update(html);
3411         }else{
3412             this.ghost.update("");
3413             html.style.margin = "0";
3414             this.ghost.dom.appendChild(html);
3415         }
3416         // ensure float = none set?? cant remember why though.
3417         var el = this.ghost.dom.firstChild;
3418                 if(el){
3419                         Roo.fly(el).setStyle('float', 'none');
3420                 }
3421     },
3422     
3423     /**
3424      * Returns the underlying proxy {@link Roo.Layer}
3425      * @return {Roo.Layer} el
3426     */
3427     getEl : function(){
3428         return this.el;
3429     },
3430
3431     /**
3432      * Returns the ghost element
3433      * @return {Roo.Element} el
3434      */
3435     getGhost : function(){
3436         return this.ghost;
3437     },
3438
3439     /**
3440      * Hides the proxy
3441      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3442      */
3443     hide : function(clear){
3444         this.el.hide();
3445         if(clear){
3446             this.reset(true);
3447         }
3448     },
3449
3450     /**
3451      * Stops the repair animation if it's currently running
3452      */
3453     stop : function(){
3454         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3455             this.anim.stop();
3456         }
3457     },
3458
3459     /**
3460      * Displays this proxy
3461      */
3462     show : function(){
3463         this.el.show();
3464     },
3465
3466     /**
3467      * Force the Layer to sync its shadow and shim positions to the element
3468      */
3469     sync : function(){
3470         this.el.sync();
3471     },
3472
3473     /**
3474      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3475      * invalid drop operation by the item being dragged.
3476      * @param {Array} xy The XY position of the element ([x, y])
3477      * @param {Function} callback The function to call after the repair is complete
3478      * @param {Object} scope The scope in which to execute the callback
3479      */
3480     repair : function(xy, callback, scope){
3481         this.callback = callback;
3482         this.scope = scope;
3483         if(xy && this.animRepair !== false){
3484             this.el.addClass("x-dd-drag-repair");
3485             this.el.hideUnders(true);
3486             this.anim = this.el.shift({
3487                 duration: this.repairDuration || .5,
3488                 easing: 'easeOut',
3489                 xy: xy,
3490                 stopFx: true,
3491                 callback: this.afterRepair,
3492                 scope: this
3493             });
3494         }else{
3495             this.afterRepair();
3496         }
3497     },
3498
3499     // private
3500     afterRepair : function(){
3501         this.hide(true);
3502         if(typeof this.callback == "function"){
3503             this.callback.call(this.scope || this);
3504         }
3505         this.callback = null;
3506         this.scope = null;
3507     }
3508 };/*
3509  * Based on:
3510  * Ext JS Library 1.1.1
3511  * Copyright(c) 2006-2007, Ext JS, LLC.
3512  *
3513  * Originally Released Under LGPL - original licence link has changed is not relivant.
3514  *
3515  * Fork - LGPL
3516  * <script type="text/javascript">
3517  */
3518
3519 /**
3520  * @class Roo.dd.DragSource
3521  * @extends Roo.dd.DDProxy
3522  * A simple class that provides the basic implementation needed to make any element draggable.
3523  * @constructor
3524  * @param {String/HTMLElement/Element} el The container element
3525  * @param {Object} config
3526  */
3527 Roo.dd.DragSource = function(el, config){
3528     this.el = Roo.get(el);
3529     this.dragData = {};
3530     
3531     Roo.apply(this, config);
3532     
3533     if(!this.proxy){
3534         this.proxy = new Roo.dd.StatusProxy();
3535     }
3536
3537     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3538           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3539     
3540     this.dragging = false;
3541 };
3542
3543 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3544     /**
3545      * @cfg {String} dropAllowed
3546      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3547      */
3548     dropAllowed : "x-dd-drop-ok",
3549     /**
3550      * @cfg {String} dropNotAllowed
3551      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3552      */
3553     dropNotAllowed : "x-dd-drop-nodrop",
3554
3555     /**
3556      * Returns the data object associated with this drag source
3557      * @return {Object} data An object containing arbitrary data
3558      */
3559     getDragData : function(e){
3560         return this.dragData;
3561     },
3562
3563     // private
3564     onDragEnter : function(e, id){
3565         var target = Roo.dd.DragDropMgr.getDDById(id);
3566         this.cachedTarget = target;
3567         if(this.beforeDragEnter(target, e, id) !== false){
3568             if(target.isNotifyTarget){
3569                 var status = target.notifyEnter(this, e, this.dragData);
3570                 this.proxy.setStatus(status);
3571             }else{
3572                 this.proxy.setStatus(this.dropAllowed);
3573             }
3574             
3575             if(this.afterDragEnter){
3576                 /**
3577                  * An empty function by default, but provided so that you can perform a custom action
3578                  * when the dragged item enters the drop target by providing an implementation.
3579                  * @param {Roo.dd.DragDrop} target The drop target
3580                  * @param {Event} e The event object
3581                  * @param {String} id The id of the dragged element
3582                  * @method afterDragEnter
3583                  */
3584                 this.afterDragEnter(target, e, id);
3585             }
3586         }
3587     },
3588
3589     /**
3590      * An empty function by default, but provided so that you can perform a custom action
3591      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3592      * @param {Roo.dd.DragDrop} target The drop target
3593      * @param {Event} e The event object
3594      * @param {String} id The id of the dragged element
3595      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3596      */
3597     beforeDragEnter : function(target, e, id){
3598         return true;
3599     },
3600
3601     // private
3602     alignElWithMouse: function() {
3603         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3604         this.proxy.sync();
3605     },
3606
3607     // private
3608     onDragOver : function(e, id){
3609         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3610         if(this.beforeDragOver(target, e, id) !== false){
3611             if(target.isNotifyTarget){
3612                 var status = target.notifyOver(this, e, this.dragData);
3613                 this.proxy.setStatus(status);
3614             }
3615
3616             if(this.afterDragOver){
3617                 /**
3618                  * An empty function by default, but provided so that you can perform a custom action
3619                  * while the dragged item is over the drop target by providing an implementation.
3620                  * @param {Roo.dd.DragDrop} target The drop target
3621                  * @param {Event} e The event object
3622                  * @param {String} id The id of the dragged element
3623                  * @method afterDragOver
3624                  */
3625                 this.afterDragOver(target, e, id);
3626             }
3627         }
3628     },
3629
3630     /**
3631      * An empty function by default, but provided so that you can perform a custom action
3632      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3633      * @param {Roo.dd.DragDrop} target The drop target
3634      * @param {Event} e The event object
3635      * @param {String} id The id of the dragged element
3636      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3637      */
3638     beforeDragOver : function(target, e, id){
3639         return true;
3640     },
3641
3642     // private
3643     onDragOut : function(e, id){
3644         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3645         if(this.beforeDragOut(target, e, id) !== false){
3646             if(target.isNotifyTarget){
3647                 target.notifyOut(this, e, this.dragData);
3648             }
3649             this.proxy.reset();
3650             if(this.afterDragOut){
3651                 /**
3652                  * An empty function by default, but provided so that you can perform a custom action
3653                  * after the dragged item is dragged out of the target without dropping.
3654                  * @param {Roo.dd.DragDrop} target The drop target
3655                  * @param {Event} e The event object
3656                  * @param {String} id The id of the dragged element
3657                  * @method afterDragOut
3658                  */
3659                 this.afterDragOut(target, e, id);
3660             }
3661         }
3662         this.cachedTarget = null;
3663     },
3664
3665     /**
3666      * An empty function by default, but provided so that you can perform a custom action before the dragged
3667      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3668      * @param {Roo.dd.DragDrop} target The drop target
3669      * @param {Event} e The event object
3670      * @param {String} id The id of the dragged element
3671      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3672      */
3673     beforeDragOut : function(target, e, id){
3674         return true;
3675     },
3676     
3677     // private
3678     onDragDrop : function(e, id){
3679         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3680         if(this.beforeDragDrop(target, e, id) !== false){
3681             if(target.isNotifyTarget){
3682                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3683                     this.onValidDrop(target, e, id);
3684                 }else{
3685                     this.onInvalidDrop(target, e, id);
3686                 }
3687             }else{
3688                 this.onValidDrop(target, e, id);
3689             }
3690             
3691             if(this.afterDragDrop){
3692                 /**
3693                  * An empty function by default, but provided so that you can perform a custom action
3694                  * after a valid drag drop has occurred by providing an implementation.
3695                  * @param {Roo.dd.DragDrop} target The drop target
3696                  * @param {Event} e The event object
3697                  * @param {String} id The id of the dropped element
3698                  * @method afterDragDrop
3699                  */
3700                 this.afterDragDrop(target, e, id);
3701             }
3702         }
3703         delete this.cachedTarget;
3704     },
3705
3706     /**
3707      * An empty function by default, but provided so that you can perform a custom action before the dragged
3708      * item is dropped onto the target and optionally cancel the onDragDrop.
3709      * @param {Roo.dd.DragDrop} target The drop target
3710      * @param {Event} e The event object
3711      * @param {String} id The id of the dragged element
3712      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3713      */
3714     beforeDragDrop : function(target, e, id){
3715         return true;
3716     },
3717
3718     // private
3719     onValidDrop : function(target, e, id){
3720         this.hideProxy();
3721         if(this.afterValidDrop){
3722             /**
3723              * An empty function by default, but provided so that you can perform a custom action
3724              * after a valid drop has occurred by providing an implementation.
3725              * @param {Object} target The target DD 
3726              * @param {Event} e The event object
3727              * @param {String} id The id of the dropped element
3728              * @method afterInvalidDrop
3729              */
3730             this.afterValidDrop(target, e, id);
3731         }
3732     },
3733
3734     // private
3735     getRepairXY : function(e, data){
3736         return this.el.getXY();  
3737     },
3738
3739     // private
3740     onInvalidDrop : function(target, e, id){
3741         this.beforeInvalidDrop(target, e, id);
3742         if(this.cachedTarget){
3743             if(this.cachedTarget.isNotifyTarget){
3744                 this.cachedTarget.notifyOut(this, e, this.dragData);
3745             }
3746             this.cacheTarget = null;
3747         }
3748         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3749
3750         if(this.afterInvalidDrop){
3751             /**
3752              * An empty function by default, but provided so that you can perform a custom action
3753              * after an invalid drop has occurred by providing an implementation.
3754              * @param {Event} e The event object
3755              * @param {String} id The id of the dropped element
3756              * @method afterInvalidDrop
3757              */
3758             this.afterInvalidDrop(e, id);
3759         }
3760     },
3761
3762     // private
3763     afterRepair : function(){
3764         if(Roo.enableFx){
3765             this.el.highlight(this.hlColor || "c3daf9");
3766         }
3767         this.dragging = false;
3768     },
3769
3770     /**
3771      * An empty function by default, but provided so that you can perform a custom action after an invalid
3772      * drop has occurred.
3773      * @param {Roo.dd.DragDrop} target The drop target
3774      * @param {Event} e The event object
3775      * @param {String} id The id of the dragged element
3776      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3777      */
3778     beforeInvalidDrop : function(target, e, id){
3779         return true;
3780     },
3781
3782     // private
3783     handleMouseDown : function(e){
3784         if(this.dragging) {
3785             return;
3786         }
3787         var data = this.getDragData(e);
3788         if(data && this.onBeforeDrag(data, e) !== false){
3789             this.dragData = data;
3790             this.proxy.stop();
3791             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3792         } 
3793     },
3794
3795     /**
3796      * An empty function by default, but provided so that you can perform a custom action before the initial
3797      * drag event begins and optionally cancel it.
3798      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3799      * @param {Event} e The event object
3800      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3801      */
3802     onBeforeDrag : function(data, e){
3803         return true;
3804     },
3805
3806     /**
3807      * An empty function by default, but provided so that you can perform a custom action once the initial
3808      * drag event has begun.  The drag cannot be canceled from this function.
3809      * @param {Number} x The x position of the click on the dragged object
3810      * @param {Number} y The y position of the click on the dragged object
3811      */
3812     onStartDrag : Roo.emptyFn,
3813
3814     // private - YUI override
3815     startDrag : function(x, y){
3816         this.proxy.reset();
3817         this.dragging = true;
3818         this.proxy.update("");
3819         this.onInitDrag(x, y);
3820         this.proxy.show();
3821     },
3822
3823     // private
3824     onInitDrag : function(x, y){
3825         var clone = this.el.dom.cloneNode(true);
3826         clone.id = Roo.id(); // prevent duplicate ids
3827         this.proxy.update(clone);
3828         this.onStartDrag(x, y);
3829         return true;
3830     },
3831
3832     /**
3833      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3834      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3835      */
3836     getProxy : function(){
3837         return this.proxy;  
3838     },
3839
3840     /**
3841      * Hides the drag source's {@link Roo.dd.StatusProxy}
3842      */
3843     hideProxy : function(){
3844         this.proxy.hide();  
3845         this.proxy.reset(true);
3846         this.dragging = false;
3847     },
3848
3849     // private
3850     triggerCacheRefresh : function(){
3851         Roo.dd.DDM.refreshCache(this.groups);
3852     },
3853
3854     // private - override to prevent hiding
3855     b4EndDrag: function(e) {
3856     },
3857
3858     // private - override to prevent moving
3859     endDrag : function(e){
3860         this.onEndDrag(this.dragData, e);
3861     },
3862
3863     // private
3864     onEndDrag : function(data, e){
3865     },
3866     
3867     // private - pin to cursor
3868     autoOffset : function(x, y) {
3869         this.setDelta(-12, -20);
3870     }    
3871 });/*
3872  * Based on:
3873  * Ext JS Library 1.1.1
3874  * Copyright(c) 2006-2007, Ext JS, LLC.
3875  *
3876  * Originally Released Under LGPL - original licence link has changed is not relivant.
3877  *
3878  * Fork - LGPL
3879  * <script type="text/javascript">
3880  */
3881
3882
3883 /**
3884  * @class Roo.dd.DropTarget
3885  * @extends Roo.dd.DDTarget
3886  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3887  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3888  * @constructor
3889  * @param {String/HTMLElement/Element} el The container element
3890  * @param {Object} config
3891  */
3892 Roo.dd.DropTarget = function(el, config){
3893     this.el = Roo.get(el);
3894     
3895     var listeners = false; ;
3896     if (config && config.listeners) {
3897         listeners= config.listeners;
3898         delete config.listeners;
3899     }
3900     Roo.apply(this, config);
3901     
3902     if(this.containerScroll){
3903         Roo.dd.ScrollManager.register(this.el);
3904     }
3905     this.addEvents( {
3906          /**
3907          * @scope Roo.dd.DropTarget
3908          */
3909          
3910          /**
3911          * @event enter
3912          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3913          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3914          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3915          * 
3916          * IMPORTANT : it should set this.overClass and this.dropAllowed
3917          * 
3918          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3919          * @param {Event} e The event
3920          * @param {Object} data An object containing arbitrary data supplied by the drag source
3921          */
3922         "enter" : true,
3923         
3924          /**
3925          * @event over
3926          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3927          * This method will be called on every mouse movement while the drag source is over the drop target.
3928          * This default implementation simply returns the dropAllowed config value.
3929          * 
3930          * IMPORTANT : it should set this.dropAllowed
3931          * 
3932          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3933          * @param {Event} e The event
3934          * @param {Object} data An object containing arbitrary data supplied by the drag source
3935          
3936          */
3937         "over" : true,
3938         /**
3939          * @event out
3940          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3941          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3942          * overClass (if any) from the drop element.
3943          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3944          * @param {Event} e The event
3945          * @param {Object} data An object containing arbitrary data supplied by the drag source
3946          */
3947          "out" : true,
3948          
3949         /**
3950          * @event drop
3951          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3952          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3953          * implementation that does something to process the drop event and returns true so that the drag source's
3954          * repair action does not run.
3955          * 
3956          * IMPORTANT : it should set this.success
3957          * 
3958          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3959          * @param {Event} e The event
3960          * @param {Object} data An object containing arbitrary data supplied by the drag source
3961         */
3962          "drop" : true
3963     });
3964             
3965      
3966     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3967         this.el.dom, 
3968         this.ddGroup || this.group,
3969         {
3970             isTarget: true,
3971             listeners : listeners || {} 
3972            
3973         
3974         }
3975     );
3976
3977 };
3978
3979 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3980     /**
3981      * @cfg {String} overClass
3982      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3983      */
3984      /**
3985      * @cfg {String} ddGroup
3986      * The drag drop group to handle drop events for
3987      */
3988      
3989     /**
3990      * @cfg {String} dropAllowed
3991      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3992      */
3993     dropAllowed : "x-dd-drop-ok",
3994     /**
3995      * @cfg {String} dropNotAllowed
3996      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3997      */
3998     dropNotAllowed : "x-dd-drop-nodrop",
3999     /**
4000      * @cfg {boolean} success
4001      * set this after drop listener.. 
4002      */
4003     success : false,
4004     /**
4005      * @cfg {boolean|String} valid true/false or string (add/sub/ok/nodrop)
4006      * if the drop point is valid for over/enter..
4007      */
4008     valid : false,
4009     // private
4010     isTarget : true,
4011
4012     // private
4013     isNotifyTarget : true,
4014     
4015     /**
4016      * @hide
4017      */
4018     notifyEnter : function(dd, e, data){
4019         this.valid = true;
4020         this.fireEvent('enter', dd, e, data);
4021         if(this.overClass){
4022             this.el.addClass(this.overClass);
4023         }
4024         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4025             this.valid ? this.dropAllowed : this.dropNotAllowed
4026         );
4027     },
4028
4029     /**
4030      * @hide
4031      */
4032     notifyOver : function(dd, e, data){
4033         this.valid = true;
4034         this.fireEvent('over', dd, e, data);
4035         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4036             this.valid ? this.dropAllowed : this.dropNotAllowed
4037         );
4038     },
4039
4040     /**
4041      * @hide
4042      */
4043     notifyOut : function(dd, e, data){
4044         this.fireEvent('out', dd, e, data);
4045         if(this.overClass){
4046             this.el.removeClass(this.overClass);
4047         }
4048     },
4049
4050     /**
4051      * @hide
4052      */
4053     notifyDrop : function(dd, e, data){
4054         this.success = false;
4055         this.fireEvent('drop', dd, e, data);
4056         return this.success;
4057     }
4058 });/*
4059  * Based on:
4060  * Ext JS Library 1.1.1
4061  * Copyright(c) 2006-2007, Ext JS, LLC.
4062  *
4063  * Originally Released Under LGPL - original licence link has changed is not relivant.
4064  *
4065  * Fork - LGPL
4066  * <script type="text/javascript">
4067  */
4068
4069
4070 /**
4071  * @class Roo.dd.DragZone
4072  * @extends Roo.dd.DragSource
4073  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4074  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4075  * @constructor
4076  * @param {String/HTMLElement/Element} el The container element
4077  * @param {Object} config
4078  */
4079 Roo.dd.DragZone = function(el, config){
4080     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4081     if(this.containerScroll){
4082         Roo.dd.ScrollManager.register(this.el);
4083     }
4084 };
4085
4086 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4087     /**
4088      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4089      * for auto scrolling during drag operations.
4090      */
4091     /**
4092      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4093      * method after a failed drop (defaults to "c3daf9" - light blue)
4094      */
4095
4096     /**
4097      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4098      * for a valid target to drag based on the mouse down. Override this method
4099      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4100      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4101      * @param {EventObject} e The mouse down event
4102      * @return {Object} The dragData
4103      */
4104     getDragData : function(e){
4105         return Roo.dd.Registry.getHandleFromEvent(e);
4106     },
4107     
4108     /**
4109      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4110      * this.dragData.ddel
4111      * @param {Number} x The x position of the click on the dragged object
4112      * @param {Number} y The y position of the click on the dragged object
4113      * @return {Boolean} true to continue the drag, false to cancel
4114      */
4115     onInitDrag : function(x, y){
4116         this.proxy.update(this.dragData.ddel.cloneNode(true));
4117         this.onStartDrag(x, y);
4118         return true;
4119     },
4120     
4121     /**
4122      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4123      */
4124     afterRepair : function(){
4125         if(Roo.enableFx){
4126             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4127         }
4128         this.dragging = false;
4129     },
4130
4131     /**
4132      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4133      * the XY of this.dragData.ddel
4134      * @param {EventObject} e The mouse up event
4135      * @return {Array} The xy location (e.g. [100, 200])
4136      */
4137     getRepairXY : function(e){
4138         return Roo.Element.fly(this.dragData.ddel).getXY();  
4139     }
4140 });/*
4141  * Based on:
4142  * Ext JS Library 1.1.1
4143  * Copyright(c) 2006-2007, Ext JS, LLC.
4144  *
4145  * Originally Released Under LGPL - original licence link has changed is not relivant.
4146  *
4147  * Fork - LGPL
4148  * <script type="text/javascript">
4149  */
4150 /**
4151  * @class Roo.dd.DropZone
4152  * @extends Roo.dd.DropTarget
4153  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4154  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4155  * @constructor
4156  * @param {String/HTMLElement/Element} el The container element
4157  * @param {Object} config
4158  */
4159 Roo.dd.DropZone = function(el, config){
4160     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4161 };
4162
4163 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4164     /**
4165      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4166      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4167      * provide your own custom lookup.
4168      * @param {Event} e The event
4169      * @return {Object} data The custom data
4170      */
4171     getTargetFromEvent : function(e){
4172         return Roo.dd.Registry.getTargetFromEvent(e);
4173     },
4174
4175     /**
4176      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4177      * that it has registered.  This method has no default implementation and should be overridden to provide
4178      * node-specific processing if necessary.
4179      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4180      * {@link #getTargetFromEvent} for this node)
4181      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4182      * @param {Event} e The event
4183      * @param {Object} data An object containing arbitrary data supplied by the drag source
4184      */
4185     onNodeEnter : function(n, dd, e, data){
4186         
4187     },
4188
4189     /**
4190      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4191      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4192      * overridden to provide the proper feedback.
4193      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4194      * {@link #getTargetFromEvent} for this node)
4195      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4196      * @param {Event} e The event
4197      * @param {Object} data An object containing arbitrary data supplied by the drag source
4198      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4199      * underlying {@link Roo.dd.StatusProxy} can be updated
4200      */
4201     onNodeOver : function(n, dd, e, data){
4202         return this.dropAllowed;
4203     },
4204
4205     /**
4206      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4207      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4208      * node-specific processing if necessary.
4209      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4210      * {@link #getTargetFromEvent} for this node)
4211      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4212      * @param {Event} e The event
4213      * @param {Object} data An object containing arbitrary data supplied by the drag source
4214      */
4215     onNodeOut : function(n, dd, e, data){
4216         
4217     },
4218
4219     /**
4220      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4221      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4222      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4223      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4224      * {@link #getTargetFromEvent} for this node)
4225      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4226      * @param {Event} e The event
4227      * @param {Object} data An object containing arbitrary data supplied by the drag source
4228      * @return {Boolean} True if the drop was valid, else false
4229      */
4230     onNodeDrop : function(n, dd, e, data){
4231         return false;
4232     },
4233
4234     /**
4235      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4236      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4237      * it should be overridden to provide the proper feedback if necessary.
4238      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4239      * @param {Event} e The event
4240      * @param {Object} data An object containing arbitrary data supplied by the drag source
4241      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4242      * underlying {@link Roo.dd.StatusProxy} can be updated
4243      */
4244     onContainerOver : function(dd, e, data){
4245         return this.dropNotAllowed;
4246     },
4247
4248     /**
4249      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4250      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4251      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4252      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4253      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4254      * @param {Event} e The event
4255      * @param {Object} data An object containing arbitrary data supplied by the drag source
4256      * @return {Boolean} True if the drop was valid, else false
4257      */
4258     onContainerDrop : function(dd, e, data){
4259         return false;
4260     },
4261
4262     /**
4263      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4264      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4265      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4266      * you should override this method and provide a custom implementation.
4267      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4268      * @param {Event} e The event
4269      * @param {Object} data An object containing arbitrary data supplied by the drag source
4270      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4271      * underlying {@link Roo.dd.StatusProxy} can be updated
4272      */
4273     notifyEnter : function(dd, e, data){
4274         return this.dropNotAllowed;
4275     },
4276
4277     /**
4278      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4279      * This method will be called on every mouse movement while the drag source is over the drop zone.
4280      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4281      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4282      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4283      * registered node, it will call {@link #onContainerOver}.
4284      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4285      * @param {Event} e The event
4286      * @param {Object} data An object containing arbitrary data supplied by the drag source
4287      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4288      * underlying {@link Roo.dd.StatusProxy} can be updated
4289      */
4290     notifyOver : function(dd, e, data){
4291         var n = this.getTargetFromEvent(e);
4292         if(!n){ // not over valid drop target
4293             if(this.lastOverNode){
4294                 this.onNodeOut(this.lastOverNode, dd, e, data);
4295                 this.lastOverNode = null;
4296             }
4297             return this.onContainerOver(dd, e, data);
4298         }
4299         if(this.lastOverNode != n){
4300             if(this.lastOverNode){
4301                 this.onNodeOut(this.lastOverNode, dd, e, data);
4302             }
4303             this.onNodeEnter(n, dd, e, data);
4304             this.lastOverNode = n;
4305         }
4306         return this.onNodeOver(n, dd, e, data);
4307     },
4308
4309     /**
4310      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4311      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4312      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4313      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4314      * @param {Event} e The event
4315      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4316      */
4317     notifyOut : function(dd, e, data){
4318         if(this.lastOverNode){
4319             this.onNodeOut(this.lastOverNode, dd, e, data);
4320             this.lastOverNode = null;
4321         }
4322     },
4323
4324     /**
4325      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4326      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4327      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4328      * otherwise it will call {@link #onContainerDrop}.
4329      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4330      * @param {Event} e The event
4331      * @param {Object} data An object containing arbitrary data supplied by the drag source
4332      * @return {Boolean} True if the drop was valid, else false
4333      */
4334     notifyDrop : function(dd, e, data){
4335         if(this.lastOverNode){
4336             this.onNodeOut(this.lastOverNode, dd, e, data);
4337             this.lastOverNode = null;
4338         }
4339         var n = this.getTargetFromEvent(e);
4340         return n ?
4341             this.onNodeDrop(n, dd, e, data) :
4342             this.onContainerDrop(dd, e, data);
4343     },
4344
4345     // private
4346     triggerCacheRefresh : function(){
4347         Roo.dd.DDM.refreshCache(this.groups);
4348     }  
4349 });/*
4350  * Based on:
4351  * Ext JS Library 1.1.1
4352  * Copyright(c) 2006-2007, Ext JS, LLC.
4353  *
4354  * Originally Released Under LGPL - original licence link has changed is not relivant.
4355  *
4356  * Fork - LGPL
4357  * <script type="text/javascript">
4358  */
4359
4360
4361 /**
4362  * @class Roo.data.SortTypes
4363  * @singleton
4364  * Defines the default sorting (casting?) comparison functions used when sorting data.
4365  */
4366 Roo.data.SortTypes = {
4367     /**
4368      * Default sort that does nothing
4369      * @param {Mixed} s The value being converted
4370      * @return {Mixed} The comparison value
4371      */
4372     none : function(s){
4373         return s;
4374     },
4375     
4376     /**
4377      * The regular expression used to strip tags
4378      * @type {RegExp}
4379      * @property
4380      */
4381     stripTagsRE : /<\/?[^>]+>/gi,
4382     
4383     /**
4384      * Strips all HTML tags to sort on text only
4385      * @param {Mixed} s The value being converted
4386      * @return {String} The comparison value
4387      */
4388     asText : function(s){
4389         return String(s).replace(this.stripTagsRE, "");
4390     },
4391     
4392     /**
4393      * Strips all HTML tags to sort on text only - Case insensitive
4394      * @param {Mixed} s The value being converted
4395      * @return {String} The comparison value
4396      */
4397     asUCText : function(s){
4398         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4399     },
4400     
4401     /**
4402      * Case insensitive string
4403      * @param {Mixed} s The value being converted
4404      * @return {String} The comparison value
4405      */
4406     asUCString : function(s) {
4407         return String(s).toUpperCase();
4408     },
4409     
4410     /**
4411      * Date sorting
4412      * @param {Mixed} s The value being converted
4413      * @return {Number} The comparison value
4414      */
4415     asDate : function(s) {
4416         if(!s){
4417             return 0;
4418         }
4419         if(s instanceof Date){
4420             return s.getTime();
4421         }
4422         return Date.parse(String(s));
4423     },
4424     
4425     /**
4426      * Float sorting
4427      * @param {Mixed} s The value being converted
4428      * @return {Float} The comparison value
4429      */
4430     asFloat : function(s) {
4431         var val = parseFloat(String(s).replace(/,/g, ""));
4432         if(isNaN(val)) val = 0;
4433         return val;
4434     },
4435     
4436     /**
4437      * Integer sorting
4438      * @param {Mixed} s The value being converted
4439      * @return {Number} The comparison value
4440      */
4441     asInt : function(s) {
4442         var val = parseInt(String(s).replace(/,/g, ""));
4443         if(isNaN(val)) val = 0;
4444         return val;
4445     }
4446 };/*
4447  * Based on:
4448  * Ext JS Library 1.1.1
4449  * Copyright(c) 2006-2007, Ext JS, LLC.
4450  *
4451  * Originally Released Under LGPL - original licence link has changed is not relivant.
4452  *
4453  * Fork - LGPL
4454  * <script type="text/javascript">
4455  */
4456
4457 /**
4458 * @class Roo.data.Record
4459  * Instances of this class encapsulate both record <em>definition</em> information, and record
4460  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4461  * to access Records cached in an {@link Roo.data.Store} object.<br>
4462  * <p>
4463  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4464  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4465  * objects.<br>
4466  * <p>
4467  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4468  * @constructor
4469  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4470  * {@link #create}. The parameters are the same.
4471  * @param {Array} data An associative Array of data values keyed by the field name.
4472  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4473  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4474  * not specified an integer id is generated.
4475  */
4476 Roo.data.Record = function(data, id){
4477     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4478     this.data = data;
4479 };
4480
4481 /**
4482  * Generate a constructor for a specific record layout.
4483  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4484  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4485  * Each field definition object may contain the following properties: <ul>
4486  * <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,
4487  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4488  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4489  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4490  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4491  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4492  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4493  * this may be omitted.</p></li>
4494  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4495  * <ul><li>auto (Default, implies no conversion)</li>
4496  * <li>string</li>
4497  * <li>int</li>
4498  * <li>float</li>
4499  * <li>boolean</li>
4500  * <li>date</li></ul></p></li>
4501  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4502  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4503  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4504  * by the Reader into an object that will be stored in the Record. It is passed the
4505  * following parameters:<ul>
4506  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4507  * </ul></p></li>
4508  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4509  * </ul>
4510  * <br>usage:<br><pre><code>
4511 var TopicRecord = Roo.data.Record.create(
4512     {name: 'title', mapping: 'topic_title'},
4513     {name: 'author', mapping: 'username'},
4514     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4515     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4516     {name: 'lastPoster', mapping: 'user2'},
4517     {name: 'excerpt', mapping: 'post_text'}
4518 );
4519
4520 var myNewRecord = new TopicRecord({
4521     title: 'Do my job please',
4522     author: 'noobie',
4523     totalPosts: 1,
4524     lastPost: new Date(),
4525     lastPoster: 'Animal',
4526     excerpt: 'No way dude!'
4527 });
4528 myStore.add(myNewRecord);
4529 </code></pre>
4530  * @method create
4531  * @static
4532  */
4533 Roo.data.Record.create = function(o){
4534     var f = function(){
4535         f.superclass.constructor.apply(this, arguments);
4536     };
4537     Roo.extend(f, Roo.data.Record);
4538     var p = f.prototype;
4539     p.fields = new Roo.util.MixedCollection(false, function(field){
4540         return field.name;
4541     });
4542     for(var i = 0, len = o.length; i < len; i++){
4543         p.fields.add(new Roo.data.Field(o[i]));
4544     }
4545     f.getField = function(name){
4546         return p.fields.get(name);  
4547     };
4548     return f;
4549 };
4550
4551 Roo.data.Record.AUTO_ID = 1000;
4552 Roo.data.Record.EDIT = 'edit';
4553 Roo.data.Record.REJECT = 'reject';
4554 Roo.data.Record.COMMIT = 'commit';
4555
4556 Roo.data.Record.prototype = {
4557     /**
4558      * Readonly flag - true if this record has been modified.
4559      * @type Boolean
4560      */
4561     dirty : false,
4562     editing : false,
4563     error: null,
4564     modified: null,
4565
4566     // private
4567     join : function(store){
4568         this.store = store;
4569     },
4570
4571     /**
4572      * Set the named field to the specified value.
4573      * @param {String} name The name of the field to set.
4574      * @param {Object} value The value to set the field to.
4575      */
4576     set : function(name, value){
4577         if(this.data[name] == value){
4578             return;
4579         }
4580         this.dirty = true;
4581         if(!this.modified){
4582             this.modified = {};
4583         }
4584         if(typeof this.modified[name] == 'undefined'){
4585             this.modified[name] = this.data[name];
4586         }
4587         this.data[name] = value;
4588         if(!this.editing){
4589             this.store.afterEdit(this);
4590         }       
4591     },
4592
4593     /**
4594      * Get the value of the named field.
4595      * @param {String} name The name of the field to get the value of.
4596      * @return {Object} The value of the field.
4597      */
4598     get : function(name){
4599         return this.data[name]; 
4600     },
4601
4602     // private
4603     beginEdit : function(){
4604         this.editing = true;
4605         this.modified = {}; 
4606     },
4607
4608     // private
4609     cancelEdit : function(){
4610         this.editing = false;
4611         delete this.modified;
4612     },
4613
4614     // private
4615     endEdit : function(){
4616         this.editing = false;
4617         if(this.dirty && this.store){
4618             this.store.afterEdit(this);
4619         }
4620     },
4621
4622     /**
4623      * Usually called by the {@link Roo.data.Store} which owns the Record.
4624      * Rejects all changes made to the Record since either creation, or the last commit operation.
4625      * Modified fields are reverted to their original values.
4626      * <p>
4627      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4628      * of reject operations.
4629      */
4630     reject : function(){
4631         var m = this.modified;
4632         for(var n in m){
4633             if(typeof m[n] != "function"){
4634                 this.data[n] = m[n];
4635             }
4636         }
4637         this.dirty = false;
4638         delete this.modified;
4639         this.editing = false;
4640         if(this.store){
4641             this.store.afterReject(this);
4642         }
4643     },
4644
4645     /**
4646      * Usually called by the {@link Roo.data.Store} which owns the Record.
4647      * Commits all changes made to the Record since either creation, or the last commit operation.
4648      * <p>
4649      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4650      * of commit operations.
4651      */
4652     commit : function(){
4653         this.dirty = false;
4654         delete this.modified;
4655         this.editing = false;
4656         if(this.store){
4657             this.store.afterCommit(this);
4658         }
4659     },
4660
4661     // private
4662     hasError : function(){
4663         return this.error != null;
4664     },
4665
4666     // private
4667     clearError : function(){
4668         this.error = null;
4669     },
4670
4671     /**
4672      * Creates a copy of this record.
4673      * @param {String} id (optional) A new record id if you don't want to use this record's id
4674      * @return {Record}
4675      */
4676     copy : function(newId) {
4677         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4678     }
4679 };/*
4680  * Based on:
4681  * Ext JS Library 1.1.1
4682  * Copyright(c) 2006-2007, Ext JS, LLC.
4683  *
4684  * Originally Released Under LGPL - original licence link has changed is not relivant.
4685  *
4686  * Fork - LGPL
4687  * <script type="text/javascript">
4688  */
4689
4690
4691
4692 /**
4693  * @class Roo.data.Store
4694  * @extends Roo.util.Observable
4695  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4696  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4697  * <p>
4698  * 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
4699  * has no knowledge of the format of the data returned by the Proxy.<br>
4700  * <p>
4701  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4702  * instances from the data object. These records are cached and made available through accessor functions.
4703  * @constructor
4704  * Creates a new Store.
4705  * @param {Object} config A config object containing the objects needed for the Store to access data,
4706  * and read the data into Records.
4707  */
4708 Roo.data.Store = function(config){
4709     this.data = new Roo.util.MixedCollection(false);
4710     this.data.getKey = function(o){
4711         return o.id;
4712     };
4713     this.baseParams = {};
4714     // private
4715     this.paramNames = {
4716         "start" : "start",
4717         "limit" : "limit",
4718         "sort" : "sort",
4719         "dir" : "dir"
4720     };
4721
4722     if(config && config.data){
4723         this.inlineData = config.data;
4724         delete config.data;
4725     }
4726
4727     Roo.apply(this, config);
4728     
4729     if(this.reader){ // reader passed
4730         this.reader = Roo.factory(this.reader, Roo.data);
4731         this.reader.xmodule = this.xmodule || false;
4732         if(!this.recordType){
4733             this.recordType = this.reader.recordType;
4734         }
4735         if(this.reader.onMetaChange){
4736             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4737         }
4738     }
4739
4740     if(this.recordType){
4741         this.fields = this.recordType.prototype.fields;
4742     }
4743     this.modified = [];
4744
4745     this.addEvents({
4746         /**
4747          * @event datachanged
4748          * Fires when the data cache has changed, and a widget which is using this Store
4749          * as a Record cache should refresh its view.
4750          * @param {Store} this
4751          */
4752         datachanged : true,
4753         /**
4754          * @event metachange
4755          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4756          * @param {Store} this
4757          * @param {Object} meta The JSON metadata
4758          */
4759         metachange : true,
4760         /**
4761          * @event add
4762          * Fires when Records have been added to the Store
4763          * @param {Store} this
4764          * @param {Roo.data.Record[]} records The array of Records added
4765          * @param {Number} index The index at which the record(s) were added
4766          */
4767         add : true,
4768         /**
4769          * @event remove
4770          * Fires when a Record has been removed from the Store
4771          * @param {Store} this
4772          * @param {Roo.data.Record} record The Record that was removed
4773          * @param {Number} index The index at which the record was removed
4774          */
4775         remove : true,
4776         /**
4777          * @event update
4778          * Fires when a Record has been updated
4779          * @param {Store} this
4780          * @param {Roo.data.Record} record The Record that was updated
4781          * @param {String} operation The update operation being performed.  Value may be one of:
4782          * <pre><code>
4783  Roo.data.Record.EDIT
4784  Roo.data.Record.REJECT
4785  Roo.data.Record.COMMIT
4786          * </code></pre>
4787          */
4788         update : true,
4789         /**
4790          * @event clear
4791          * Fires when the data cache has been cleared.
4792          * @param {Store} this
4793          */
4794         clear : true,
4795         /**
4796          * @event beforeload
4797          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4798          * the load action will be canceled.
4799          * @param {Store} this
4800          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4801          */
4802         beforeload : true,
4803         /**
4804          * @event load
4805          * Fires after a new set of Records has been loaded.
4806          * @param {Store} this
4807          * @param {Roo.data.Record[]} records The Records that were loaded
4808          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4809          */
4810         load : true,
4811         /**
4812          * @event loadexception
4813          * Fires if an exception occurs in the Proxy during loading.
4814          * Called with the signature of the Proxy's "loadexception" event.
4815          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4816          * 
4817          * @param {Proxy} 
4818          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4819          * @param {Object} load options 
4820          * @param {Object} jsonData from your request (normally this contains the Exception)
4821          */
4822         loadexception : true
4823     });
4824     
4825     if(this.proxy){
4826         this.proxy = Roo.factory(this.proxy, Roo.data);
4827         this.proxy.xmodule = this.xmodule || false;
4828         this.relayEvents(this.proxy,  ["loadexception"]);
4829     }
4830     this.sortToggle = {};
4831
4832     Roo.data.Store.superclass.constructor.call(this);
4833
4834     if(this.inlineData){
4835         this.loadData(this.inlineData);
4836         delete this.inlineData;
4837     }
4838 };
4839 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4840      /**
4841     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4842     * without a remote query - used by combo/forms at present.
4843     */
4844     
4845     /**
4846     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4847     */
4848     /**
4849     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4850     */
4851     /**
4852     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4853     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4854     */
4855     /**
4856     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4857     * on any HTTP request
4858     */
4859     /**
4860     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4861     */
4862     /**
4863     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4864     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4865     */
4866     remoteSort : false,
4867
4868     /**
4869     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4870      * loaded or when a record is removed. (defaults to false).
4871     */
4872     pruneModifiedRecords : false,
4873
4874     // private
4875     lastOptions : null,
4876
4877     /**
4878      * Add Records to the Store and fires the add event.
4879      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4880      */
4881     add : function(records){
4882         records = [].concat(records);
4883         for(var i = 0, len = records.length; i < len; i++){
4884             records[i].join(this);
4885         }
4886         var index = this.data.length;
4887         this.data.addAll(records);
4888         this.fireEvent("add", this, records, index);
4889     },
4890
4891     /**
4892      * Remove a Record from the Store and fires the remove event.
4893      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4894      */
4895     remove : function(record){
4896         var index = this.data.indexOf(record);
4897         this.data.removeAt(index);
4898         if(this.pruneModifiedRecords){
4899             this.modified.remove(record);
4900         }
4901         this.fireEvent("remove", this, record, index);
4902     },
4903
4904     /**
4905      * Remove all Records from the Store and fires the clear event.
4906      */
4907     removeAll : function(){
4908         this.data.clear();
4909         if(this.pruneModifiedRecords){
4910             this.modified = [];
4911         }
4912         this.fireEvent("clear", this);
4913     },
4914
4915     /**
4916      * Inserts Records to the Store at the given index and fires the add event.
4917      * @param {Number} index The start index at which to insert the passed Records.
4918      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4919      */
4920     insert : function(index, records){
4921         records = [].concat(records);
4922         for(var i = 0, len = records.length; i < len; i++){
4923             this.data.insert(index, records[i]);
4924             records[i].join(this);
4925         }
4926         this.fireEvent("add", this, records, index);
4927     },
4928
4929     /**
4930      * Get the index within the cache of the passed Record.
4931      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4932      * @return {Number} The index of the passed Record. Returns -1 if not found.
4933      */
4934     indexOf : function(record){
4935         return this.data.indexOf(record);
4936     },
4937
4938     /**
4939      * Get the index within the cache of the Record with the passed id.
4940      * @param {String} id The id of the Record to find.
4941      * @return {Number} The index of the Record. Returns -1 if not found.
4942      */
4943     indexOfId : function(id){
4944         return this.data.indexOfKey(id);
4945     },
4946
4947     /**
4948      * Get the Record with the specified id.
4949      * @param {String} id The id of the Record to find.
4950      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4951      */
4952     getById : function(id){
4953         return this.data.key(id);
4954     },
4955
4956     /**
4957      * Get the Record at the specified index.
4958      * @param {Number} index The index of the Record to find.
4959      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4960      */
4961     getAt : function(index){
4962         return this.data.itemAt(index);
4963     },
4964
4965     /**
4966      * Returns a range of Records between specified indices.
4967      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4968      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4969      * @return {Roo.data.Record[]} An array of Records
4970      */
4971     getRange : function(start, end){
4972         return this.data.getRange(start, end);
4973     },
4974
4975     // private
4976     storeOptions : function(o){
4977         o = Roo.apply({}, o);
4978         delete o.callback;
4979         delete o.scope;
4980         this.lastOptions = o;
4981     },
4982
4983     /**
4984      * Loads the Record cache from the configured Proxy using the configured Reader.
4985      * <p>
4986      * If using remote paging, then the first load call must specify the <em>start</em>
4987      * and <em>limit</em> properties in the options.params property to establish the initial
4988      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4989      * <p>
4990      * <strong>It is important to note that for remote data sources, loading is asynchronous,
4991      * and this call will return before the new data has been loaded. Perform any post-processing
4992      * in a callback function, or in a "load" event handler.</strong>
4993      * <p>
4994      * @param {Object} options An object containing properties which control loading options:<ul>
4995      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
4996      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
4997      * passed the following arguments:<ul>
4998      * <li>r : Roo.data.Record[]</li>
4999      * <li>options: Options object from the load call</li>
5000      * <li>success: Boolean success indicator</li></ul></li>
5001      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5002      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5003      * </ul>
5004      */
5005     load : function(options){
5006         options = options || {};
5007         if(this.fireEvent("beforeload", this, options) !== false){
5008             this.storeOptions(options);
5009             var p = Roo.apply(options.params || {}, this.baseParams);
5010             // if meta was not loaded from remote source.. try requesting it.
5011             if (!this.reader.metaFromRemote) {
5012                 p._requestMeta = 1;
5013             }
5014             if(this.sortInfo && this.remoteSort){
5015                 var pn = this.paramNames;
5016                 p[pn["sort"]] = this.sortInfo.field;
5017                 p[pn["dir"]] = this.sortInfo.direction;
5018             }
5019             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5020         }
5021     },
5022
5023     /**
5024      * Reloads the Record cache from the configured Proxy using the configured Reader and
5025      * the options from the last load operation performed.
5026      * @param {Object} options (optional) An object containing properties which may override the options
5027      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5028      * the most recently used options are reused).
5029      */
5030     reload : function(options){
5031         this.load(Roo.applyIf(options||{}, this.lastOptions));
5032     },
5033
5034     // private
5035     // Called as a callback by the Reader during a load operation.
5036     loadRecords : function(o, options, success){
5037         if(!o || success === false){
5038             if(success !== false){
5039                 this.fireEvent("load", this, [], options);
5040             }
5041             if(options.callback){
5042                 options.callback.call(options.scope || this, [], options, false);
5043             }
5044             return;
5045         }
5046         // if data returned failure - throw an exception.
5047         if (o.success === false) {
5048             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5049             return;
5050         }
5051         var r = o.records, t = o.totalRecords || r.length;
5052         if(!options || options.add !== true){
5053             if(this.pruneModifiedRecords){
5054                 this.modified = [];
5055             }
5056             for(var i = 0, len = r.length; i < len; i++){
5057                 r[i].join(this);
5058             }
5059             if(this.snapshot){
5060                 this.data = this.snapshot;
5061                 delete this.snapshot;
5062             }
5063             this.data.clear();
5064             this.data.addAll(r);
5065             this.totalLength = t;
5066             this.applySort();
5067             this.fireEvent("datachanged", this);
5068         }else{
5069             this.totalLength = Math.max(t, this.data.length+r.length);
5070             this.add(r);
5071         }
5072         this.fireEvent("load", this, r, options);
5073         if(options.callback){
5074             options.callback.call(options.scope || this, r, options, true);
5075         }
5076     },
5077
5078     /**
5079      * Loads data from a passed data block. A Reader which understands the format of the data
5080      * must have been configured in the constructor.
5081      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5082      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5083      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5084      */
5085     loadData : function(o, append){
5086         var r = this.reader.readRecords(o);
5087         this.loadRecords(r, {add: append}, true);
5088     },
5089
5090     /**
5091      * Gets the number of cached records.
5092      * <p>
5093      * <em>If using paging, this may not be the total size of the dataset. If the data object
5094      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5095      * the data set size</em>
5096      */
5097     getCount : function(){
5098         return this.data.length || 0;
5099     },
5100
5101     /**
5102      * Gets the total number of records in the dataset as returned by the server.
5103      * <p>
5104      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5105      * the dataset size</em>
5106      */
5107     getTotalCount : function(){
5108         return this.totalLength || 0;
5109     },
5110
5111     /**
5112      * Returns the sort state of the Store as an object with two properties:
5113      * <pre><code>
5114  field {String} The name of the field by which the Records are sorted
5115  direction {String} The sort order, "ASC" or "DESC"
5116      * </code></pre>
5117      */
5118     getSortState : function(){
5119         return this.sortInfo;
5120     },
5121
5122     // private
5123     applySort : function(){
5124         if(this.sortInfo && !this.remoteSort){
5125             var s = this.sortInfo, f = s.field;
5126             var st = this.fields.get(f).sortType;
5127             var fn = function(r1, r2){
5128                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5129                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5130             };
5131             this.data.sort(s.direction, fn);
5132             if(this.snapshot && this.snapshot != this.data){
5133                 this.snapshot.sort(s.direction, fn);
5134             }
5135         }
5136     },
5137
5138     /**
5139      * Sets the default sort column and order to be used by the next load operation.
5140      * @param {String} fieldName The name of the field to sort by.
5141      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5142      */
5143     setDefaultSort : function(field, dir){
5144         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5145     },
5146
5147     /**
5148      * Sort the Records.
5149      * If remote sorting is used, the sort is performed on the server, and the cache is
5150      * reloaded. If local sorting is used, the cache is sorted internally.
5151      * @param {String} fieldName The name of the field to sort by.
5152      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5153      */
5154     sort : function(fieldName, dir){
5155         var f = this.fields.get(fieldName);
5156         if(!dir){
5157             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
5158                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5159             }else{
5160                 dir = f.sortDir;
5161             }
5162         }
5163         this.sortToggle[f.name] = dir;
5164         this.sortInfo = {field: f.name, direction: dir};
5165         if(!this.remoteSort){
5166             this.applySort();
5167             this.fireEvent("datachanged", this);
5168         }else{
5169             this.load(this.lastOptions);
5170         }
5171     },
5172
5173     /**
5174      * Calls the specified function for each of the Records in the cache.
5175      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5176      * Returning <em>false</em> aborts and exits the iteration.
5177      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5178      */
5179     each : function(fn, scope){
5180         this.data.each(fn, scope);
5181     },
5182
5183     /**
5184      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5185      * (e.g., during paging).
5186      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5187      */
5188     getModifiedRecords : function(){
5189         return this.modified;
5190     },
5191
5192     // private
5193     createFilterFn : function(property, value, anyMatch){
5194         if(!value.exec){ // not a regex
5195             value = String(value);
5196             if(value.length == 0){
5197                 return false;
5198             }
5199             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5200         }
5201         return function(r){
5202             return value.test(r.data[property]);
5203         };
5204     },
5205
5206     /**
5207      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5208      * @param {String} property A field on your records
5209      * @param {Number} start The record index to start at (defaults to 0)
5210      * @param {Number} end The last record index to include (defaults to length - 1)
5211      * @return {Number} The sum
5212      */
5213     sum : function(property, start, end){
5214         var rs = this.data.items, v = 0;
5215         start = start || 0;
5216         end = (end || end === 0) ? end : rs.length-1;
5217
5218         for(var i = start; i <= end; i++){
5219             v += (rs[i].data[property] || 0);
5220         }
5221         return v;
5222     },
5223
5224     /**
5225      * Filter the records by a specified property.
5226      * @param {String} field A field on your records
5227      * @param {String/RegExp} value Either a string that the field
5228      * should start with or a RegExp to test against the field
5229      * @param {Boolean} anyMatch True to match any part not just the beginning
5230      */
5231     filter : function(property, value, anyMatch){
5232         var fn = this.createFilterFn(property, value, anyMatch);
5233         return fn ? this.filterBy(fn) : this.clearFilter();
5234     },
5235
5236     /**
5237      * Filter by a function. The specified function will be called with each
5238      * record in this data source. If the function returns true the record is included,
5239      * otherwise it is filtered.
5240      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5241      * @param {Object} scope (optional) The scope of the function (defaults to this)
5242      */
5243     filterBy : function(fn, scope){
5244         this.snapshot = this.snapshot || this.data;
5245         this.data = this.queryBy(fn, scope||this);
5246         this.fireEvent("datachanged", this);
5247     },
5248
5249     /**
5250      * Query the records by a specified property.
5251      * @param {String} field A field on your records
5252      * @param {String/RegExp} value Either a string that the field
5253      * should start with or a RegExp to test against the field
5254      * @param {Boolean} anyMatch True to match any part not just the beginning
5255      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5256      */
5257     query : function(property, value, anyMatch){
5258         var fn = this.createFilterFn(property, value, anyMatch);
5259         return fn ? this.queryBy(fn) : this.data.clone();
5260     },
5261
5262     /**
5263      * Query by a function. The specified function will be called with each
5264      * record in this data source. If the function returns true the record is included
5265      * in the results.
5266      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5267      * @param {Object} scope (optional) The scope of the function (defaults to this)
5268       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5269      **/
5270     queryBy : function(fn, scope){
5271         var data = this.snapshot || this.data;
5272         return data.filterBy(fn, scope||this);
5273     },
5274
5275     /**
5276      * Collects unique values for a particular dataIndex from this store.
5277      * @param {String} dataIndex The property to collect
5278      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5279      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5280      * @return {Array} An array of the unique values
5281      **/
5282     collect : function(dataIndex, allowNull, bypassFilter){
5283         var d = (bypassFilter === true && this.snapshot) ?
5284                 this.snapshot.items : this.data.items;
5285         var v, sv, r = [], l = {};
5286         for(var i = 0, len = d.length; i < len; i++){
5287             v = d[i].data[dataIndex];
5288             sv = String(v);
5289             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5290                 l[sv] = true;
5291                 r[r.length] = v;
5292             }
5293         }
5294         return r;
5295     },
5296
5297     /**
5298      * Revert to a view of the Record cache with no filtering applied.
5299      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5300      */
5301     clearFilter : function(suppressEvent){
5302         if(this.snapshot && this.snapshot != this.data){
5303             this.data = this.snapshot;
5304             delete this.snapshot;
5305             if(suppressEvent !== true){
5306                 this.fireEvent("datachanged", this);
5307             }
5308         }
5309     },
5310
5311     // private
5312     afterEdit : function(record){
5313         if(this.modified.indexOf(record) == -1){
5314             this.modified.push(record);
5315         }
5316         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5317     },
5318
5319     // private
5320     afterReject : function(record){
5321         this.modified.remove(record);
5322         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5323     },
5324
5325     // private
5326     afterCommit : function(record){
5327         this.modified.remove(record);
5328         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5329     },
5330
5331     /**
5332      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5333      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5334      */
5335     commitChanges : function(){
5336         var m = this.modified.slice(0);
5337         this.modified = [];
5338         for(var i = 0, len = m.length; i < len; i++){
5339             m[i].commit();
5340         }
5341     },
5342
5343     /**
5344      * Cancel outstanding changes on all changed records.
5345      */
5346     rejectChanges : function(){
5347         var m = this.modified.slice(0);
5348         this.modified = [];
5349         for(var i = 0, len = m.length; i < len; i++){
5350             m[i].reject();
5351         }
5352     },
5353
5354     onMetaChange : function(meta, rtype, o){
5355         this.recordType = rtype;
5356         this.fields = rtype.prototype.fields;
5357         delete this.snapshot;
5358         this.sortInfo = meta.sortInfo || this.sortInfo;
5359         this.modified = [];
5360         this.fireEvent('metachange', this, this.reader.meta);
5361     }
5362 });/*
5363  * Based on:
5364  * Ext JS Library 1.1.1
5365  * Copyright(c) 2006-2007, Ext JS, LLC.
5366  *
5367  * Originally Released Under LGPL - original licence link has changed is not relivant.
5368  *
5369  * Fork - LGPL
5370  * <script type="text/javascript">
5371  */
5372
5373 /**
5374  * @class Roo.data.SimpleStore
5375  * @extends Roo.data.Store
5376  * Small helper class to make creating Stores from Array data easier.
5377  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5378  * @cfg {Array} fields An array of field definition objects, or field name strings.
5379  * @cfg {Array} data The multi-dimensional array of data
5380  * @constructor
5381  * @param {Object} config
5382  */
5383 Roo.data.SimpleStore = function(config){
5384     Roo.data.SimpleStore.superclass.constructor.call(this, {
5385         isLocal : true,
5386         reader: new Roo.data.ArrayReader({
5387                 id: config.id
5388             },
5389             Roo.data.Record.create(config.fields)
5390         ),
5391         proxy : new Roo.data.MemoryProxy(config.data)
5392     });
5393     this.load();
5394 };
5395 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5396  * Based on:
5397  * Ext JS Library 1.1.1
5398  * Copyright(c) 2006-2007, Ext JS, LLC.
5399  *
5400  * Originally Released Under LGPL - original licence link has changed is not relivant.
5401  *
5402  * Fork - LGPL
5403  * <script type="text/javascript">
5404  */
5405
5406 /**
5407 /**
5408  * @extends Roo.data.Store
5409  * @class Roo.data.JsonStore
5410  * Small helper class to make creating Stores for JSON data easier. <br/>
5411 <pre><code>
5412 var store = new Roo.data.JsonStore({
5413     url: 'get-images.php',
5414     root: 'images',
5415     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5416 });
5417 </code></pre>
5418  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5419  * JsonReader and HttpProxy (unless inline data is provided).</b>
5420  * @cfg {Array} fields An array of field definition objects, or field name strings.
5421  * @constructor
5422  * @param {Object} config
5423  */
5424 Roo.data.JsonStore = function(c){
5425     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5426         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5427         reader: new Roo.data.JsonReader(c, c.fields)
5428     }));
5429 };
5430 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5431  * Based on:
5432  * Ext JS Library 1.1.1
5433  * Copyright(c) 2006-2007, Ext JS, LLC.
5434  *
5435  * Originally Released Under LGPL - original licence link has changed is not relivant.
5436  *
5437  * Fork - LGPL
5438  * <script type="text/javascript">
5439  */
5440
5441  
5442 Roo.data.Field = function(config){
5443     if(typeof config == "string"){
5444         config = {name: config};
5445     }
5446     Roo.apply(this, config);
5447     
5448     if(!this.type){
5449         this.type = "auto";
5450     }
5451     
5452     var st = Roo.data.SortTypes;
5453     // named sortTypes are supported, here we look them up
5454     if(typeof this.sortType == "string"){
5455         this.sortType = st[this.sortType];
5456     }
5457     
5458     // set default sortType for strings and dates
5459     if(!this.sortType){
5460         switch(this.type){
5461             case "string":
5462                 this.sortType = st.asUCString;
5463                 break;
5464             case "date":
5465                 this.sortType = st.asDate;
5466                 break;
5467             default:
5468                 this.sortType = st.none;
5469         }
5470     }
5471
5472     // define once
5473     var stripRe = /[\$,%]/g;
5474
5475     // prebuilt conversion function for this field, instead of
5476     // switching every time we're reading a value
5477     if(!this.convert){
5478         var cv, dateFormat = this.dateFormat;
5479         switch(this.type){
5480             case "":
5481             case "auto":
5482             case undefined:
5483                 cv = function(v){ return v; };
5484                 break;
5485             case "string":
5486                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5487                 break;
5488             case "int":
5489                 cv = function(v){
5490                     return v !== undefined && v !== null && v !== '' ?
5491                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5492                     };
5493                 break;
5494             case "float":
5495                 cv = function(v){
5496                     return v !== undefined && v !== null && v !== '' ?
5497                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5498                     };
5499                 break;
5500             case "bool":
5501             case "boolean":
5502                 cv = function(v){ return v === true || v === "true" || v == 1; };
5503                 break;
5504             case "date":
5505                 cv = function(v){
5506                     if(!v){
5507                         return '';
5508                     }
5509                     if(v instanceof Date){
5510                         return v;
5511                     }
5512                     if(dateFormat){
5513                         if(dateFormat == "timestamp"){
5514                             return new Date(v*1000);
5515                         }
5516                         return Date.parseDate(v, dateFormat);
5517                     }
5518                     var parsed = Date.parse(v);
5519                     return parsed ? new Date(parsed) : null;
5520                 };
5521              break;
5522             
5523         }
5524         this.convert = cv;
5525     }
5526 };
5527
5528 Roo.data.Field.prototype = {
5529     dateFormat: null,
5530     defaultValue: "",
5531     mapping: null,
5532     sortType : null,
5533     sortDir : "ASC"
5534 };/*
5535  * Based on:
5536  * Ext JS Library 1.1.1
5537  * Copyright(c) 2006-2007, Ext JS, LLC.
5538  *
5539  * Originally Released Under LGPL - original licence link has changed is not relivant.
5540  *
5541  * Fork - LGPL
5542  * <script type="text/javascript">
5543  */
5544  
5545 // Base class for reading structured data from a data source.  This class is intended to be
5546 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5547
5548 /**
5549  * @class Roo.data.DataReader
5550  * Base class for reading structured data from a data source.  This class is intended to be
5551  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5552  */
5553
5554 Roo.data.DataReader = function(meta, recordType){
5555     
5556     this.meta = meta;
5557     
5558     this.recordType = recordType instanceof Array ? 
5559         Roo.data.Record.create(recordType) : recordType;
5560 };
5561
5562 Roo.data.DataReader.prototype = {
5563      /**
5564      * Create an empty record
5565      * @param {Object} data (optional) - overlay some values
5566      * @return {Roo.data.Record} record created.
5567      */
5568     newRow :  function(d) {
5569         var da =  {};
5570         this.recordType.prototype.fields.each(function(c) {
5571             switch( c.type) {
5572                 case 'int' : da[c.name] = 0; break;
5573                 case 'date' : da[c.name] = new Date(); break;
5574                 case 'float' : da[c.name] = 0.0; break;
5575                 case 'boolean' : da[c.name] = false; break;
5576                 default : da[c.name] = ""; break;
5577             }
5578             
5579         });
5580         return new this.recordType(Roo.apply(da, d));
5581     }
5582     
5583 };/*
5584  * Based on:
5585  * Ext JS Library 1.1.1
5586  * Copyright(c) 2006-2007, Ext JS, LLC.
5587  *
5588  * Originally Released Under LGPL - original licence link has changed is not relivant.
5589  *
5590  * Fork - LGPL
5591  * <script type="text/javascript">
5592  */
5593
5594 /**
5595  * @class Roo.data.DataProxy
5596  * @extends Roo.data.Observable
5597  * This class is an abstract base class for implementations which provide retrieval of
5598  * unformatted data objects.<br>
5599  * <p>
5600  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5601  * (of the appropriate type which knows how to parse the data object) to provide a block of
5602  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5603  * <p>
5604  * Custom implementations must implement the load method as described in
5605  * {@link Roo.data.HttpProxy#load}.
5606  */
5607 Roo.data.DataProxy = function(){
5608     this.addEvents({
5609         /**
5610          * @event beforeload
5611          * Fires before a network request is made to retrieve a data object.
5612          * @param {Object} This DataProxy object.
5613          * @param {Object} params The params parameter to the load function.
5614          */
5615         beforeload : true,
5616         /**
5617          * @event load
5618          * Fires before the load method's callback is called.
5619          * @param {Object} This DataProxy object.
5620          * @param {Object} o The data object.
5621          * @param {Object} arg The callback argument object passed to the load function.
5622          */
5623         load : true,
5624         /**
5625          * @event loadexception
5626          * Fires if an Exception occurs during data retrieval.
5627          * @param {Object} This DataProxy object.
5628          * @param {Object} o The data object.
5629          * @param {Object} arg The callback argument object passed to the load function.
5630          * @param {Object} e The Exception.
5631          */
5632         loadexception : true
5633     });
5634     Roo.data.DataProxy.superclass.constructor.call(this);
5635 };
5636
5637 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5638
5639     /**
5640      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5641      */
5642 /*
5643  * Based on:
5644  * Ext JS Library 1.1.1
5645  * Copyright(c) 2006-2007, Ext JS, LLC.
5646  *
5647  * Originally Released Under LGPL - original licence link has changed is not relivant.
5648  *
5649  * Fork - LGPL
5650  * <script type="text/javascript">
5651  */
5652 /**
5653  * @class Roo.data.MemoryProxy
5654  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5655  * to the Reader when its load method is called.
5656  * @constructor
5657  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5658  */
5659 Roo.data.MemoryProxy = function(data){
5660     if (data.data) {
5661         data = data.data;
5662     }
5663     Roo.data.MemoryProxy.superclass.constructor.call(this);
5664     this.data = data;
5665 };
5666
5667 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5668     /**
5669      * Load data from the requested source (in this case an in-memory
5670      * data object passed to the constructor), read the data object into
5671      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5672      * process that block using the passed callback.
5673      * @param {Object} params This parameter is not used by the MemoryProxy class.
5674      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5675      * object into a block of Roo.data.Records.
5676      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5677      * The function must be passed <ul>
5678      * <li>The Record block object</li>
5679      * <li>The "arg" argument from the load function</li>
5680      * <li>A boolean success indicator</li>
5681      * </ul>
5682      * @param {Object} scope The scope in which to call the callback
5683      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5684      */
5685     load : function(params, reader, callback, scope, arg){
5686         params = params || {};
5687         var result;
5688         try {
5689             result = reader.readRecords(this.data);
5690         }catch(e){
5691             this.fireEvent("loadexception", this, arg, null, e);
5692             callback.call(scope, null, arg, false);
5693             return;
5694         }
5695         callback.call(scope, result, arg, true);
5696     },
5697     
5698     // private
5699     update : function(params, records){
5700         
5701     }
5702 });/*
5703  * Based on:
5704  * Ext JS Library 1.1.1
5705  * Copyright(c) 2006-2007, Ext JS, LLC.
5706  *
5707  * Originally Released Under LGPL - original licence link has changed is not relivant.
5708  *
5709  * Fork - LGPL
5710  * <script type="text/javascript">
5711  */
5712 /**
5713  * @class Roo.data.HttpProxy
5714  * @extends Roo.data.DataProxy
5715  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5716  * configured to reference a certain URL.<br><br>
5717  * <p>
5718  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5719  * from which the running page was served.<br><br>
5720  * <p>
5721  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5722  * <p>
5723  * Be aware that to enable the browser to parse an XML document, the server must set
5724  * the Content-Type header in the HTTP response to "text/xml".
5725  * @constructor
5726  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5727  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5728  * will be used to make the request.
5729  */
5730 Roo.data.HttpProxy = function(conn){
5731     Roo.data.HttpProxy.superclass.constructor.call(this);
5732     // is conn a conn config or a real conn?
5733     this.conn = conn;
5734     this.useAjax = !conn || !conn.events;
5735   
5736 };
5737
5738 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5739     // thse are take from connection...
5740     
5741     /**
5742      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5743      */
5744     /**
5745      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5746      * extra parameters to each request made by this object. (defaults to undefined)
5747      */
5748     /**
5749      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5750      *  to each request made by this object. (defaults to undefined)
5751      */
5752     /**
5753      * @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)
5754      */
5755     /**
5756      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5757      */
5758      /**
5759      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5760      * @type Boolean
5761      */
5762   
5763
5764     /**
5765      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5766      * @type Boolean
5767      */
5768     /**
5769      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5770      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5771      * a finer-grained basis than the DataProxy events.
5772      */
5773     getConnection : function(){
5774         return this.useAjax ? Roo.Ajax : this.conn;
5775     },
5776
5777     /**
5778      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5779      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5780      * process that block using the passed callback.
5781      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5782      * for the request to the remote server.
5783      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5784      * object into a block of Roo.data.Records.
5785      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5786      * The function must be passed <ul>
5787      * <li>The Record block object</li>
5788      * <li>The "arg" argument from the load function</li>
5789      * <li>A boolean success indicator</li>
5790      * </ul>
5791      * @param {Object} scope The scope in which to call the callback
5792      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5793      */
5794     load : function(params, reader, callback, scope, arg){
5795         if(this.fireEvent("beforeload", this, params) !== false){
5796             var  o = {
5797                 params : params || {},
5798                 request: {
5799                     callback : callback,
5800                     scope : scope,
5801                     arg : arg
5802                 },
5803                 reader: reader,
5804                 callback : this.loadResponse,
5805                 scope: this
5806             };
5807             if(this.useAjax){
5808                 Roo.applyIf(o, this.conn);
5809                 if(this.activeRequest){
5810                     Roo.Ajax.abort(this.activeRequest);
5811                 }
5812                 this.activeRequest = Roo.Ajax.request(o);
5813             }else{
5814                 this.conn.request(o);
5815             }
5816         }else{
5817             callback.call(scope||this, null, arg, false);
5818         }
5819     },
5820
5821     // private
5822     loadResponse : function(o, success, response){
5823         delete this.activeRequest;
5824         if(!success){
5825             this.fireEvent("loadexception", this, o, response);
5826             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5827             return;
5828         }
5829         var result;
5830         try {
5831             result = o.reader.read(response);
5832         }catch(e){
5833             this.fireEvent("loadexception", this, o, response, e);
5834             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5835             return;
5836         }
5837         
5838         this.fireEvent("load", this, o, o.request.arg);
5839         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5840     },
5841
5842     // private
5843     update : function(dataSet){
5844
5845     },
5846
5847     // private
5848     updateResponse : function(dataSet){
5849
5850     }
5851 });/*
5852  * Based on:
5853  * Ext JS Library 1.1.1
5854  * Copyright(c) 2006-2007, Ext JS, LLC.
5855  *
5856  * Originally Released Under LGPL - original licence link has changed is not relivant.
5857  *
5858  * Fork - LGPL
5859  * <script type="text/javascript">
5860  */
5861
5862 /**
5863  * @class Roo.data.ScriptTagProxy
5864  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5865  * other than the originating domain of the running page.<br><br>
5866  * <p>
5867  * <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
5868  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5869  * <p>
5870  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5871  * source code that is used as the source inside a &lt;script> tag.<br><br>
5872  * <p>
5873  * In order for the browser to process the returned data, the server must wrap the data object
5874  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5875  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5876  * depending on whether the callback name was passed:
5877  * <p>
5878  * <pre><code>
5879 boolean scriptTag = false;
5880 String cb = request.getParameter("callback");
5881 if (cb != null) {
5882     scriptTag = true;
5883     response.setContentType("text/javascript");
5884 } else {
5885     response.setContentType("application/x-json");
5886 }
5887 Writer out = response.getWriter();
5888 if (scriptTag) {
5889     out.write(cb + "(");
5890 }
5891 out.print(dataBlock.toJsonString());
5892 if (scriptTag) {
5893     out.write(");");
5894 }
5895 </pre></code>
5896  *
5897  * @constructor
5898  * @param {Object} config A configuration object.
5899  */
5900 Roo.data.ScriptTagProxy = function(config){
5901     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5902     Roo.apply(this, config);
5903     this.head = document.getElementsByTagName("head")[0];
5904 };
5905
5906 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5907
5908 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5909     /**
5910      * @cfg {String} url The URL from which to request the data object.
5911      */
5912     /**
5913      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5914      */
5915     timeout : 30000,
5916     /**
5917      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5918      * the server the name of the callback function set up by the load call to process the returned data object.
5919      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5920      * javascript output which calls this named function passing the data object as its only parameter.
5921      */
5922     callbackParam : "callback",
5923     /**
5924      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5925      * name to the request.
5926      */
5927     nocache : true,
5928
5929     /**
5930      * Load data from the configured URL, read the data object into
5931      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5932      * process that block using the passed callback.
5933      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5934      * for the request to the remote server.
5935      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5936      * object into a block of Roo.data.Records.
5937      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5938      * The function must be passed <ul>
5939      * <li>The Record block object</li>
5940      * <li>The "arg" argument from the load function</li>
5941      * <li>A boolean success indicator</li>
5942      * </ul>
5943      * @param {Object} scope The scope in which to call the callback
5944      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5945      */
5946     load : function(params, reader, callback, scope, arg){
5947         if(this.fireEvent("beforeload", this, params) !== false){
5948
5949             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5950
5951             var url = this.url;
5952             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5953             if(this.nocache){
5954                 url += "&_dc=" + (new Date().getTime());
5955             }
5956             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5957             var trans = {
5958                 id : transId,
5959                 cb : "stcCallback"+transId,
5960                 scriptId : "stcScript"+transId,
5961                 params : params,
5962                 arg : arg,
5963                 url : url,
5964                 callback : callback,
5965                 scope : scope,
5966                 reader : reader
5967             };
5968             var conn = this;
5969
5970             window[trans.cb] = function(o){
5971                 conn.handleResponse(o, trans);
5972             };
5973
5974             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5975
5976             if(this.autoAbort !== false){
5977                 this.abort();
5978             }
5979
5980             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
5981
5982             var script = document.createElement("script");
5983             script.setAttribute("src", url);
5984             script.setAttribute("type", "text/javascript");
5985             script.setAttribute("id", trans.scriptId);
5986             this.head.appendChild(script);
5987
5988             this.trans = trans;
5989         }else{
5990             callback.call(scope||this, null, arg, false);
5991         }
5992     },
5993
5994     // private
5995     isLoading : function(){
5996         return this.trans ? true : false;
5997     },
5998
5999     /**
6000      * Abort the current server request.
6001      */
6002     abort : function(){
6003         if(this.isLoading()){
6004             this.destroyTrans(this.trans);
6005         }
6006     },
6007
6008     // private
6009     destroyTrans : function(trans, isLoaded){
6010         this.head.removeChild(document.getElementById(trans.scriptId));
6011         clearTimeout(trans.timeoutId);
6012         if(isLoaded){
6013             window[trans.cb] = undefined;
6014             try{
6015                 delete window[trans.cb];
6016             }catch(e){}
6017         }else{
6018             // if hasn't been loaded, wait for load to remove it to prevent script error
6019             window[trans.cb] = function(){
6020                 window[trans.cb] = undefined;
6021                 try{
6022                     delete window[trans.cb];
6023                 }catch(e){}
6024             };
6025         }
6026     },
6027
6028     // private
6029     handleResponse : function(o, trans){
6030         this.trans = false;
6031         this.destroyTrans(trans, true);
6032         var result;
6033         try {
6034             result = trans.reader.readRecords(o);
6035         }catch(e){
6036             this.fireEvent("loadexception", this, o, trans.arg, e);
6037             trans.callback.call(trans.scope||window, null, trans.arg, false);
6038             return;
6039         }
6040         this.fireEvent("load", this, o, trans.arg);
6041         trans.callback.call(trans.scope||window, result, trans.arg, true);
6042     },
6043
6044     // private
6045     handleFailure : function(trans){
6046         this.trans = false;
6047         this.destroyTrans(trans, false);
6048         this.fireEvent("loadexception", this, null, trans.arg);
6049         trans.callback.call(trans.scope||window, null, trans.arg, false);
6050     }
6051 });/*
6052  * Based on:
6053  * Ext JS Library 1.1.1
6054  * Copyright(c) 2006-2007, Ext JS, LLC.
6055  *
6056  * Originally Released Under LGPL - original licence link has changed is not relivant.
6057  *
6058  * Fork - LGPL
6059  * <script type="text/javascript">
6060  */
6061
6062 /**
6063  * @class Roo.data.JsonReader
6064  * @extends Roo.data.DataReader
6065  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6066  * based on mappings in a provided Roo.data.Record constructor.
6067  * 
6068  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6069  * in the reply previously. 
6070  * 
6071  * <p>
6072  * Example code:
6073  * <pre><code>
6074 var RecordDef = Roo.data.Record.create([
6075     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6076     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6077 ]);
6078 var myReader = new Roo.data.JsonReader({
6079     totalProperty: "results",    // The property which contains the total dataset size (optional)
6080     root: "rows",                // The property which contains an Array of row objects
6081     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6082 }, RecordDef);
6083 </code></pre>
6084  * <p>
6085  * This would consume a JSON file like this:
6086  * <pre><code>
6087 { 'results': 2, 'rows': [
6088     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6089     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6090 }
6091 </code></pre>
6092  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6093  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6094  * paged from the remote server.
6095  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6096  * @cfg {String} root name of the property which contains the Array of row objects.
6097  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6098  * @constructor
6099  * Create a new JsonReader
6100  * @param {Object} meta Metadata configuration options
6101  * @param {Object} recordType Either an Array of field definition objects,
6102  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6103  */
6104 Roo.data.JsonReader = function(meta, recordType){
6105     
6106     meta = meta || {};
6107     // set some defaults:
6108     Roo.applyIf(meta, {
6109         totalProperty: 'total',
6110         successProperty : 'success',
6111         root : 'data',
6112         id : 'id'
6113     });
6114     
6115     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6116 };
6117 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6118     
6119     /**
6120      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6121      * Used by Store query builder to append _requestMeta to params.
6122      * 
6123      */
6124     metaFromRemote : false,
6125     /**
6126      * This method is only used by a DataProxy which has retrieved data from a remote server.
6127      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6128      * @return {Object} data A data block which is used by an Roo.data.Store object as
6129      * a cache of Roo.data.Records.
6130      */
6131     read : function(response){
6132         var json = response.responseText;
6133        
6134         var o = /* eval:var:o */ eval("("+json+")");
6135         if(!o) {
6136             throw {message: "JsonReader.read: Json object not found"};
6137         }
6138         
6139         if(o.metaData){
6140             
6141             delete this.ef;
6142             this.metaFromRemote = true;
6143             this.meta = o.metaData;
6144             this.recordType = Roo.data.Record.create(o.metaData.fields);
6145             this.onMetaChange(this.meta, this.recordType, o);
6146         }
6147         return this.readRecords(o);
6148     },
6149
6150     // private function a store will implement
6151     onMetaChange : function(meta, recordType, o){
6152
6153     },
6154
6155     /**
6156          * @ignore
6157          */
6158     simpleAccess: function(obj, subsc) {
6159         return obj[subsc];
6160     },
6161
6162         /**
6163          * @ignore
6164          */
6165     getJsonAccessor: function(){
6166         var re = /[\[\.]/;
6167         return function(expr) {
6168             try {
6169                 return(re.test(expr))
6170                     ? new Function("obj", "return obj." + expr)
6171                     : function(obj){
6172                         return obj[expr];
6173                     };
6174             } catch(e){}
6175             return Roo.emptyFn;
6176         };
6177     }(),
6178
6179     /**
6180      * Create a data block containing Roo.data.Records from an XML document.
6181      * @param {Object} o An object which contains an Array of row objects in the property specified
6182      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6183      * which contains the total size of the dataset.
6184      * @return {Object} data A data block which is used by an Roo.data.Store object as
6185      * a cache of Roo.data.Records.
6186      */
6187     readRecords : function(o){
6188         /**
6189          * After any data loads, the raw JSON data is available for further custom processing.
6190          * @type Object
6191          */
6192         this.jsonData = o;
6193         var s = this.meta, Record = this.recordType,
6194             f = Record.prototype.fields, fi = f.items, fl = f.length;
6195
6196 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6197         if (!this.ef) {
6198             if(s.totalProperty) {
6199                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6200                 }
6201                 if(s.successProperty) {
6202                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6203                 }
6204                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6205                 if (s.id) {
6206                         var g = this.getJsonAccessor(s.id);
6207                         this.getId = function(rec) {
6208                                 var r = g(rec);
6209                                 return (r === undefined || r === "") ? null : r;
6210                         };
6211                 } else {
6212                         this.getId = function(){return null;};
6213                 }
6214             this.ef = [];
6215             for(var jj = 0; jj < fl; jj++){
6216                 f = fi[jj];
6217                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6218                 this.ef[jj] = this.getJsonAccessor(map);
6219             }
6220         }
6221
6222         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6223         if(s.totalProperty){
6224             var vt = parseInt(this.getTotal(o), 10);
6225             if(!isNaN(vt)){
6226                 totalRecords = vt;
6227             }
6228         }
6229         if(s.successProperty){
6230             var vs = this.getSuccess(o);
6231             if(vs === false || vs === 'false'){
6232                 success = false;
6233             }
6234         }
6235         var records = [];
6236             for(var i = 0; i < c; i++){
6237                     var n = root[i];
6238                 var values = {};
6239                 var id = this.getId(n);
6240                 for(var j = 0; j < fl; j++){
6241                     f = fi[j];
6242                 var v = this.ef[j](n);
6243                 if (!f.convert) {
6244                     Roo.log('missing convert for ' + f.name);
6245                     Roo.log(f);
6246                     continue;
6247                 }
6248                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6249                 }
6250                 var record = new Record(values, id);
6251                 record.json = n;
6252                 records[i] = record;
6253             }
6254             return {
6255                 success : success,
6256                 records : records,
6257                 totalRecords : totalRecords
6258             };
6259     }
6260 });/*
6261  * Based on:
6262  * Ext JS Library 1.1.1
6263  * Copyright(c) 2006-2007, Ext JS, LLC.
6264  *
6265  * Originally Released Under LGPL - original licence link has changed is not relivant.
6266  *
6267  * Fork - LGPL
6268  * <script type="text/javascript">
6269  */
6270
6271 /**
6272  * @class Roo.data.XmlReader
6273  * @extends Roo.data.DataReader
6274  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6275  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6276  * <p>
6277  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6278  * header in the HTTP response must be set to "text/xml".</em>
6279  * <p>
6280  * Example code:
6281  * <pre><code>
6282 var RecordDef = Roo.data.Record.create([
6283    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6284    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6285 ]);
6286 var myReader = new Roo.data.XmlReader({
6287    totalRecords: "results", // The element which contains the total dataset size (optional)
6288    record: "row",           // The repeated element which contains row information
6289    id: "id"                 // The element within the row that provides an ID for the record (optional)
6290 }, RecordDef);
6291 </code></pre>
6292  * <p>
6293  * This would consume an XML file like this:
6294  * <pre><code>
6295 &lt;?xml?>
6296 &lt;dataset>
6297  &lt;results>2&lt;/results>
6298  &lt;row>
6299    &lt;id>1&lt;/id>
6300    &lt;name>Bill&lt;/name>
6301    &lt;occupation>Gardener&lt;/occupation>
6302  &lt;/row>
6303  &lt;row>
6304    &lt;id>2&lt;/id>
6305    &lt;name>Ben&lt;/name>
6306    &lt;occupation>Horticulturalist&lt;/occupation>
6307  &lt;/row>
6308 &lt;/dataset>
6309 </code></pre>
6310  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6311  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6312  * paged from the remote server.
6313  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6314  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6315  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6316  * a record identifier value.
6317  * @constructor
6318  * Create a new XmlReader
6319  * @param {Object} meta Metadata configuration options
6320  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6321  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6322  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6323  */
6324 Roo.data.XmlReader = function(meta, recordType){
6325     meta = meta || {};
6326     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6327 };
6328 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6329     /**
6330      * This method is only used by a DataProxy which has retrieved data from a remote server.
6331          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6332          * to contain a method called 'responseXML' that returns an XML document object.
6333      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6334      * a cache of Roo.data.Records.
6335      */
6336     read : function(response){
6337         var doc = response.responseXML;
6338         if(!doc) {
6339             throw {message: "XmlReader.read: XML Document not available"};
6340         }
6341         return this.readRecords(doc);
6342     },
6343
6344     /**
6345      * Create a data block containing Roo.data.Records from an XML document.
6346          * @param {Object} doc A parsed XML document.
6347      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6348      * a cache of Roo.data.Records.
6349      */
6350     readRecords : function(doc){
6351         /**
6352          * After any data loads/reads, the raw XML Document is available for further custom processing.
6353          * @type XMLDocument
6354          */
6355         this.xmlData = doc;
6356         var root = doc.documentElement || doc;
6357         var q = Roo.DomQuery;
6358         var recordType = this.recordType, fields = recordType.prototype.fields;
6359         var sid = this.meta.id;
6360         var totalRecords = 0, success = true;
6361         if(this.meta.totalRecords){
6362             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6363         }
6364         
6365         if(this.meta.success){
6366             var sv = q.selectValue(this.meta.success, root, true);
6367             success = sv !== false && sv !== 'false';
6368         }
6369         var records = [];
6370         var ns = q.select(this.meta.record, root);
6371         for(var i = 0, len = ns.length; i < len; i++) {
6372                 var n = ns[i];
6373                 var values = {};
6374                 var id = sid ? q.selectValue(sid, n) : undefined;
6375                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6376                     var f = fields.items[j];
6377                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6378                     v = f.convert(v);
6379                     values[f.name] = v;
6380                 }
6381                 var record = new recordType(values, id);
6382                 record.node = n;
6383                 records[records.length] = record;
6384             }
6385
6386             return {
6387                 success : success,
6388                 records : records,
6389                 totalRecords : totalRecords || records.length
6390             };
6391     }
6392 });/*
6393  * Based on:
6394  * Ext JS Library 1.1.1
6395  * Copyright(c) 2006-2007, Ext JS, LLC.
6396  *
6397  * Originally Released Under LGPL - original licence link has changed is not relivant.
6398  *
6399  * Fork - LGPL
6400  * <script type="text/javascript">
6401  */
6402
6403 /**
6404  * @class Roo.data.ArrayReader
6405  * @extends Roo.data.DataReader
6406  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6407  * Each element of that Array represents a row of data fields. The
6408  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6409  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6410  * <p>
6411  * Example code:.
6412  * <pre><code>
6413 var RecordDef = Roo.data.Record.create([
6414     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6415     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6416 ]);
6417 var myReader = new Roo.data.ArrayReader({
6418     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6419 }, RecordDef);
6420 </code></pre>
6421  * <p>
6422  * This would consume an Array like this:
6423  * <pre><code>
6424 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6425   </code></pre>
6426  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6427  * @constructor
6428  * Create a new JsonReader
6429  * @param {Object} meta Metadata configuration options.
6430  * @param {Object} recordType Either an Array of field definition objects
6431  * as specified to {@link Roo.data.Record#create},
6432  * or an {@link Roo.data.Record} object
6433  * created using {@link Roo.data.Record#create}.
6434  */
6435 Roo.data.ArrayReader = function(meta, recordType){
6436     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6437 };
6438
6439 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6440     /**
6441      * Create a data block containing Roo.data.Records from an XML document.
6442      * @param {Object} o An Array of row objects which represents the dataset.
6443      * @return {Object} data A data block which is used by an Roo.data.Store object as
6444      * a cache of Roo.data.Records.
6445      */
6446     readRecords : function(o){
6447         var sid = this.meta ? this.meta.id : null;
6448         var recordType = this.recordType, fields = recordType.prototype.fields;
6449         var records = [];
6450         var root = o;
6451             for(var i = 0; i < root.length; i++){
6452                     var n = root[i];
6453                 var values = {};
6454                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6455                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6456                 var f = fields.items[j];
6457                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6458                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6459                 v = f.convert(v);
6460                 values[f.name] = v;
6461             }
6462                 var record = new recordType(values, id);
6463                 record.json = n;
6464                 records[records.length] = record;
6465             }
6466             return {
6467                 records : records,
6468                 totalRecords : records.length
6469             };
6470     }
6471 });/*
6472  * Based on:
6473  * Ext JS Library 1.1.1
6474  * Copyright(c) 2006-2007, Ext JS, LLC.
6475  *
6476  * Originally Released Under LGPL - original licence link has changed is not relivant.
6477  *
6478  * Fork - LGPL
6479  * <script type="text/javascript">
6480  */
6481
6482
6483 /**
6484  * @class Roo.data.Tree
6485  * @extends Roo.util.Observable
6486  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6487  * in the tree have most standard DOM functionality.
6488  * @constructor
6489  * @param {Node} root (optional) The root node
6490  */
6491 Roo.data.Tree = function(root){
6492    this.nodeHash = {};
6493    /**
6494     * The root node for this tree
6495     * @type Node
6496     */
6497    this.root = null;
6498    if(root){
6499        this.setRootNode(root);
6500    }
6501    this.addEvents({
6502        /**
6503         * @event append
6504         * Fires when a new child node is appended to a node in this tree.
6505         * @param {Tree} tree The owner tree
6506         * @param {Node} parent The parent node
6507         * @param {Node} node The newly appended node
6508         * @param {Number} index The index of the newly appended node
6509         */
6510        "append" : true,
6511        /**
6512         * @event remove
6513         * Fires when a child node is removed from a node in this tree.
6514         * @param {Tree} tree The owner tree
6515         * @param {Node} parent The parent node
6516         * @param {Node} node The child node removed
6517         */
6518        "remove" : true,
6519        /**
6520         * @event move
6521         * Fires when a node is moved to a new location in the tree
6522         * @param {Tree} tree The owner tree
6523         * @param {Node} node The node moved
6524         * @param {Node} oldParent The old parent of this node
6525         * @param {Node} newParent The new parent of this node
6526         * @param {Number} index The index it was moved to
6527         */
6528        "move" : true,
6529        /**
6530         * @event insert
6531         * Fires when a new child node is inserted in a node in this tree.
6532         * @param {Tree} tree The owner tree
6533         * @param {Node} parent The parent node
6534         * @param {Node} node The child node inserted
6535         * @param {Node} refNode The child node the node was inserted before
6536         */
6537        "insert" : true,
6538        /**
6539         * @event beforeappend
6540         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6541         * @param {Tree} tree The owner tree
6542         * @param {Node} parent The parent node
6543         * @param {Node} node The child node to be appended
6544         */
6545        "beforeappend" : true,
6546        /**
6547         * @event beforeremove
6548         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6549         * @param {Tree} tree The owner tree
6550         * @param {Node} parent The parent node
6551         * @param {Node} node The child node to be removed
6552         */
6553        "beforeremove" : true,
6554        /**
6555         * @event beforemove
6556         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6557         * @param {Tree} tree The owner tree
6558         * @param {Node} node The node being moved
6559         * @param {Node} oldParent The parent of the node
6560         * @param {Node} newParent The new parent the node is moving to
6561         * @param {Number} index The index it is being moved to
6562         */
6563        "beforemove" : true,
6564        /**
6565         * @event beforeinsert
6566         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6567         * @param {Tree} tree The owner tree
6568         * @param {Node} parent The parent node
6569         * @param {Node} node The child node to be inserted
6570         * @param {Node} refNode The child node the node is being inserted before
6571         */
6572        "beforeinsert" : true
6573    });
6574
6575     Roo.data.Tree.superclass.constructor.call(this);
6576 };
6577
6578 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6579     pathSeparator: "/",
6580
6581     proxyNodeEvent : function(){
6582         return this.fireEvent.apply(this, arguments);
6583     },
6584
6585     /**
6586      * Returns the root node for this tree.
6587      * @return {Node}
6588      */
6589     getRootNode : function(){
6590         return this.root;
6591     },
6592
6593     /**
6594      * Sets the root node for this tree.
6595      * @param {Node} node
6596      * @return {Node}
6597      */
6598     setRootNode : function(node){
6599         this.root = node;
6600         node.ownerTree = this;
6601         node.isRoot = true;
6602         this.registerNode(node);
6603         return node;
6604     },
6605
6606     /**
6607      * Gets a node in this tree by its id.
6608      * @param {String} id
6609      * @return {Node}
6610      */
6611     getNodeById : function(id){
6612         return this.nodeHash[id];
6613     },
6614
6615     registerNode : function(node){
6616         this.nodeHash[node.id] = node;
6617     },
6618
6619     unregisterNode : function(node){
6620         delete this.nodeHash[node.id];
6621     },
6622
6623     toString : function(){
6624         return "[Tree"+(this.id?" "+this.id:"")+"]";
6625     }
6626 });
6627
6628 /**
6629  * @class Roo.data.Node
6630  * @extends Roo.util.Observable
6631  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6632  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6633  * @constructor
6634  * @param {Object} attributes The attributes/config for the node
6635  */
6636 Roo.data.Node = function(attributes){
6637     /**
6638      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6639      * @type {Object}
6640      */
6641     this.attributes = attributes || {};
6642     this.leaf = this.attributes.leaf;
6643     /**
6644      * The node id. @type String
6645      */
6646     this.id = this.attributes.id;
6647     if(!this.id){
6648         this.id = Roo.id(null, "ynode-");
6649         this.attributes.id = this.id;
6650     }
6651     /**
6652      * All child nodes of this node. @type Array
6653      */
6654     this.childNodes = [];
6655     if(!this.childNodes.indexOf){ // indexOf is a must
6656         this.childNodes.indexOf = function(o){
6657             for(var i = 0, len = this.length; i < len; i++){
6658                 if(this[i] == o) {
6659                     return i;
6660                 }
6661             }
6662             return -1;
6663         };
6664     }
6665     /**
6666      * The parent node for this node. @type Node
6667      */
6668     this.parentNode = null;
6669     /**
6670      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6671      */
6672     this.firstChild = null;
6673     /**
6674      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6675      */
6676     this.lastChild = null;
6677     /**
6678      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6679      */
6680     this.previousSibling = null;
6681     /**
6682      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6683      */
6684     this.nextSibling = null;
6685
6686     this.addEvents({
6687        /**
6688         * @event append
6689         * Fires when a new child node is appended
6690         * @param {Tree} tree The owner tree
6691         * @param {Node} this This node
6692         * @param {Node} node The newly appended node
6693         * @param {Number} index The index of the newly appended node
6694         */
6695        "append" : true,
6696        /**
6697         * @event remove
6698         * Fires when a child node is removed
6699         * @param {Tree} tree The owner tree
6700         * @param {Node} this This node
6701         * @param {Node} node The removed node
6702         */
6703        "remove" : true,
6704        /**
6705         * @event move
6706         * Fires when this node is moved to a new location in the tree
6707         * @param {Tree} tree The owner tree
6708         * @param {Node} this This node
6709         * @param {Node} oldParent The old parent of this node
6710         * @param {Node} newParent The new parent of this node
6711         * @param {Number} index The index it was moved to
6712         */
6713        "move" : true,
6714        /**
6715         * @event insert
6716         * Fires when a new child node is inserted.
6717         * @param {Tree} tree The owner tree
6718         * @param {Node} this This node
6719         * @param {Node} node The child node inserted
6720         * @param {Node} refNode The child node the node was inserted before
6721         */
6722        "insert" : true,
6723        /**
6724         * @event beforeappend
6725         * Fires before a new child is appended, return false to cancel the append.
6726         * @param {Tree} tree The owner tree
6727         * @param {Node} this This node
6728         * @param {Node} node The child node to be appended
6729         */
6730        "beforeappend" : true,
6731        /**
6732         * @event beforeremove
6733         * Fires before a child is removed, return false to cancel the remove.
6734         * @param {Tree} tree The owner tree
6735         * @param {Node} this This node
6736         * @param {Node} node The child node to be removed
6737         */
6738        "beforeremove" : true,
6739        /**
6740         * @event beforemove
6741         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6742         * @param {Tree} tree The owner tree
6743         * @param {Node} this This node
6744         * @param {Node} oldParent The parent of this node
6745         * @param {Node} newParent The new parent this node is moving to
6746         * @param {Number} index The index it is being moved to
6747         */
6748        "beforemove" : true,
6749        /**
6750         * @event beforeinsert
6751         * Fires before a new child is inserted, return false to cancel the insert.
6752         * @param {Tree} tree The owner tree
6753         * @param {Node} this This node
6754         * @param {Node} node The child node to be inserted
6755         * @param {Node} refNode The child node the node is being inserted before
6756         */
6757        "beforeinsert" : true
6758    });
6759     this.listeners = this.attributes.listeners;
6760     Roo.data.Node.superclass.constructor.call(this);
6761 };
6762
6763 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6764     fireEvent : function(evtName){
6765         // first do standard event for this node
6766         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6767             return false;
6768         }
6769         // then bubble it up to the tree if the event wasn't cancelled
6770         var ot = this.getOwnerTree();
6771         if(ot){
6772             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6773                 return false;
6774             }
6775         }
6776         return true;
6777     },
6778
6779     /**
6780      * Returns true if this node is a leaf
6781      * @return {Boolean}
6782      */
6783     isLeaf : function(){
6784         return this.leaf === true;
6785     },
6786
6787     // private
6788     setFirstChild : function(node){
6789         this.firstChild = node;
6790     },
6791
6792     //private
6793     setLastChild : function(node){
6794         this.lastChild = node;
6795     },
6796
6797
6798     /**
6799      * Returns true if this node is the last child of its parent
6800      * @return {Boolean}
6801      */
6802     isLast : function(){
6803        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6804     },
6805
6806     /**
6807      * Returns true if this node is the first child of its parent
6808      * @return {Boolean}
6809      */
6810     isFirst : function(){
6811        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6812     },
6813
6814     hasChildNodes : function(){
6815         return !this.isLeaf() && this.childNodes.length > 0;
6816     },
6817
6818     /**
6819      * Insert node(s) as the last child node of this node.
6820      * @param {Node/Array} node The node or Array of nodes to append
6821      * @return {Node} The appended node if single append, or null if an array was passed
6822      */
6823     appendChild : function(node){
6824         var multi = false;
6825         if(node instanceof Array){
6826             multi = node;
6827         }else if(arguments.length > 1){
6828             multi = arguments;
6829         }
6830         // if passed an array or multiple args do them one by one
6831         if(multi){
6832             for(var i = 0, len = multi.length; i < len; i++) {
6833                 this.appendChild(multi[i]);
6834             }
6835         }else{
6836             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6837                 return false;
6838             }
6839             var index = this.childNodes.length;
6840             var oldParent = node.parentNode;
6841             // it's a move, make sure we move it cleanly
6842             if(oldParent){
6843                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6844                     return false;
6845                 }
6846                 oldParent.removeChild(node);
6847             }
6848             index = this.childNodes.length;
6849             if(index == 0){
6850                 this.setFirstChild(node);
6851             }
6852             this.childNodes.push(node);
6853             node.parentNode = this;
6854             var ps = this.childNodes[index-1];
6855             if(ps){
6856                 node.previousSibling = ps;
6857                 ps.nextSibling = node;
6858             }else{
6859                 node.previousSibling = null;
6860             }
6861             node.nextSibling = null;
6862             this.setLastChild(node);
6863             node.setOwnerTree(this.getOwnerTree());
6864             this.fireEvent("append", this.ownerTree, this, node, index);
6865             if(oldParent){
6866                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6867             }
6868             return node;
6869         }
6870     },
6871
6872     /**
6873      * Removes a child node from this node.
6874      * @param {Node} node The node to remove
6875      * @return {Node} The removed node
6876      */
6877     removeChild : function(node){
6878         var index = this.childNodes.indexOf(node);
6879         if(index == -1){
6880             return false;
6881         }
6882         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6883             return false;
6884         }
6885
6886         // remove it from childNodes collection
6887         this.childNodes.splice(index, 1);
6888
6889         // update siblings
6890         if(node.previousSibling){
6891             node.previousSibling.nextSibling = node.nextSibling;
6892         }
6893         if(node.nextSibling){
6894             node.nextSibling.previousSibling = node.previousSibling;
6895         }
6896
6897         // update child refs
6898         if(this.firstChild == node){
6899             this.setFirstChild(node.nextSibling);
6900         }
6901         if(this.lastChild == node){
6902             this.setLastChild(node.previousSibling);
6903         }
6904
6905         node.setOwnerTree(null);
6906         // clear any references from the node
6907         node.parentNode = null;
6908         node.previousSibling = null;
6909         node.nextSibling = null;
6910         this.fireEvent("remove", this.ownerTree, this, node);
6911         return node;
6912     },
6913
6914     /**
6915      * Inserts the first node before the second node in this nodes childNodes collection.
6916      * @param {Node} node The node to insert
6917      * @param {Node} refNode The node to insert before (if null the node is appended)
6918      * @return {Node} The inserted node
6919      */
6920     insertBefore : function(node, refNode){
6921         if(!refNode){ // like standard Dom, refNode can be null for append
6922             return this.appendChild(node);
6923         }
6924         // nothing to do
6925         if(node == refNode){
6926             return false;
6927         }
6928
6929         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6930             return false;
6931         }
6932         var index = this.childNodes.indexOf(refNode);
6933         var oldParent = node.parentNode;
6934         var refIndex = index;
6935
6936         // when moving internally, indexes will change after remove
6937         if(oldParent == this && this.childNodes.indexOf(node) < index){
6938             refIndex--;
6939         }
6940
6941         // it's a move, make sure we move it cleanly
6942         if(oldParent){
6943             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6944                 return false;
6945             }
6946             oldParent.removeChild(node);
6947         }
6948         if(refIndex == 0){
6949             this.setFirstChild(node);
6950         }
6951         this.childNodes.splice(refIndex, 0, node);
6952         node.parentNode = this;
6953         var ps = this.childNodes[refIndex-1];
6954         if(ps){
6955             node.previousSibling = ps;
6956             ps.nextSibling = node;
6957         }else{
6958             node.previousSibling = null;
6959         }
6960         node.nextSibling = refNode;
6961         refNode.previousSibling = node;
6962         node.setOwnerTree(this.getOwnerTree());
6963         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6964         if(oldParent){
6965             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6966         }
6967         return node;
6968     },
6969
6970     /**
6971      * Returns the child node at the specified index.
6972      * @param {Number} index
6973      * @return {Node}
6974      */
6975     item : function(index){
6976         return this.childNodes[index];
6977     },
6978
6979     /**
6980      * Replaces one child node in this node with another.
6981      * @param {Node} newChild The replacement node
6982      * @param {Node} oldChild The node to replace
6983      * @return {Node} The replaced node
6984      */
6985     replaceChild : function(newChild, oldChild){
6986         this.insertBefore(newChild, oldChild);
6987         this.removeChild(oldChild);
6988         return oldChild;
6989     },
6990
6991     /**
6992      * Returns the index of a child node
6993      * @param {Node} node
6994      * @return {Number} The index of the node or -1 if it was not found
6995      */
6996     indexOf : function(child){
6997         return this.childNodes.indexOf(child);
6998     },
6999
7000     /**
7001      * Returns the tree this node is in.
7002      * @return {Tree}
7003      */
7004     getOwnerTree : function(){
7005         // if it doesn't have one, look for one
7006         if(!this.ownerTree){
7007             var p = this;
7008             while(p){
7009                 if(p.ownerTree){
7010                     this.ownerTree = p.ownerTree;
7011                     break;
7012                 }
7013                 p = p.parentNode;
7014             }
7015         }
7016         return this.ownerTree;
7017     },
7018
7019     /**
7020      * Returns depth of this node (the root node has a depth of 0)
7021      * @return {Number}
7022      */
7023     getDepth : function(){
7024         var depth = 0;
7025         var p = this;
7026         while(p.parentNode){
7027             ++depth;
7028             p = p.parentNode;
7029         }
7030         return depth;
7031     },
7032
7033     // private
7034     setOwnerTree : function(tree){
7035         // if it's move, we need to update everyone
7036         if(tree != this.ownerTree){
7037             if(this.ownerTree){
7038                 this.ownerTree.unregisterNode(this);
7039             }
7040             this.ownerTree = tree;
7041             var cs = this.childNodes;
7042             for(var i = 0, len = cs.length; i < len; i++) {
7043                 cs[i].setOwnerTree(tree);
7044             }
7045             if(tree){
7046                 tree.registerNode(this);
7047             }
7048         }
7049     },
7050
7051     /**
7052      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7053      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7054      * @return {String} The path
7055      */
7056     getPath : function(attr){
7057         attr = attr || "id";
7058         var p = this.parentNode;
7059         var b = [this.attributes[attr]];
7060         while(p){
7061             b.unshift(p.attributes[attr]);
7062             p = p.parentNode;
7063         }
7064         var sep = this.getOwnerTree().pathSeparator;
7065         return sep + b.join(sep);
7066     },
7067
7068     /**
7069      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7070      * function call will be the scope provided or the current node. The arguments to the function
7071      * will be the args provided or the current node. If the function returns false at any point,
7072      * the bubble is stopped.
7073      * @param {Function} fn The function to call
7074      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7075      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7076      */
7077     bubble : function(fn, scope, args){
7078         var p = this;
7079         while(p){
7080             if(fn.call(scope || p, args || p) === false){
7081                 break;
7082             }
7083             p = p.parentNode;
7084         }
7085     },
7086
7087     /**
7088      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7089      * function call will be the scope provided or the current node. The arguments to the function
7090      * will be the args provided or the current node. If the function returns false at any point,
7091      * the cascade is stopped on that branch.
7092      * @param {Function} fn The function to call
7093      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7094      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7095      */
7096     cascade : function(fn, scope, args){
7097         if(fn.call(scope || this, args || this) !== false){
7098             var cs = this.childNodes;
7099             for(var i = 0, len = cs.length; i < len; i++) {
7100                 cs[i].cascade(fn, scope, args);
7101             }
7102         }
7103     },
7104
7105     /**
7106      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7107      * function call will be the scope provided or the current node. The arguments to the function
7108      * will be the args provided or the current node. If the function returns false at any point,
7109      * the iteration stops.
7110      * @param {Function} fn The function to call
7111      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7112      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7113      */
7114     eachChild : function(fn, scope, args){
7115         var cs = this.childNodes;
7116         for(var i = 0, len = cs.length; i < len; i++) {
7117                 if(fn.call(scope || this, args || cs[i]) === false){
7118                     break;
7119                 }
7120         }
7121     },
7122
7123     /**
7124      * Finds the first child that has the attribute with the specified value.
7125      * @param {String} attribute The attribute name
7126      * @param {Mixed} value The value to search for
7127      * @return {Node} The found child or null if none was found
7128      */
7129     findChild : function(attribute, value){
7130         var cs = this.childNodes;
7131         for(var i = 0, len = cs.length; i < len; i++) {
7132                 if(cs[i].attributes[attribute] == value){
7133                     return cs[i];
7134                 }
7135         }
7136         return null;
7137     },
7138
7139     /**
7140      * Finds the first child by a custom function. The child matches if the function passed
7141      * returns true.
7142      * @param {Function} fn
7143      * @param {Object} scope (optional)
7144      * @return {Node} The found child or null if none was found
7145      */
7146     findChildBy : function(fn, scope){
7147         var cs = this.childNodes;
7148         for(var i = 0, len = cs.length; i < len; i++) {
7149                 if(fn.call(scope||cs[i], cs[i]) === true){
7150                     return cs[i];
7151                 }
7152         }
7153         return null;
7154     },
7155
7156     /**
7157      * Sorts this nodes children using the supplied sort function
7158      * @param {Function} fn
7159      * @param {Object} scope (optional)
7160      */
7161     sort : function(fn, scope){
7162         var cs = this.childNodes;
7163         var len = cs.length;
7164         if(len > 0){
7165             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7166             cs.sort(sortFn);
7167             for(var i = 0; i < len; i++){
7168                 var n = cs[i];
7169                 n.previousSibling = cs[i-1];
7170                 n.nextSibling = cs[i+1];
7171                 if(i == 0){
7172                     this.setFirstChild(n);
7173                 }
7174                 if(i == len-1){
7175                     this.setLastChild(n);
7176                 }
7177             }
7178         }
7179     },
7180
7181     /**
7182      * Returns true if this node is an ancestor (at any point) of the passed node.
7183      * @param {Node} node
7184      * @return {Boolean}
7185      */
7186     contains : function(node){
7187         return node.isAncestor(this);
7188     },
7189
7190     /**
7191      * Returns true if the passed node is an ancestor (at any point) of this node.
7192      * @param {Node} node
7193      * @return {Boolean}
7194      */
7195     isAncestor : function(node){
7196         var p = this.parentNode;
7197         while(p){
7198             if(p == node){
7199                 return true;
7200             }
7201             p = p.parentNode;
7202         }
7203         return false;
7204     },
7205
7206     toString : function(){
7207         return "[Node"+(this.id?" "+this.id:"")+"]";
7208     }
7209 });/*
7210  * Based on:
7211  * Ext JS Library 1.1.1
7212  * Copyright(c) 2006-2007, Ext JS, LLC.
7213  *
7214  * Originally Released Under LGPL - original licence link has changed is not relivant.
7215  *
7216  * Fork - LGPL
7217  * <script type="text/javascript">
7218  */
7219  
7220
7221 /**
7222  * @class Roo.ComponentMgr
7223  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7224  * @singleton
7225  */
7226 Roo.ComponentMgr = function(){
7227     var all = new Roo.util.MixedCollection();
7228
7229     return {
7230         /**
7231          * Registers a component.
7232          * @param {Roo.Component} c The component
7233          */
7234         register : function(c){
7235             all.add(c);
7236         },
7237
7238         /**
7239          * Unregisters a component.
7240          * @param {Roo.Component} c The component
7241          */
7242         unregister : function(c){
7243             all.remove(c);
7244         },
7245
7246         /**
7247          * Returns a component by id
7248          * @param {String} id The component id
7249          */
7250         get : function(id){
7251             return all.get(id);
7252         },
7253
7254         /**
7255          * Registers a function that will be called when a specified component is added to ComponentMgr
7256          * @param {String} id The component id
7257          * @param {Funtction} fn The callback function
7258          * @param {Object} scope The scope of the callback
7259          */
7260         onAvailable : function(id, fn, scope){
7261             all.on("add", function(index, o){
7262                 if(o.id == id){
7263                     fn.call(scope || o, o);
7264                     all.un("add", fn, scope);
7265                 }
7266             });
7267         }
7268     };
7269 }();/*
7270  * Based on:
7271  * Ext JS Library 1.1.1
7272  * Copyright(c) 2006-2007, Ext JS, LLC.
7273  *
7274  * Originally Released Under LGPL - original licence link has changed is not relivant.
7275  *
7276  * Fork - LGPL
7277  * <script type="text/javascript">
7278  */
7279  
7280 /**
7281  * @class Roo.Component
7282  * @extends Roo.util.Observable
7283  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7284  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7285  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7286  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7287  * All visual components (widgets) that require rendering into a layout should subclass Component.
7288  * @constructor
7289  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7290  * 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
7291  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7292  */
7293 Roo.Component = function(config){
7294     config = config || {};
7295     if(config.tagName || config.dom || typeof config == "string"){ // element object
7296         config = {el: config, id: config.id || config};
7297     }
7298     this.initialConfig = config;
7299
7300     Roo.apply(this, config);
7301     this.addEvents({
7302         /**
7303          * @event disable
7304          * Fires after the component is disabled.
7305              * @param {Roo.Component} this
7306              */
7307         disable : true,
7308         /**
7309          * @event enable
7310          * Fires after the component is enabled.
7311              * @param {Roo.Component} this
7312              */
7313         enable : true,
7314         /**
7315          * @event beforeshow
7316          * Fires before the component is shown.  Return false to stop the show.
7317              * @param {Roo.Component} this
7318              */
7319         beforeshow : true,
7320         /**
7321          * @event show
7322          * Fires after the component is shown.
7323              * @param {Roo.Component} this
7324              */
7325         show : true,
7326         /**
7327          * @event beforehide
7328          * Fires before the component is hidden. Return false to stop the hide.
7329              * @param {Roo.Component} this
7330              */
7331         beforehide : true,
7332         /**
7333          * @event hide
7334          * Fires after the component is hidden.
7335              * @param {Roo.Component} this
7336              */
7337         hide : true,
7338         /**
7339          * @event beforerender
7340          * Fires before the component is rendered. Return false to stop the render.
7341              * @param {Roo.Component} this
7342              */
7343         beforerender : true,
7344         /**
7345          * @event render
7346          * Fires after the component is rendered.
7347              * @param {Roo.Component} this
7348              */
7349         render : true,
7350         /**
7351          * @event beforedestroy
7352          * Fires before the component is destroyed. Return false to stop the destroy.
7353              * @param {Roo.Component} this
7354              */
7355         beforedestroy : true,
7356         /**
7357          * @event destroy
7358          * Fires after the component is destroyed.
7359              * @param {Roo.Component} this
7360              */
7361         destroy : true
7362     });
7363     if(!this.id){
7364         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7365     }
7366     Roo.ComponentMgr.register(this);
7367     Roo.Component.superclass.constructor.call(this);
7368     this.initComponent();
7369     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7370         this.render(this.renderTo);
7371         delete this.renderTo;
7372     }
7373 };
7374
7375 // private
7376 Roo.Component.AUTO_ID = 1000;
7377
7378 Roo.extend(Roo.Component, Roo.util.Observable, {
7379     /**
7380      * @property {Boolean} hidden
7381      * true if this component is hidden. Read-only.
7382      */
7383     hidden : false,
7384     /**
7385      * true if this component is disabled. Read-only.
7386      */
7387     disabled : false,
7388     /**
7389      * true if this component has been rendered. Read-only.
7390      */
7391     rendered : false,
7392     
7393     /** @cfg {String} disableClass
7394      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7395      */
7396     disabledClass : "x-item-disabled",
7397         /** @cfg {Boolean} allowDomMove
7398          * Whether the component can move the Dom node when rendering (defaults to true).
7399          */
7400     allowDomMove : true,
7401     /** @cfg {String} hideMode
7402      * How this component should hidden. Supported values are
7403      * "visibility" (css visibility), "offsets" (negative offset position) and
7404      * "display" (css display) - defaults to "display".
7405      */
7406     hideMode: 'display',
7407
7408     // private
7409     ctype : "Roo.Component",
7410
7411     /** @cfg {String} actionMode 
7412      * which property holds the element that used for  hide() / show() / disable() / enable()
7413      * default is 'el' 
7414      */
7415     actionMode : "el",
7416
7417     // private
7418     getActionEl : function(){
7419         return this[this.actionMode];
7420     },
7421
7422     initComponent : Roo.emptyFn,
7423     /**
7424      * If this is a lazy rendering component, render it to its container element.
7425      * @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.
7426      */
7427     render : function(container, position){
7428         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7429             if(!container && this.el){
7430                 this.el = Roo.get(this.el);
7431                 container = this.el.dom.parentNode;
7432                 this.allowDomMove = false;
7433             }
7434             this.container = Roo.get(container);
7435             this.rendered = true;
7436             if(position !== undefined){
7437                 if(typeof position == 'number'){
7438                     position = this.container.dom.childNodes[position];
7439                 }else{
7440                     position = Roo.getDom(position);
7441                 }
7442             }
7443             this.onRender(this.container, position || null);
7444             if(this.cls){
7445                 this.el.addClass(this.cls);
7446                 delete this.cls;
7447             }
7448             if(this.style){
7449                 this.el.applyStyles(this.style);
7450                 delete this.style;
7451             }
7452             this.fireEvent("render", this);
7453             this.afterRender(this.container);
7454             if(this.hidden){
7455                 this.hide();
7456             }
7457             if(this.disabled){
7458                 this.disable();
7459             }
7460         }
7461         return this;
7462     },
7463
7464     // private
7465     // default function is not really useful
7466     onRender : function(ct, position){
7467         if(this.el){
7468             this.el = Roo.get(this.el);
7469             if(this.allowDomMove !== false){
7470                 ct.dom.insertBefore(this.el.dom, position);
7471             }
7472         }
7473     },
7474
7475     // private
7476     getAutoCreate : function(){
7477         var cfg = typeof this.autoCreate == "object" ?
7478                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7479         if(this.id && !cfg.id){
7480             cfg.id = this.id;
7481         }
7482         return cfg;
7483     },
7484
7485     // private
7486     afterRender : Roo.emptyFn,
7487
7488     /**
7489      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7490      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7491      */
7492     destroy : function(){
7493         if(this.fireEvent("beforedestroy", this) !== false){
7494             this.purgeListeners();
7495             this.beforeDestroy();
7496             if(this.rendered){
7497                 this.el.removeAllListeners();
7498                 this.el.remove();
7499                 if(this.actionMode == "container"){
7500                     this.container.remove();
7501                 }
7502             }
7503             this.onDestroy();
7504             Roo.ComponentMgr.unregister(this);
7505             this.fireEvent("destroy", this);
7506         }
7507     },
7508
7509         // private
7510     beforeDestroy : function(){
7511
7512     },
7513
7514         // private
7515         onDestroy : function(){
7516
7517     },
7518
7519     /**
7520      * Returns the underlying {@link Roo.Element}.
7521      * @return {Roo.Element} The element
7522      */
7523     getEl : function(){
7524         return this.el;
7525     },
7526
7527     /**
7528      * Returns the id of this component.
7529      * @return {String}
7530      */
7531     getId : function(){
7532         return this.id;
7533     },
7534
7535     /**
7536      * Try to focus this component.
7537      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7538      * @return {Roo.Component} this
7539      */
7540     focus : function(selectText){
7541         if(this.rendered){
7542             this.el.focus();
7543             if(selectText === true){
7544                 this.el.dom.select();
7545             }
7546         }
7547         return this;
7548     },
7549
7550     // private
7551     blur : function(){
7552         if(this.rendered){
7553             this.el.blur();
7554         }
7555         return this;
7556     },
7557
7558     /**
7559      * Disable this component.
7560      * @return {Roo.Component} this
7561      */
7562     disable : function(){
7563         if(this.rendered){
7564             this.onDisable();
7565         }
7566         this.disabled = true;
7567         this.fireEvent("disable", this);
7568         return this;
7569     },
7570
7571         // private
7572     onDisable : function(){
7573         this.getActionEl().addClass(this.disabledClass);
7574         this.el.dom.disabled = true;
7575     },
7576
7577     /**
7578      * Enable this component.
7579      * @return {Roo.Component} this
7580      */
7581     enable : function(){
7582         if(this.rendered){
7583             this.onEnable();
7584         }
7585         this.disabled = false;
7586         this.fireEvent("enable", this);
7587         return this;
7588     },
7589
7590         // private
7591     onEnable : function(){
7592         this.getActionEl().removeClass(this.disabledClass);
7593         this.el.dom.disabled = false;
7594     },
7595
7596     /**
7597      * Convenience function for setting disabled/enabled by boolean.
7598      * @param {Boolean} disabled
7599      */
7600     setDisabled : function(disabled){
7601         this[disabled ? "disable" : "enable"]();
7602     },
7603
7604     /**
7605      * Show this component.
7606      * @return {Roo.Component} this
7607      */
7608     show: function(){
7609         if(this.fireEvent("beforeshow", this) !== false){
7610             this.hidden = false;
7611             if(this.rendered){
7612                 this.onShow();
7613             }
7614             this.fireEvent("show", this);
7615         }
7616         return this;
7617     },
7618
7619     // private
7620     onShow : function(){
7621         var ae = this.getActionEl();
7622         if(this.hideMode == 'visibility'){
7623             ae.dom.style.visibility = "visible";
7624         }else if(this.hideMode == 'offsets'){
7625             ae.removeClass('x-hidden');
7626         }else{
7627             ae.dom.style.display = "";
7628         }
7629     },
7630
7631     /**
7632      * Hide this component.
7633      * @return {Roo.Component} this
7634      */
7635     hide: function(){
7636         if(this.fireEvent("beforehide", this) !== false){
7637             this.hidden = true;
7638             if(this.rendered){
7639                 this.onHide();
7640             }
7641             this.fireEvent("hide", this);
7642         }
7643         return this;
7644     },
7645
7646     // private
7647     onHide : function(){
7648         var ae = this.getActionEl();
7649         if(this.hideMode == 'visibility'){
7650             ae.dom.style.visibility = "hidden";
7651         }else if(this.hideMode == 'offsets'){
7652             ae.addClass('x-hidden');
7653         }else{
7654             ae.dom.style.display = "none";
7655         }
7656     },
7657
7658     /**
7659      * Convenience function to hide or show this component by boolean.
7660      * @param {Boolean} visible True to show, false to hide
7661      * @return {Roo.Component} this
7662      */
7663     setVisible: function(visible){
7664         if(visible) {
7665             this.show();
7666         }else{
7667             this.hide();
7668         }
7669         return this;
7670     },
7671
7672     /**
7673      * Returns true if this component is visible.
7674      */
7675     isVisible : function(){
7676         return this.getActionEl().isVisible();
7677     },
7678
7679     cloneConfig : function(overrides){
7680         overrides = overrides || {};
7681         var id = overrides.id || Roo.id();
7682         var cfg = Roo.applyIf(overrides, this.initialConfig);
7683         cfg.id = id; // prevent dup id
7684         return new this.constructor(cfg);
7685     }
7686 });/*
7687  * Based on:
7688  * Ext JS Library 1.1.1
7689  * Copyright(c) 2006-2007, Ext JS, LLC.
7690  *
7691  * Originally Released Under LGPL - original licence link has changed is not relivant.
7692  *
7693  * Fork - LGPL
7694  * <script type="text/javascript">
7695  */
7696  (function(){ 
7697 /**
7698  * @class Roo.Layer
7699  * @extends Roo.Element
7700  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7701  * automatic maintaining of shadow/shim positions.
7702  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7703  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7704  * you can pass a string with a CSS class name. False turns off the shadow.
7705  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7706  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7707  * @cfg {String} cls CSS class to add to the element
7708  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7709  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7710  * @constructor
7711  * @param {Object} config An object with config options.
7712  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7713  */
7714
7715 Roo.Layer = function(config, existingEl){
7716     config = config || {};
7717     var dh = Roo.DomHelper;
7718     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7719     if(existingEl){
7720         this.dom = Roo.getDom(existingEl);
7721     }
7722     if(!this.dom){
7723         var o = config.dh || {tag: "div", cls: "x-layer"};
7724         this.dom = dh.append(pel, o);
7725     }
7726     if(config.cls){
7727         this.addClass(config.cls);
7728     }
7729     this.constrain = config.constrain !== false;
7730     this.visibilityMode = Roo.Element.VISIBILITY;
7731     if(config.id){
7732         this.id = this.dom.id = config.id;
7733     }else{
7734         this.id = Roo.id(this.dom);
7735     }
7736     this.zindex = config.zindex || this.getZIndex();
7737     this.position("absolute", this.zindex);
7738     if(config.shadow){
7739         this.shadowOffset = config.shadowOffset || 4;
7740         this.shadow = new Roo.Shadow({
7741             offset : this.shadowOffset,
7742             mode : config.shadow
7743         });
7744     }else{
7745         this.shadowOffset = 0;
7746     }
7747     this.useShim = config.shim !== false && Roo.useShims;
7748     this.useDisplay = config.useDisplay;
7749     this.hide();
7750 };
7751
7752 var supr = Roo.Element.prototype;
7753
7754 // shims are shared among layer to keep from having 100 iframes
7755 var shims = [];
7756
7757 Roo.extend(Roo.Layer, Roo.Element, {
7758
7759     getZIndex : function(){
7760         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7761     },
7762
7763     getShim : function(){
7764         if(!this.useShim){
7765             return null;
7766         }
7767         if(this.shim){
7768             return this.shim;
7769         }
7770         var shim = shims.shift();
7771         if(!shim){
7772             shim = this.createShim();
7773             shim.enableDisplayMode('block');
7774             shim.dom.style.display = 'none';
7775             shim.dom.style.visibility = 'visible';
7776         }
7777         var pn = this.dom.parentNode;
7778         if(shim.dom.parentNode != pn){
7779             pn.insertBefore(shim.dom, this.dom);
7780         }
7781         shim.setStyle('z-index', this.getZIndex()-2);
7782         this.shim = shim;
7783         return shim;
7784     },
7785
7786     hideShim : function(){
7787         if(this.shim){
7788             this.shim.setDisplayed(false);
7789             shims.push(this.shim);
7790             delete this.shim;
7791         }
7792     },
7793
7794     disableShadow : function(){
7795         if(this.shadow){
7796             this.shadowDisabled = true;
7797             this.shadow.hide();
7798             this.lastShadowOffset = this.shadowOffset;
7799             this.shadowOffset = 0;
7800         }
7801     },
7802
7803     enableShadow : function(show){
7804         if(this.shadow){
7805             this.shadowDisabled = false;
7806             this.shadowOffset = this.lastShadowOffset;
7807             delete this.lastShadowOffset;
7808             if(show){
7809                 this.sync(true);
7810             }
7811         }
7812     },
7813
7814     // private
7815     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7816     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7817     sync : function(doShow){
7818         var sw = this.shadow;
7819         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7820             var sh = this.getShim();
7821
7822             var w = this.getWidth(),
7823                 h = this.getHeight();
7824
7825             var l = this.getLeft(true),
7826                 t = this.getTop(true);
7827
7828             if(sw && !this.shadowDisabled){
7829                 if(doShow && !sw.isVisible()){
7830                     sw.show(this);
7831                 }else{
7832                     sw.realign(l, t, w, h);
7833                 }
7834                 if(sh){
7835                     if(doShow){
7836                        sh.show();
7837                     }
7838                     // fit the shim behind the shadow, so it is shimmed too
7839                     var a = sw.adjusts, s = sh.dom.style;
7840                     s.left = (Math.min(l, l+a.l))+"px";
7841                     s.top = (Math.min(t, t+a.t))+"px";
7842                     s.width = (w+a.w)+"px";
7843                     s.height = (h+a.h)+"px";
7844                 }
7845             }else if(sh){
7846                 if(doShow){
7847                    sh.show();
7848                 }
7849                 sh.setSize(w, h);
7850                 sh.setLeftTop(l, t);
7851             }
7852             
7853         }
7854     },
7855
7856     // private
7857     destroy : function(){
7858         this.hideShim();
7859         if(this.shadow){
7860             this.shadow.hide();
7861         }
7862         this.removeAllListeners();
7863         var pn = this.dom.parentNode;
7864         if(pn){
7865             pn.removeChild(this.dom);
7866         }
7867         Roo.Element.uncache(this.id);
7868     },
7869
7870     remove : function(){
7871         this.destroy();
7872     },
7873
7874     // private
7875     beginUpdate : function(){
7876         this.updating = true;
7877     },
7878
7879     // private
7880     endUpdate : function(){
7881         this.updating = false;
7882         this.sync(true);
7883     },
7884
7885     // private
7886     hideUnders : function(negOffset){
7887         if(this.shadow){
7888             this.shadow.hide();
7889         }
7890         this.hideShim();
7891     },
7892
7893     // private
7894     constrainXY : function(){
7895         if(this.constrain){
7896             var vw = Roo.lib.Dom.getViewWidth(),
7897                 vh = Roo.lib.Dom.getViewHeight();
7898             var s = Roo.get(document).getScroll();
7899
7900             var xy = this.getXY();
7901             var x = xy[0], y = xy[1];   
7902             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7903             // only move it if it needs it
7904             var moved = false;
7905             // first validate right/bottom
7906             if((x + w) > vw+s.left){
7907                 x = vw - w - this.shadowOffset;
7908                 moved = true;
7909             }
7910             if((y + h) > vh+s.top){
7911                 y = vh - h - this.shadowOffset;
7912                 moved = true;
7913             }
7914             // then make sure top/left isn't negative
7915             if(x < s.left){
7916                 x = s.left;
7917                 moved = true;
7918             }
7919             if(y < s.top){
7920                 y = s.top;
7921                 moved = true;
7922             }
7923             if(moved){
7924                 if(this.avoidY){
7925                     var ay = this.avoidY;
7926                     if(y <= ay && (y+h) >= ay){
7927                         y = ay-h-5;   
7928                     }
7929                 }
7930                 xy = [x, y];
7931                 this.storeXY(xy);
7932                 supr.setXY.call(this, xy);
7933                 this.sync();
7934             }
7935         }
7936     },
7937
7938     isVisible : function(){
7939         return this.visible;    
7940     },
7941
7942     // private
7943     showAction : function(){
7944         this.visible = true; // track visibility to prevent getStyle calls
7945         if(this.useDisplay === true){
7946             this.setDisplayed("");
7947         }else if(this.lastXY){
7948             supr.setXY.call(this, this.lastXY);
7949         }else if(this.lastLT){
7950             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7951         }
7952     },
7953
7954     // private
7955     hideAction : function(){
7956         this.visible = false;
7957         if(this.useDisplay === true){
7958             this.setDisplayed(false);
7959         }else{
7960             this.setLeftTop(-10000,-10000);
7961         }
7962     },
7963
7964     // overridden Element method
7965     setVisible : function(v, a, d, c, e){
7966         if(v){
7967             this.showAction();
7968         }
7969         if(a && v){
7970             var cb = function(){
7971                 this.sync(true);
7972                 if(c){
7973                     c();
7974                 }
7975             }.createDelegate(this);
7976             supr.setVisible.call(this, true, true, d, cb, e);
7977         }else{
7978             if(!v){
7979                 this.hideUnders(true);
7980             }
7981             var cb = c;
7982             if(a){
7983                 cb = function(){
7984                     this.hideAction();
7985                     if(c){
7986                         c();
7987                     }
7988                 }.createDelegate(this);
7989             }
7990             supr.setVisible.call(this, v, a, d, cb, e);
7991             if(v){
7992                 this.sync(true);
7993             }else if(!a){
7994                 this.hideAction();
7995             }
7996         }
7997     },
7998
7999     storeXY : function(xy){
8000         delete this.lastLT;
8001         this.lastXY = xy;
8002     },
8003
8004     storeLeftTop : function(left, top){
8005         delete this.lastXY;
8006         this.lastLT = [left, top];
8007     },
8008
8009     // private
8010     beforeFx : function(){
8011         this.beforeAction();
8012         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8013     },
8014
8015     // private
8016     afterFx : function(){
8017         Roo.Layer.superclass.afterFx.apply(this, arguments);
8018         this.sync(this.isVisible());
8019     },
8020
8021     // private
8022     beforeAction : function(){
8023         if(!this.updating && this.shadow){
8024             this.shadow.hide();
8025         }
8026     },
8027
8028     // overridden Element method
8029     setLeft : function(left){
8030         this.storeLeftTop(left, this.getTop(true));
8031         supr.setLeft.apply(this, arguments);
8032         this.sync();
8033     },
8034
8035     setTop : function(top){
8036         this.storeLeftTop(this.getLeft(true), top);
8037         supr.setTop.apply(this, arguments);
8038         this.sync();
8039     },
8040
8041     setLeftTop : function(left, top){
8042         this.storeLeftTop(left, top);
8043         supr.setLeftTop.apply(this, arguments);
8044         this.sync();
8045     },
8046
8047     setXY : function(xy, a, d, c, e){
8048         this.fixDisplay();
8049         this.beforeAction();
8050         this.storeXY(xy);
8051         var cb = this.createCB(c);
8052         supr.setXY.call(this, xy, a, d, cb, e);
8053         if(!a){
8054             cb();
8055         }
8056     },
8057
8058     // private
8059     createCB : function(c){
8060         var el = this;
8061         return function(){
8062             el.constrainXY();
8063             el.sync(true);
8064             if(c){
8065                 c();
8066             }
8067         };
8068     },
8069
8070     // overridden Element method
8071     setX : function(x, a, d, c, e){
8072         this.setXY([x, this.getY()], a, d, c, e);
8073     },
8074
8075     // overridden Element method
8076     setY : function(y, a, d, c, e){
8077         this.setXY([this.getX(), y], a, d, c, e);
8078     },
8079
8080     // overridden Element method
8081     setSize : function(w, h, a, d, c, e){
8082         this.beforeAction();
8083         var cb = this.createCB(c);
8084         supr.setSize.call(this, w, h, a, d, cb, e);
8085         if(!a){
8086             cb();
8087         }
8088     },
8089
8090     // overridden Element method
8091     setWidth : function(w, a, d, c, e){
8092         this.beforeAction();
8093         var cb = this.createCB(c);
8094         supr.setWidth.call(this, w, a, d, cb, e);
8095         if(!a){
8096             cb();
8097         }
8098     },
8099
8100     // overridden Element method
8101     setHeight : function(h, a, d, c, e){
8102         this.beforeAction();
8103         var cb = this.createCB(c);
8104         supr.setHeight.call(this, h, a, d, cb, e);
8105         if(!a){
8106             cb();
8107         }
8108     },
8109
8110     // overridden Element method
8111     setBounds : function(x, y, w, h, a, d, c, e){
8112         this.beforeAction();
8113         var cb = this.createCB(c);
8114         if(!a){
8115             this.storeXY([x, y]);
8116             supr.setXY.call(this, [x, y]);
8117             supr.setSize.call(this, w, h, a, d, cb, e);
8118             cb();
8119         }else{
8120             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8121         }
8122         return this;
8123     },
8124     
8125     /**
8126      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8127      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8128      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8129      * @param {Number} zindex The new z-index to set
8130      * @return {this} The Layer
8131      */
8132     setZIndex : function(zindex){
8133         this.zindex = zindex;
8134         this.setStyle("z-index", zindex + 2);
8135         if(this.shadow){
8136             this.shadow.setZIndex(zindex + 1);
8137         }
8138         if(this.shim){
8139             this.shim.setStyle("z-index", zindex);
8140         }
8141     }
8142 });
8143 })();/*
8144  * Based on:
8145  * Ext JS Library 1.1.1
8146  * Copyright(c) 2006-2007, Ext JS, LLC.
8147  *
8148  * Originally Released Under LGPL - original licence link has changed is not relivant.
8149  *
8150  * Fork - LGPL
8151  * <script type="text/javascript">
8152  */
8153
8154
8155 /**
8156  * @class Roo.Shadow
8157  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8158  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8159  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8160  * @constructor
8161  * Create a new Shadow
8162  * @param {Object} config The config object
8163  */
8164 Roo.Shadow = function(config){
8165     Roo.apply(this, config);
8166     if(typeof this.mode != "string"){
8167         this.mode = this.defaultMode;
8168     }
8169     var o = this.offset, a = {h: 0};
8170     var rad = Math.floor(this.offset/2);
8171     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8172         case "drop":
8173             a.w = 0;
8174             a.l = a.t = o;
8175             a.t -= 1;
8176             if(Roo.isIE){
8177                 a.l -= this.offset + rad;
8178                 a.t -= this.offset + rad;
8179                 a.w -= rad;
8180                 a.h -= rad;
8181                 a.t += 1;
8182             }
8183         break;
8184         case "sides":
8185             a.w = (o*2);
8186             a.l = -o;
8187             a.t = o-1;
8188             if(Roo.isIE){
8189                 a.l -= (this.offset - rad);
8190                 a.t -= this.offset + rad;
8191                 a.l += 1;
8192                 a.w -= (this.offset - rad)*2;
8193                 a.w -= rad + 1;
8194                 a.h -= 1;
8195             }
8196         break;
8197         case "frame":
8198             a.w = a.h = (o*2);
8199             a.l = a.t = -o;
8200             a.t += 1;
8201             a.h -= 2;
8202             if(Roo.isIE){
8203                 a.l -= (this.offset - rad);
8204                 a.t -= (this.offset - rad);
8205                 a.l += 1;
8206                 a.w -= (this.offset + rad + 1);
8207                 a.h -= (this.offset + rad);
8208                 a.h += 1;
8209             }
8210         break;
8211     };
8212
8213     this.adjusts = a;
8214 };
8215
8216 Roo.Shadow.prototype = {
8217     /**
8218      * @cfg {String} mode
8219      * The shadow display mode.  Supports the following options:<br />
8220      * sides: Shadow displays on both sides and bottom only<br />
8221      * frame: Shadow displays equally on all four sides<br />
8222      * drop: Traditional bottom-right drop shadow (default)
8223      */
8224     /**
8225      * @cfg {String} offset
8226      * The number of pixels to offset the shadow from the element (defaults to 4)
8227      */
8228     offset: 4,
8229
8230     // private
8231     defaultMode: "drop",
8232
8233     /**
8234      * Displays the shadow under the target element
8235      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8236      */
8237     show : function(target){
8238         target = Roo.get(target);
8239         if(!this.el){
8240             this.el = Roo.Shadow.Pool.pull();
8241             if(this.el.dom.nextSibling != target.dom){
8242                 this.el.insertBefore(target);
8243             }
8244         }
8245         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8246         if(Roo.isIE){
8247             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8248         }
8249         this.realign(
8250             target.getLeft(true),
8251             target.getTop(true),
8252             target.getWidth(),
8253             target.getHeight()
8254         );
8255         this.el.dom.style.display = "block";
8256     },
8257
8258     /**
8259      * Returns true if the shadow is visible, else false
8260      */
8261     isVisible : function(){
8262         return this.el ? true : false;  
8263     },
8264
8265     /**
8266      * Direct alignment when values are already available. Show must be called at least once before
8267      * calling this method to ensure it is initialized.
8268      * @param {Number} left The target element left position
8269      * @param {Number} top The target element top position
8270      * @param {Number} width The target element width
8271      * @param {Number} height The target element height
8272      */
8273     realign : function(l, t, w, h){
8274         if(!this.el){
8275             return;
8276         }
8277         var a = this.adjusts, d = this.el.dom, s = d.style;
8278         var iea = 0;
8279         s.left = (l+a.l)+"px";
8280         s.top = (t+a.t)+"px";
8281         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8282  
8283         if(s.width != sws || s.height != shs){
8284             s.width = sws;
8285             s.height = shs;
8286             if(!Roo.isIE){
8287                 var cn = d.childNodes;
8288                 var sww = Math.max(0, (sw-12))+"px";
8289                 cn[0].childNodes[1].style.width = sww;
8290                 cn[1].childNodes[1].style.width = sww;
8291                 cn[2].childNodes[1].style.width = sww;
8292                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8293             }
8294         }
8295     },
8296
8297     /**
8298      * Hides this shadow
8299      */
8300     hide : function(){
8301         if(this.el){
8302             this.el.dom.style.display = "none";
8303             Roo.Shadow.Pool.push(this.el);
8304             delete this.el;
8305         }
8306     },
8307
8308     /**
8309      * Adjust the z-index of this shadow
8310      * @param {Number} zindex The new z-index
8311      */
8312     setZIndex : function(z){
8313         this.zIndex = z;
8314         if(this.el){
8315             this.el.setStyle("z-index", z);
8316         }
8317     }
8318 };
8319
8320 // Private utility class that manages the internal Shadow cache
8321 Roo.Shadow.Pool = function(){
8322     var p = [];
8323     var markup = Roo.isIE ?
8324                  '<div class="x-ie-shadow"></div>' :
8325                  '<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>';
8326     return {
8327         pull : function(){
8328             var sh = p.shift();
8329             if(!sh){
8330                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8331                 sh.autoBoxAdjust = false;
8332             }
8333             return sh;
8334         },
8335
8336         push : function(sh){
8337             p.push(sh);
8338         }
8339     };
8340 }();/*
8341  * Based on:
8342  * Ext JS Library 1.1.1
8343  * Copyright(c) 2006-2007, Ext JS, LLC.
8344  *
8345  * Originally Released Under LGPL - original licence link has changed is not relivant.
8346  *
8347  * Fork - LGPL
8348  * <script type="text/javascript">
8349  */
8350
8351 /**
8352  * @class Roo.BoxComponent
8353  * @extends Roo.Component
8354  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8355  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8356  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8357  * layout containers.
8358  * @constructor
8359  * @param {Roo.Element/String/Object} config The configuration options.
8360  */
8361 Roo.BoxComponent = function(config){
8362     Roo.Component.call(this, config);
8363     this.addEvents({
8364         /**
8365          * @event resize
8366          * Fires after the component is resized.
8367              * @param {Roo.Component} this
8368              * @param {Number} adjWidth The box-adjusted width that was set
8369              * @param {Number} adjHeight The box-adjusted height that was set
8370              * @param {Number} rawWidth The width that was originally specified
8371              * @param {Number} rawHeight The height that was originally specified
8372              */
8373         resize : true,
8374         /**
8375          * @event move
8376          * Fires after the component is moved.
8377              * @param {Roo.Component} this
8378              * @param {Number} x The new x position
8379              * @param {Number} y The new y position
8380              */
8381         move : true
8382     });
8383 };
8384
8385 Roo.extend(Roo.BoxComponent, Roo.Component, {
8386     // private, set in afterRender to signify that the component has been rendered
8387     boxReady : false,
8388     // private, used to defer height settings to subclasses
8389     deferHeight: false,
8390     /** @cfg {Number} width
8391      * width (optional) size of component
8392      */
8393      /** @cfg {Number} height
8394      * height (optional) size of component
8395      */
8396      
8397     /**
8398      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8399      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8400      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8401      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8402      * @return {Roo.BoxComponent} this
8403      */
8404     setSize : function(w, h){
8405         // support for standard size objects
8406         if(typeof w == 'object'){
8407             h = w.height;
8408             w = w.width;
8409         }
8410         // not rendered
8411         if(!this.boxReady){
8412             this.width = w;
8413             this.height = h;
8414             return this;
8415         }
8416
8417         // prevent recalcs when not needed
8418         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8419             return this;
8420         }
8421         this.lastSize = {width: w, height: h};
8422
8423         var adj = this.adjustSize(w, h);
8424         var aw = adj.width, ah = adj.height;
8425         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8426             var rz = this.getResizeEl();
8427             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8428                 rz.setSize(aw, ah);
8429             }else if(!this.deferHeight && ah !== undefined){
8430                 rz.setHeight(ah);
8431             }else if(aw !== undefined){
8432                 rz.setWidth(aw);
8433             }
8434             this.onResize(aw, ah, w, h);
8435             this.fireEvent('resize', this, aw, ah, w, h);
8436         }
8437         return this;
8438     },
8439
8440     /**
8441      * Gets the current size of the component's underlying element.
8442      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8443      */
8444     getSize : function(){
8445         return this.el.getSize();
8446     },
8447
8448     /**
8449      * Gets the current XY position of the component's underlying element.
8450      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8451      * @return {Array} The XY position of the element (e.g., [100, 200])
8452      */
8453     getPosition : function(local){
8454         if(local === true){
8455             return [this.el.getLeft(true), this.el.getTop(true)];
8456         }
8457         return this.xy || this.el.getXY();
8458     },
8459
8460     /**
8461      * Gets the current box measurements of the component's underlying element.
8462      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8463      * @returns {Object} box An object in the format {x, y, width, height}
8464      */
8465     getBox : function(local){
8466         var s = this.el.getSize();
8467         if(local){
8468             s.x = this.el.getLeft(true);
8469             s.y = this.el.getTop(true);
8470         }else{
8471             var xy = this.xy || this.el.getXY();
8472             s.x = xy[0];
8473             s.y = xy[1];
8474         }
8475         return s;
8476     },
8477
8478     /**
8479      * Sets the current box measurements of the component's underlying element.
8480      * @param {Object} box An object in the format {x, y, width, height}
8481      * @returns {Roo.BoxComponent} this
8482      */
8483     updateBox : function(box){
8484         this.setSize(box.width, box.height);
8485         this.setPagePosition(box.x, box.y);
8486         return this;
8487     },
8488
8489     // protected
8490     getResizeEl : function(){
8491         return this.resizeEl || this.el;
8492     },
8493
8494     // protected
8495     getPositionEl : function(){
8496         return this.positionEl || this.el;
8497     },
8498
8499     /**
8500      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8501      * This method fires the move event.
8502      * @param {Number} left The new left
8503      * @param {Number} top The new top
8504      * @returns {Roo.BoxComponent} this
8505      */
8506     setPosition : function(x, y){
8507         this.x = x;
8508         this.y = y;
8509         if(!this.boxReady){
8510             return this;
8511         }
8512         var adj = this.adjustPosition(x, y);
8513         var ax = adj.x, ay = adj.y;
8514
8515         var el = this.getPositionEl();
8516         if(ax !== undefined || ay !== undefined){
8517             if(ax !== undefined && ay !== undefined){
8518                 el.setLeftTop(ax, ay);
8519             }else if(ax !== undefined){
8520                 el.setLeft(ax);
8521             }else if(ay !== undefined){
8522                 el.setTop(ay);
8523             }
8524             this.onPosition(ax, ay);
8525             this.fireEvent('move', this, ax, ay);
8526         }
8527         return this;
8528     },
8529
8530     /**
8531      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8532      * This method fires the move event.
8533      * @param {Number} x The new x position
8534      * @param {Number} y The new y position
8535      * @returns {Roo.BoxComponent} this
8536      */
8537     setPagePosition : function(x, y){
8538         this.pageX = x;
8539         this.pageY = y;
8540         if(!this.boxReady){
8541             return;
8542         }
8543         if(x === undefined || y === undefined){ // cannot translate undefined points
8544             return;
8545         }
8546         var p = this.el.translatePoints(x, y);
8547         this.setPosition(p.left, p.top);
8548         return this;
8549     },
8550
8551     // private
8552     onRender : function(ct, position){
8553         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8554         if(this.resizeEl){
8555             this.resizeEl = Roo.get(this.resizeEl);
8556         }
8557         if(this.positionEl){
8558             this.positionEl = Roo.get(this.positionEl);
8559         }
8560     },
8561
8562     // private
8563     afterRender : function(){
8564         Roo.BoxComponent.superclass.afterRender.call(this);
8565         this.boxReady = true;
8566         this.setSize(this.width, this.height);
8567         if(this.x || this.y){
8568             this.setPosition(this.x, this.y);
8569         }
8570         if(this.pageX || this.pageY){
8571             this.setPagePosition(this.pageX, this.pageY);
8572         }
8573     },
8574
8575     /**
8576      * Force the component's size to recalculate based on the underlying element's current height and width.
8577      * @returns {Roo.BoxComponent} this
8578      */
8579     syncSize : function(){
8580         delete this.lastSize;
8581         this.setSize(this.el.getWidth(), this.el.getHeight());
8582         return this;
8583     },
8584
8585     /**
8586      * Called after the component is resized, this method is empty by default but can be implemented by any
8587      * subclass that needs to perform custom logic after a resize occurs.
8588      * @param {Number} adjWidth The box-adjusted width that was set
8589      * @param {Number} adjHeight The box-adjusted height that was set
8590      * @param {Number} rawWidth The width that was originally specified
8591      * @param {Number} rawHeight The height that was originally specified
8592      */
8593     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8594
8595     },
8596
8597     /**
8598      * Called after the component is moved, this method is empty by default but can be implemented by any
8599      * subclass that needs to perform custom logic after a move occurs.
8600      * @param {Number} x The new x position
8601      * @param {Number} y The new y position
8602      */
8603     onPosition : function(x, y){
8604
8605     },
8606
8607     // private
8608     adjustSize : function(w, h){
8609         if(this.autoWidth){
8610             w = 'auto';
8611         }
8612         if(this.autoHeight){
8613             h = 'auto';
8614         }
8615         return {width : w, height: h};
8616     },
8617
8618     // private
8619     adjustPosition : function(x, y){
8620         return {x : x, y: y};
8621     }
8622 });/*
8623  * Based on:
8624  * Ext JS Library 1.1.1
8625  * Copyright(c) 2006-2007, Ext JS, LLC.
8626  *
8627  * Originally Released Under LGPL - original licence link has changed is not relivant.
8628  *
8629  * Fork - LGPL
8630  * <script type="text/javascript">
8631  */
8632
8633
8634 /**
8635  * @class Roo.SplitBar
8636  * @extends Roo.util.Observable
8637  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8638  * <br><br>
8639  * Usage:
8640  * <pre><code>
8641 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8642                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8643 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8644 split.minSize = 100;
8645 split.maxSize = 600;
8646 split.animate = true;
8647 split.on('moved', splitterMoved);
8648 </code></pre>
8649  * @constructor
8650  * Create a new SplitBar
8651  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8652  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8653  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8654  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8655                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8656                         position of the SplitBar).
8657  */
8658 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8659     
8660     /** @private */
8661     this.el = Roo.get(dragElement, true);
8662     this.el.dom.unselectable = "on";
8663     /** @private */
8664     this.resizingEl = Roo.get(resizingElement, true);
8665
8666     /**
8667      * @private
8668      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8669      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8670      * @type Number
8671      */
8672     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8673     
8674     /**
8675      * The minimum size of the resizing element. (Defaults to 0)
8676      * @type Number
8677      */
8678     this.minSize = 0;
8679     
8680     /**
8681      * The maximum size of the resizing element. (Defaults to 2000)
8682      * @type Number
8683      */
8684     this.maxSize = 2000;
8685     
8686     /**
8687      * Whether to animate the transition to the new size
8688      * @type Boolean
8689      */
8690     this.animate = false;
8691     
8692     /**
8693      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8694      * @type Boolean
8695      */
8696     this.useShim = false;
8697     
8698     /** @private */
8699     this.shim = null;
8700     
8701     if(!existingProxy){
8702         /** @private */
8703         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8704     }else{
8705         this.proxy = Roo.get(existingProxy).dom;
8706     }
8707     /** @private */
8708     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8709     
8710     /** @private */
8711     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8712     
8713     /** @private */
8714     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8715     
8716     /** @private */
8717     this.dragSpecs = {};
8718     
8719     /**
8720      * @private The adapter to use to positon and resize elements
8721      */
8722     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8723     this.adapter.init(this);
8724     
8725     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8726         /** @private */
8727         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8728         this.el.addClass("x-splitbar-h");
8729     }else{
8730         /** @private */
8731         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8732         this.el.addClass("x-splitbar-v");
8733     }
8734     
8735     this.addEvents({
8736         /**
8737          * @event resize
8738          * Fires when the splitter is moved (alias for {@link #event-moved})
8739          * @param {Roo.SplitBar} this
8740          * @param {Number} newSize the new width or height
8741          */
8742         "resize" : true,
8743         /**
8744          * @event moved
8745          * Fires when the splitter is moved
8746          * @param {Roo.SplitBar} this
8747          * @param {Number} newSize the new width or height
8748          */
8749         "moved" : true,
8750         /**
8751          * @event beforeresize
8752          * Fires before the splitter is dragged
8753          * @param {Roo.SplitBar} this
8754          */
8755         "beforeresize" : true,
8756
8757         "beforeapply" : true
8758     });
8759
8760     Roo.util.Observable.call(this);
8761 };
8762
8763 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8764     onStartProxyDrag : function(x, y){
8765         this.fireEvent("beforeresize", this);
8766         if(!this.overlay){
8767             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8768             o.unselectable();
8769             o.enableDisplayMode("block");
8770             // all splitbars share the same overlay
8771             Roo.SplitBar.prototype.overlay = o;
8772         }
8773         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8774         this.overlay.show();
8775         Roo.get(this.proxy).setDisplayed("block");
8776         var size = this.adapter.getElementSize(this);
8777         this.activeMinSize = this.getMinimumSize();;
8778         this.activeMaxSize = this.getMaximumSize();;
8779         var c1 = size - this.activeMinSize;
8780         var c2 = Math.max(this.activeMaxSize - size, 0);
8781         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8782             this.dd.resetConstraints();
8783             this.dd.setXConstraint(
8784                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8785                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8786             );
8787             this.dd.setYConstraint(0, 0);
8788         }else{
8789             this.dd.resetConstraints();
8790             this.dd.setXConstraint(0, 0);
8791             this.dd.setYConstraint(
8792                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8793                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8794             );
8795          }
8796         this.dragSpecs.startSize = size;
8797         this.dragSpecs.startPoint = [x, y];
8798         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8799     },
8800     
8801     /** 
8802      * @private Called after the drag operation by the DDProxy
8803      */
8804     onEndProxyDrag : function(e){
8805         Roo.get(this.proxy).setDisplayed(false);
8806         var endPoint = Roo.lib.Event.getXY(e);
8807         if(this.overlay){
8808             this.overlay.hide();
8809         }
8810         var newSize;
8811         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8812             newSize = this.dragSpecs.startSize + 
8813                 (this.placement == Roo.SplitBar.LEFT ?
8814                     endPoint[0] - this.dragSpecs.startPoint[0] :
8815                     this.dragSpecs.startPoint[0] - endPoint[0]
8816                 );
8817         }else{
8818             newSize = this.dragSpecs.startSize + 
8819                 (this.placement == Roo.SplitBar.TOP ?
8820                     endPoint[1] - this.dragSpecs.startPoint[1] :
8821                     this.dragSpecs.startPoint[1] - endPoint[1]
8822                 );
8823         }
8824         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8825         if(newSize != this.dragSpecs.startSize){
8826             if(this.fireEvent('beforeapply', this, newSize) !== false){
8827                 this.adapter.setElementSize(this, newSize);
8828                 this.fireEvent("moved", this, newSize);
8829                 this.fireEvent("resize", this, newSize);
8830             }
8831         }
8832     },
8833     
8834     /**
8835      * Get the adapter this SplitBar uses
8836      * @return The adapter object
8837      */
8838     getAdapter : function(){
8839         return this.adapter;
8840     },
8841     
8842     /**
8843      * Set the adapter this SplitBar uses
8844      * @param {Object} adapter A SplitBar adapter object
8845      */
8846     setAdapter : function(adapter){
8847         this.adapter = adapter;
8848         this.adapter.init(this);
8849     },
8850     
8851     /**
8852      * Gets the minimum size for the resizing element
8853      * @return {Number} The minimum size
8854      */
8855     getMinimumSize : function(){
8856         return this.minSize;
8857     },
8858     
8859     /**
8860      * Sets the minimum size for the resizing element
8861      * @param {Number} minSize The minimum size
8862      */
8863     setMinimumSize : function(minSize){
8864         this.minSize = minSize;
8865     },
8866     
8867     /**
8868      * Gets the maximum size for the resizing element
8869      * @return {Number} The maximum size
8870      */
8871     getMaximumSize : function(){
8872         return this.maxSize;
8873     },
8874     
8875     /**
8876      * Sets the maximum size for the resizing element
8877      * @param {Number} maxSize The maximum size
8878      */
8879     setMaximumSize : function(maxSize){
8880         this.maxSize = maxSize;
8881     },
8882     
8883     /**
8884      * Sets the initialize size for the resizing element
8885      * @param {Number} size The initial size
8886      */
8887     setCurrentSize : function(size){
8888         var oldAnimate = this.animate;
8889         this.animate = false;
8890         this.adapter.setElementSize(this, size);
8891         this.animate = oldAnimate;
8892     },
8893     
8894     /**
8895      * Destroy this splitbar. 
8896      * @param {Boolean} removeEl True to remove the element
8897      */
8898     destroy : function(removeEl){
8899         if(this.shim){
8900             this.shim.remove();
8901         }
8902         this.dd.unreg();
8903         this.proxy.parentNode.removeChild(this.proxy);
8904         if(removeEl){
8905             this.el.remove();
8906         }
8907     }
8908 });
8909
8910 /**
8911  * @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.
8912  */
8913 Roo.SplitBar.createProxy = function(dir){
8914     var proxy = new Roo.Element(document.createElement("div"));
8915     proxy.unselectable();
8916     var cls = 'x-splitbar-proxy';
8917     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8918     document.body.appendChild(proxy.dom);
8919     return proxy.dom;
8920 };
8921
8922 /** 
8923  * @class Roo.SplitBar.BasicLayoutAdapter
8924  * Default Adapter. It assumes the splitter and resizing element are not positioned
8925  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8926  */
8927 Roo.SplitBar.BasicLayoutAdapter = function(){
8928 };
8929
8930 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8931     // do nothing for now
8932     init : function(s){
8933     
8934     },
8935     /**
8936      * Called before drag operations to get the current size of the resizing element. 
8937      * @param {Roo.SplitBar} s The SplitBar using this adapter
8938      */
8939      getElementSize : function(s){
8940         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8941             return s.resizingEl.getWidth();
8942         }else{
8943             return s.resizingEl.getHeight();
8944         }
8945     },
8946     
8947     /**
8948      * Called after drag operations to set the size of the resizing element.
8949      * @param {Roo.SplitBar} s The SplitBar using this adapter
8950      * @param {Number} newSize The new size to set
8951      * @param {Function} onComplete A function to be invoked when resizing is complete
8952      */
8953     setElementSize : function(s, newSize, onComplete){
8954         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8955             if(!s.animate){
8956                 s.resizingEl.setWidth(newSize);
8957                 if(onComplete){
8958                     onComplete(s, newSize);
8959                 }
8960             }else{
8961                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8962             }
8963         }else{
8964             
8965             if(!s.animate){
8966                 s.resizingEl.setHeight(newSize);
8967                 if(onComplete){
8968                     onComplete(s, newSize);
8969                 }
8970             }else{
8971                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
8972             }
8973         }
8974     }
8975 };
8976
8977 /** 
8978  *@class Roo.SplitBar.AbsoluteLayoutAdapter
8979  * @extends Roo.SplitBar.BasicLayoutAdapter
8980  * Adapter that  moves the splitter element to align with the resized sizing element. 
8981  * Used with an absolute positioned SplitBar.
8982  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
8983  * document.body, make sure you assign an id to the body element.
8984  */
8985 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
8986     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
8987     this.container = Roo.get(container);
8988 };
8989
8990 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
8991     init : function(s){
8992         this.basic.init(s);
8993     },
8994     
8995     getElementSize : function(s){
8996         return this.basic.getElementSize(s);
8997     },
8998     
8999     setElementSize : function(s, newSize, onComplete){
9000         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9001     },
9002     
9003     moveSplitter : function(s){
9004         var yes = Roo.SplitBar;
9005         switch(s.placement){
9006             case yes.LEFT:
9007                 s.el.setX(s.resizingEl.getRight());
9008                 break;
9009             case yes.RIGHT:
9010                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9011                 break;
9012             case yes.TOP:
9013                 s.el.setY(s.resizingEl.getBottom());
9014                 break;
9015             case yes.BOTTOM:
9016                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9017                 break;
9018         }
9019     }
9020 };
9021
9022 /**
9023  * Orientation constant - Create a vertical SplitBar
9024  * @static
9025  * @type Number
9026  */
9027 Roo.SplitBar.VERTICAL = 1;
9028
9029 /**
9030  * Orientation constant - Create a horizontal SplitBar
9031  * @static
9032  * @type Number
9033  */
9034 Roo.SplitBar.HORIZONTAL = 2;
9035
9036 /**
9037  * Placement constant - The resizing element is to the left of the splitter element
9038  * @static
9039  * @type Number
9040  */
9041 Roo.SplitBar.LEFT = 1;
9042
9043 /**
9044  * Placement constant - The resizing element is to the right of the splitter element
9045  * @static
9046  * @type Number
9047  */
9048 Roo.SplitBar.RIGHT = 2;
9049
9050 /**
9051  * Placement constant - The resizing element is positioned above the splitter element
9052  * @static
9053  * @type Number
9054  */
9055 Roo.SplitBar.TOP = 3;
9056
9057 /**
9058  * Placement constant - The resizing element is positioned under splitter element
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.BOTTOM = 4;
9063 /*
9064  * Based on:
9065  * Ext JS Library 1.1.1
9066  * Copyright(c) 2006-2007, Ext JS, LLC.
9067  *
9068  * Originally Released Under LGPL - original licence link has changed is not relivant.
9069  *
9070  * Fork - LGPL
9071  * <script type="text/javascript">
9072  */
9073
9074 /**
9075  * @class Roo.View
9076  * @extends Roo.util.Observable
9077  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9078  * This class also supports single and multi selection modes. <br>
9079  * Create a data model bound view:
9080  <pre><code>
9081  var store = new Roo.data.Store(...);
9082
9083  var view = new Roo.View({
9084     el : "my-element",
9085     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9086  
9087     singleSelect: true,
9088     selectedClass: "ydataview-selected",
9089     store: store
9090  });
9091
9092  // listen for node click?
9093  view.on("click", function(vw, index, node, e){
9094  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9095  });
9096
9097  // load XML data
9098  dataModel.load("foobar.xml");
9099  </code></pre>
9100  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9101  * <br><br>
9102  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9103  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9104  * 
9105  * Note: old style constructor is still suported (container, template, config)
9106  * 
9107  * @constructor
9108  * Create a new View
9109  * @param {Object} config The config object
9110  * 
9111  */
9112 Roo.View = function(config, depreciated_tpl, depreciated_config){
9113     
9114     if (typeof(depreciated_tpl) == 'undefined') {
9115         // new way.. - universal constructor.
9116         Roo.apply(this, config);
9117         this.el  = Roo.get(this.el);
9118     } else {
9119         // old format..
9120         this.el  = Roo.get(config);
9121         this.tpl = depreciated_tpl;
9122         Roo.apply(this, depreciated_config);
9123     }
9124      
9125     
9126     if(typeof(this.tpl) == "string"){
9127         this.tpl = new Roo.Template(this.tpl);
9128     } else {
9129         // support xtype ctors..
9130         this.tpl = new Roo.factory(this.tpl, Roo);
9131     }
9132     
9133     
9134     this.tpl.compile();
9135    
9136
9137      
9138     /** @private */
9139     this.addEvents({
9140     /**
9141      * @event beforeclick
9142      * Fires before a click is processed. Returns false to cancel the default action.
9143      * @param {Roo.View} this
9144      * @param {Number} index The index of the target node
9145      * @param {HTMLElement} node The target node
9146      * @param {Roo.EventObject} e The raw event object
9147      */
9148         "beforeclick" : true,
9149     /**
9150      * @event click
9151      * Fires when a template node is clicked.
9152      * @param {Roo.View} this
9153      * @param {Number} index The index of the target node
9154      * @param {HTMLElement} node The target node
9155      * @param {Roo.EventObject} e The raw event object
9156      */
9157         "click" : true,
9158     /**
9159      * @event dblclick
9160      * Fires when a template node is double clicked.
9161      * @param {Roo.View} this
9162      * @param {Number} index The index of the target node
9163      * @param {HTMLElement} node The target node
9164      * @param {Roo.EventObject} e The raw event object
9165      */
9166         "dblclick" : true,
9167     /**
9168      * @event contextmenu
9169      * Fires when a template node is right clicked.
9170      * @param {Roo.View} this
9171      * @param {Number} index The index of the target node
9172      * @param {HTMLElement} node The target node
9173      * @param {Roo.EventObject} e The raw event object
9174      */
9175         "contextmenu" : true,
9176     /**
9177      * @event selectionchange
9178      * Fires when the selected nodes change.
9179      * @param {Roo.View} this
9180      * @param {Array} selections Array of the selected nodes
9181      */
9182         "selectionchange" : true,
9183
9184     /**
9185      * @event beforeselect
9186      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9187      * @param {Roo.View} this
9188      * @param {HTMLElement} node The node to be selected
9189      * @param {Array} selections Array of currently selected nodes
9190      */
9191         "beforeselect" : true
9192     });
9193
9194     this.el.on({
9195         "click": this.onClick,
9196         "dblclick": this.onDblClick,
9197         "contextmenu": this.onContextMenu,
9198         scope:this
9199     });
9200
9201     this.selections = [];
9202     this.nodes = [];
9203     this.cmp = new Roo.CompositeElementLite([]);
9204     if(this.store){
9205         this.store = Roo.factory(this.store, Roo.data);
9206         this.setStore(this.store, true);
9207     }
9208     Roo.View.superclass.constructor.call(this);
9209 };
9210
9211 Roo.extend(Roo.View, Roo.util.Observable, {
9212     
9213      /**
9214      * @cfg {Roo.data.Store} store Data store to load data from.
9215      */
9216     store : false,
9217     
9218     /**
9219      * @cfg {String|Roo.Element} el The container element.
9220      */
9221     el : '',
9222     
9223     /**
9224      * @cfg {String|Roo.Template} tpl The template used by this View 
9225      */
9226     tpl : false,
9227     
9228     /**
9229      * @cfg {String} selectedClass The css class to add to selected nodes
9230      */
9231     selectedClass : "x-view-selected",
9232      /**
9233      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9234      */
9235     emptyText : "",
9236     /**
9237      * @cfg {Boolean} multiSelect Allow multiple selection
9238      */
9239     
9240     multiSelect : false,
9241     /**
9242      * @cfg {Boolean} singleSelect Allow single selection
9243      */
9244     singleSelect:  false,
9245     
9246     /**
9247      * Returns the element this view is bound to.
9248      * @return {Roo.Element}
9249      */
9250     getEl : function(){
9251         return this.el;
9252     },
9253
9254     /**
9255      * Refreshes the view.
9256      */
9257     refresh : function(){
9258         var t = this.tpl;
9259         this.clearSelections();
9260         this.el.update("");
9261         var html = [];
9262         var records = this.store.getRange();
9263         if(records.length < 1){
9264             this.el.update(this.emptyText);
9265             return;
9266         }
9267         for(var i = 0, len = records.length; i < len; i++){
9268             var data = this.prepareData(records[i].data, i, records[i]);
9269             html[html.length] = t.apply(data);
9270         }
9271         this.el.update(html.join(""));
9272         this.nodes = this.el.dom.childNodes;
9273         this.updateIndexes(0);
9274     },
9275
9276     /**
9277      * Function to override to reformat the data that is sent to
9278      * the template for each node.
9279      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9280      * a JSON object for an UpdateManager bound view).
9281      */
9282     prepareData : function(data){
9283         return data;
9284     },
9285
9286     onUpdate : function(ds, record){
9287         this.clearSelections();
9288         var index = this.store.indexOf(record);
9289         var n = this.nodes[index];
9290         this.tpl.insertBefore(n, this.prepareData(record.data));
9291         n.parentNode.removeChild(n);
9292         this.updateIndexes(index, index);
9293     },
9294
9295     onAdd : function(ds, records, index){
9296         this.clearSelections();
9297         if(this.nodes.length == 0){
9298             this.refresh();
9299             return;
9300         }
9301         var n = this.nodes[index];
9302         for(var i = 0, len = records.length; i < len; i++){
9303             var d = this.prepareData(records[i].data);
9304             if(n){
9305                 this.tpl.insertBefore(n, d);
9306             }else{
9307                 this.tpl.append(this.el, d);
9308             }
9309         }
9310         this.updateIndexes(index);
9311     },
9312
9313     onRemove : function(ds, record, index){
9314         this.clearSelections();
9315         this.el.dom.removeChild(this.nodes[index]);
9316         this.updateIndexes(index);
9317     },
9318
9319     /**
9320      * Refresh an individual node.
9321      * @param {Number} index
9322      */
9323     refreshNode : function(index){
9324         this.onUpdate(this.store, this.store.getAt(index));
9325     },
9326
9327     updateIndexes : function(startIndex, endIndex){
9328         var ns = this.nodes;
9329         startIndex = startIndex || 0;
9330         endIndex = endIndex || ns.length - 1;
9331         for(var i = startIndex; i <= endIndex; i++){
9332             ns[i].nodeIndex = i;
9333         }
9334     },
9335
9336     /**
9337      * Changes the data store this view uses and refresh the view.
9338      * @param {Store} store
9339      */
9340     setStore : function(store, initial){
9341         if(!initial && this.store){
9342             this.store.un("datachanged", this.refresh);
9343             this.store.un("add", this.onAdd);
9344             this.store.un("remove", this.onRemove);
9345             this.store.un("update", this.onUpdate);
9346             this.store.un("clear", this.refresh);
9347         }
9348         if(store){
9349           
9350             store.on("datachanged", this.refresh, this);
9351             store.on("add", this.onAdd, this);
9352             store.on("remove", this.onRemove, this);
9353             store.on("update", this.onUpdate, this);
9354             store.on("clear", this.refresh, this);
9355         }
9356         
9357         if(store){
9358             this.refresh();
9359         }
9360     },
9361
9362     /**
9363      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9364      * @param {HTMLElement} node
9365      * @return {HTMLElement} The template node
9366      */
9367     findItemFromChild : function(node){
9368         var el = this.el.dom;
9369         if(!node || node.parentNode == el){
9370                     return node;
9371             }
9372             var p = node.parentNode;
9373             while(p && p != el){
9374             if(p.parentNode == el){
9375                 return p;
9376             }
9377             p = p.parentNode;
9378         }
9379             return null;
9380     },
9381
9382     /** @ignore */
9383     onClick : function(e){
9384         var item = this.findItemFromChild(e.getTarget());
9385         if(item){
9386             var index = this.indexOf(item);
9387             if(this.onItemClick(item, index, e) !== false){
9388                 this.fireEvent("click", this, index, item, e);
9389             }
9390         }else{
9391             this.clearSelections();
9392         }
9393     },
9394
9395     /** @ignore */
9396     onContextMenu : function(e){
9397         var item = this.findItemFromChild(e.getTarget());
9398         if(item){
9399             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9400         }
9401     },
9402
9403     /** @ignore */
9404     onDblClick : function(e){
9405         var item = this.findItemFromChild(e.getTarget());
9406         if(item){
9407             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9408         }
9409     },
9410
9411     onItemClick : function(item, index, e){
9412         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9413             return false;
9414         }
9415         if(this.multiSelect || this.singleSelect){
9416             if(this.multiSelect && e.shiftKey && this.lastSelection){
9417                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9418             }else{
9419                 this.select(item, this.multiSelect && e.ctrlKey);
9420                 this.lastSelection = item;
9421             }
9422             e.preventDefault();
9423         }
9424         return true;
9425     },
9426
9427     /**
9428      * Get the number of selected nodes.
9429      * @return {Number}
9430      */
9431     getSelectionCount : function(){
9432         return this.selections.length;
9433     },
9434
9435     /**
9436      * Get the currently selected nodes.
9437      * @return {Array} An array of HTMLElements
9438      */
9439     getSelectedNodes : function(){
9440         return this.selections;
9441     },
9442
9443     /**
9444      * Get the indexes of the selected nodes.
9445      * @return {Array}
9446      */
9447     getSelectedIndexes : function(){
9448         var indexes = [], s = this.selections;
9449         for(var i = 0, len = s.length; i < len; i++){
9450             indexes.push(s[i].nodeIndex);
9451         }
9452         return indexes;
9453     },
9454
9455     /**
9456      * Clear all selections
9457      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9458      */
9459     clearSelections : function(suppressEvent){
9460         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9461             this.cmp.elements = this.selections;
9462             this.cmp.removeClass(this.selectedClass);
9463             this.selections = [];
9464             if(!suppressEvent){
9465                 this.fireEvent("selectionchange", this, this.selections);
9466             }
9467         }
9468     },
9469
9470     /**
9471      * Returns true if the passed node is selected
9472      * @param {HTMLElement/Number} node The node or node index
9473      * @return {Boolean}
9474      */
9475     isSelected : function(node){
9476         var s = this.selections;
9477         if(s.length < 1){
9478             return false;
9479         }
9480         node = this.getNode(node);
9481         return s.indexOf(node) !== -1;
9482     },
9483
9484     /**
9485      * Selects nodes.
9486      * @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
9487      * @param {Boolean} keepExisting (optional) true to keep existing selections
9488      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9489      */
9490     select : function(nodeInfo, keepExisting, suppressEvent){
9491         if(nodeInfo instanceof Array){
9492             if(!keepExisting){
9493                 this.clearSelections(true);
9494             }
9495             for(var i = 0, len = nodeInfo.length; i < len; i++){
9496                 this.select(nodeInfo[i], true, true);
9497             }
9498         } else{
9499             var node = this.getNode(nodeInfo);
9500             if(node && !this.isSelected(node)){
9501                 if(!keepExisting){
9502                     this.clearSelections(true);
9503                 }
9504                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9505                     Roo.fly(node).addClass(this.selectedClass);
9506                     this.selections.push(node);
9507                     if(!suppressEvent){
9508                         this.fireEvent("selectionchange", this, this.selections);
9509                     }
9510                 }
9511             }
9512         }
9513     },
9514
9515     /**
9516      * Gets a template node.
9517      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9518      * @return {HTMLElement} The node or null if it wasn't found
9519      */
9520     getNode : function(nodeInfo){
9521         if(typeof nodeInfo == "string"){
9522             return document.getElementById(nodeInfo);
9523         }else if(typeof nodeInfo == "number"){
9524             return this.nodes[nodeInfo];
9525         }
9526         return nodeInfo;
9527     },
9528
9529     /**
9530      * Gets a range template nodes.
9531      * @param {Number} startIndex
9532      * @param {Number} endIndex
9533      * @return {Array} An array of nodes
9534      */
9535     getNodes : function(start, end){
9536         var ns = this.nodes;
9537         start = start || 0;
9538         end = typeof end == "undefined" ? ns.length - 1 : end;
9539         var nodes = [];
9540         if(start <= end){
9541             for(var i = start; i <= end; i++){
9542                 nodes.push(ns[i]);
9543             }
9544         } else{
9545             for(var i = start; i >= end; i--){
9546                 nodes.push(ns[i]);
9547             }
9548         }
9549         return nodes;
9550     },
9551
9552     /**
9553      * Finds the index of the passed node
9554      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9555      * @return {Number} The index of the node or -1
9556      */
9557     indexOf : function(node){
9558         node = this.getNode(node);
9559         if(typeof node.nodeIndex == "number"){
9560             return node.nodeIndex;
9561         }
9562         var ns = this.nodes;
9563         for(var i = 0, len = ns.length; i < len; i++){
9564             if(ns[i] == node){
9565                 return i;
9566             }
9567         }
9568         return -1;
9569     }
9570 });
9571 /*
9572  * Based on:
9573  * Ext JS Library 1.1.1
9574  * Copyright(c) 2006-2007, Ext JS, LLC.
9575  *
9576  * Originally Released Under LGPL - original licence link has changed is not relivant.
9577  *
9578  * Fork - LGPL
9579  * <script type="text/javascript">
9580  */
9581
9582 /**
9583  * @class Roo.JsonView
9584  * @extends Roo.View
9585  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9586 <pre><code>
9587 var view = new Roo.JsonView({
9588     container: "my-element",
9589     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9590     multiSelect: true, 
9591     jsonRoot: "data" 
9592 });
9593
9594 // listen for node click?
9595 view.on("click", function(vw, index, node, e){
9596     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9597 });
9598
9599 // direct load of JSON data
9600 view.load("foobar.php");
9601
9602 // Example from my blog list
9603 var tpl = new Roo.Template(
9604     '&lt;div class="entry"&gt;' +
9605     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9606     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9607     "&lt;/div&gt;&lt;hr /&gt;"
9608 );
9609
9610 var moreView = new Roo.JsonView({
9611     container :  "entry-list", 
9612     template : tpl,
9613     jsonRoot: "posts"
9614 });
9615 moreView.on("beforerender", this.sortEntries, this);
9616 moreView.load({
9617     url: "/blog/get-posts.php",
9618     params: "allposts=true",
9619     text: "Loading Blog Entries..."
9620 });
9621 </code></pre>
9622
9623 * Note: old code is supported with arguments : (container, template, config)
9624
9625
9626  * @constructor
9627  * Create a new JsonView
9628  * 
9629  * @param {Object} config The config object
9630  * 
9631  */
9632 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9633     
9634     
9635     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9636
9637     var um = this.el.getUpdateManager();
9638     um.setRenderer(this);
9639     um.on("update", this.onLoad, this);
9640     um.on("failure", this.onLoadException, this);
9641
9642     /**
9643      * @event beforerender
9644      * Fires before rendering of the downloaded JSON data.
9645      * @param {Roo.JsonView} this
9646      * @param {Object} data The JSON data loaded
9647      */
9648     /**
9649      * @event load
9650      * Fires when data is loaded.
9651      * @param {Roo.JsonView} this
9652      * @param {Object} data The JSON data loaded
9653      * @param {Object} response The raw Connect response object
9654      */
9655     /**
9656      * @event loadexception
9657      * Fires when loading fails.
9658      * @param {Roo.JsonView} this
9659      * @param {Object} response The raw Connect response object
9660      */
9661     this.addEvents({
9662         'beforerender' : true,
9663         'load' : true,
9664         'loadexception' : true
9665     });
9666 };
9667 Roo.extend(Roo.JsonView, Roo.View, {
9668     /**
9669      * @type {String} The root property in the loaded JSON object that contains the data
9670      */
9671     jsonRoot : "",
9672
9673     /**
9674      * Refreshes the view.
9675      */
9676     refresh : function(){
9677         this.clearSelections();
9678         this.el.update("");
9679         var html = [];
9680         var o = this.jsonData;
9681         if(o && o.length > 0){
9682             for(var i = 0, len = o.length; i < len; i++){
9683                 var data = this.prepareData(o[i], i, o);
9684                 html[html.length] = this.tpl.apply(data);
9685             }
9686         }else{
9687             html.push(this.emptyText);
9688         }
9689         this.el.update(html.join(""));
9690         this.nodes = this.el.dom.childNodes;
9691         this.updateIndexes(0);
9692     },
9693
9694     /**
9695      * 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.
9696      * @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:
9697      <pre><code>
9698      view.load({
9699          url: "your-url.php",
9700          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9701          callback: yourFunction,
9702          scope: yourObject, //(optional scope)
9703          discardUrl: false,
9704          nocache: false,
9705          text: "Loading...",
9706          timeout: 30,
9707          scripts: false
9708      });
9709      </code></pre>
9710      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9711      * 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.
9712      * @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}
9713      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9714      * @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.
9715      */
9716     load : function(){
9717         var um = this.el.getUpdateManager();
9718         um.update.apply(um, arguments);
9719     },
9720
9721     render : function(el, response){
9722         this.clearSelections();
9723         this.el.update("");
9724         var o;
9725         try{
9726             o = Roo.util.JSON.decode(response.responseText);
9727             if(this.jsonRoot){
9728                 
9729                 o = o[this.jsonRoot];
9730             }
9731         } catch(e){
9732         }
9733         /**
9734          * The current JSON data or null
9735          */
9736         this.jsonData = o;
9737         this.beforeRender();
9738         this.refresh();
9739     },
9740
9741 /**
9742  * Get the number of records in the current JSON dataset
9743  * @return {Number}
9744  */
9745     getCount : function(){
9746         return this.jsonData ? this.jsonData.length : 0;
9747     },
9748
9749 /**
9750  * Returns the JSON object for the specified node(s)
9751  * @param {HTMLElement/Array} node The node or an array of nodes
9752  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9753  * you get the JSON object for the node
9754  */
9755     getNodeData : function(node){
9756         if(node instanceof Array){
9757             var data = [];
9758             for(var i = 0, len = node.length; i < len; i++){
9759                 data.push(this.getNodeData(node[i]));
9760             }
9761             return data;
9762         }
9763         return this.jsonData[this.indexOf(node)] || null;
9764     },
9765
9766     beforeRender : function(){
9767         this.snapshot = this.jsonData;
9768         if(this.sortInfo){
9769             this.sort.apply(this, this.sortInfo);
9770         }
9771         this.fireEvent("beforerender", this, this.jsonData);
9772     },
9773
9774     onLoad : function(el, o){
9775         this.fireEvent("load", this, this.jsonData, o);
9776     },
9777
9778     onLoadException : function(el, o){
9779         this.fireEvent("loadexception", this, o);
9780     },
9781
9782 /**
9783  * Filter the data by a specific property.
9784  * @param {String} property A property on your JSON objects
9785  * @param {String/RegExp} value Either string that the property values
9786  * should start with, or a RegExp to test against the property
9787  */
9788     filter : function(property, value){
9789         if(this.jsonData){
9790             var data = [];
9791             var ss = this.snapshot;
9792             if(typeof value == "string"){
9793                 var vlen = value.length;
9794                 if(vlen == 0){
9795                     this.clearFilter();
9796                     return;
9797                 }
9798                 value = value.toLowerCase();
9799                 for(var i = 0, len = ss.length; i < len; i++){
9800                     var o = ss[i];
9801                     if(o[property].substr(0, vlen).toLowerCase() == value){
9802                         data.push(o);
9803                     }
9804                 }
9805             } else if(value.exec){ // regex?
9806                 for(var i = 0, len = ss.length; i < len; i++){
9807                     var o = ss[i];
9808                     if(value.test(o[property])){
9809                         data.push(o);
9810                     }
9811                 }
9812             } else{
9813                 return;
9814             }
9815             this.jsonData = data;
9816             this.refresh();
9817         }
9818     },
9819
9820 /**
9821  * Filter by a function. The passed function will be called with each
9822  * object in the current dataset. If the function returns true the value is kept,
9823  * otherwise it is filtered.
9824  * @param {Function} fn
9825  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9826  */
9827     filterBy : function(fn, scope){
9828         if(this.jsonData){
9829             var data = [];
9830             var ss = this.snapshot;
9831             for(var i = 0, len = ss.length; i < len; i++){
9832                 var o = ss[i];
9833                 if(fn.call(scope || this, o)){
9834                     data.push(o);
9835                 }
9836             }
9837             this.jsonData = data;
9838             this.refresh();
9839         }
9840     },
9841
9842 /**
9843  * Clears the current filter.
9844  */
9845     clearFilter : function(){
9846         if(this.snapshot && this.jsonData != this.snapshot){
9847             this.jsonData = this.snapshot;
9848             this.refresh();
9849         }
9850     },
9851
9852
9853 /**
9854  * Sorts the data for this view and refreshes it.
9855  * @param {String} property A property on your JSON objects to sort on
9856  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9857  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9858  */
9859     sort : function(property, dir, sortType){
9860         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9861         if(this.jsonData){
9862             var p = property;
9863             var dsc = dir && dir.toLowerCase() == "desc";
9864             var f = function(o1, o2){
9865                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9866                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9867                 ;
9868                 if(v1 < v2){
9869                     return dsc ? +1 : -1;
9870                 } else if(v1 > v2){
9871                     return dsc ? -1 : +1;
9872                 } else{
9873                     return 0;
9874                 }
9875             };
9876             this.jsonData.sort(f);
9877             this.refresh();
9878             if(this.jsonData != this.snapshot){
9879                 this.snapshot.sort(f);
9880             }
9881         }
9882     }
9883 });/*
9884  * Based on:
9885  * Ext JS Library 1.1.1
9886  * Copyright(c) 2006-2007, Ext JS, LLC.
9887  *
9888  * Originally Released Under LGPL - original licence link has changed is not relivant.
9889  *
9890  * Fork - LGPL
9891  * <script type="text/javascript">
9892  */
9893  
9894
9895 /**
9896  * @class Roo.ColorPalette
9897  * @extends Roo.Component
9898  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
9899  * Here's an example of typical usage:
9900  * <pre><code>
9901 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
9902 cp.render('my-div');
9903
9904 cp.on('select', function(palette, selColor){
9905     // do something with selColor
9906 });
9907 </code></pre>
9908  * @constructor
9909  * Create a new ColorPalette
9910  * @param {Object} config The config object
9911  */
9912 Roo.ColorPalette = function(config){
9913     Roo.ColorPalette.superclass.constructor.call(this, config);
9914     this.addEvents({
9915         /**
9916              * @event select
9917              * Fires when a color is selected
9918              * @param {ColorPalette} this
9919              * @param {String} color The 6-digit color hex code (without the # symbol)
9920              */
9921         select: true
9922     });
9923
9924     if(this.handler){
9925         this.on("select", this.handler, this.scope, true);
9926     }
9927 };
9928 Roo.extend(Roo.ColorPalette, Roo.Component, {
9929     /**
9930      * @cfg {String} itemCls
9931      * The CSS class to apply to the containing element (defaults to "x-color-palette")
9932      */
9933     itemCls : "x-color-palette",
9934     /**
9935      * @cfg {String} value
9936      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
9937      * the hex codes are case-sensitive.
9938      */
9939     value : null,
9940     clickEvent:'click',
9941     // private
9942     ctype: "Roo.ColorPalette",
9943
9944     /**
9945      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
9946      */
9947     allowReselect : false,
9948
9949     /**
9950      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
9951      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
9952      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
9953      * of colors with the width setting until the box is symmetrical.</p>
9954      * <p>You can override individual colors if needed:</p>
9955      * <pre><code>
9956 var cp = new Roo.ColorPalette();
9957 cp.colors[0] = "FF0000";  // change the first box to red
9958 </code></pre>
9959
9960 Or you can provide a custom array of your own for complete control:
9961 <pre><code>
9962 var cp = new Roo.ColorPalette();
9963 cp.colors = ["000000", "993300", "333300"];
9964 </code></pre>
9965      * @type Array
9966      */
9967     colors : [
9968         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
9969         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
9970         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
9971         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
9972         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
9973     ],
9974
9975     // private
9976     onRender : function(container, position){
9977         var t = new Roo.MasterTemplate(
9978             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
9979         );
9980         var c = this.colors;
9981         for(var i = 0, len = c.length; i < len; i++){
9982             t.add([c[i]]);
9983         }
9984         var el = document.createElement("div");
9985         el.className = this.itemCls;
9986         t.overwrite(el);
9987         container.dom.insertBefore(el, position);
9988         this.el = Roo.get(el);
9989         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
9990         if(this.clickEvent != 'click'){
9991             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
9992         }
9993     },
9994
9995     // private
9996     afterRender : function(){
9997         Roo.ColorPalette.superclass.afterRender.call(this);
9998         if(this.value){
9999             var s = this.value;
10000             this.value = null;
10001             this.select(s);
10002         }
10003     },
10004
10005     // private
10006     handleClick : function(e, t){
10007         e.preventDefault();
10008         if(!this.disabled){
10009             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10010             this.select(c.toUpperCase());
10011         }
10012     },
10013
10014     /**
10015      * Selects the specified color in the palette (fires the select event)
10016      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10017      */
10018     select : function(color){
10019         color = color.replace("#", "");
10020         if(color != this.value || this.allowReselect){
10021             var el = this.el;
10022             if(this.value){
10023                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10024             }
10025             el.child("a.color-"+color).addClass("x-color-palette-sel");
10026             this.value = color;
10027             this.fireEvent("select", this, color);
10028         }
10029     }
10030 });/*
10031  * Based on:
10032  * Ext JS Library 1.1.1
10033  * Copyright(c) 2006-2007, Ext JS, LLC.
10034  *
10035  * Originally Released Under LGPL - original licence link has changed is not relivant.
10036  *
10037  * Fork - LGPL
10038  * <script type="text/javascript">
10039  */
10040  
10041 /**
10042  * @class Roo.DatePicker
10043  * @extends Roo.Component
10044  * Simple date picker class.
10045  * @constructor
10046  * Create a new DatePicker
10047  * @param {Object} config The config object
10048  */
10049 Roo.DatePicker = function(config){
10050     Roo.DatePicker.superclass.constructor.call(this, config);
10051
10052     this.value = config && config.value ?
10053                  config.value.clearTime() : new Date().clearTime();
10054
10055     this.addEvents({
10056         /**
10057              * @event select
10058              * Fires when a date is selected
10059              * @param {DatePicker} this
10060              * @param {Date} date The selected date
10061              */
10062         select: true
10063     });
10064
10065     if(this.handler){
10066         this.on("select", this.handler,  this.scope || this);
10067     }
10068     // build the disabledDatesRE
10069     if(!this.disabledDatesRE && this.disabledDates){
10070         var dd = this.disabledDates;
10071         var re = "(?:";
10072         for(var i = 0; i < dd.length; i++){
10073             re += dd[i];
10074             if(i != dd.length-1) re += "|";
10075         }
10076         this.disabledDatesRE = new RegExp(re + ")");
10077     }
10078 };
10079
10080 Roo.extend(Roo.DatePicker, Roo.Component, {
10081     /**
10082      * @cfg {String} todayText
10083      * The text to display on the button that selects the current date (defaults to "Today")
10084      */
10085     todayText : "Today",
10086     /**
10087      * @cfg {String} okText
10088      * The text to display on the ok button
10089      */
10090     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10091     /**
10092      * @cfg {String} cancelText
10093      * The text to display on the cancel button
10094      */
10095     cancelText : "Cancel",
10096     /**
10097      * @cfg {String} todayTip
10098      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10099      */
10100     todayTip : "{0} (Spacebar)",
10101     /**
10102      * @cfg {Date} minDate
10103      * Minimum allowable date (JavaScript date object, defaults to null)
10104      */
10105     minDate : null,
10106     /**
10107      * @cfg {Date} maxDate
10108      * Maximum allowable date (JavaScript date object, defaults to null)
10109      */
10110     maxDate : null,
10111     /**
10112      * @cfg {String} minText
10113      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10114      */
10115     minText : "This date is before the minimum date",
10116     /**
10117      * @cfg {String} maxText
10118      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10119      */
10120     maxText : "This date is after the maximum date",
10121     /**
10122      * @cfg {String} format
10123      * The default date format string which can be overriden for localization support.  The format must be
10124      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10125      */
10126     format : "m/d/y",
10127     /**
10128      * @cfg {Array} disabledDays
10129      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10130      */
10131     disabledDays : null,
10132     /**
10133      * @cfg {String} disabledDaysText
10134      * The tooltip to display when the date falls on a disabled day (defaults to "")
10135      */
10136     disabledDaysText : "",
10137     /**
10138      * @cfg {RegExp} disabledDatesRE
10139      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10140      */
10141     disabledDatesRE : null,
10142     /**
10143      * @cfg {String} disabledDatesText
10144      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10145      */
10146     disabledDatesText : "",
10147     /**
10148      * @cfg {Boolean} constrainToViewport
10149      * True to constrain the date picker to the viewport (defaults to true)
10150      */
10151     constrainToViewport : true,
10152     /**
10153      * @cfg {Array} monthNames
10154      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10155      */
10156     monthNames : Date.monthNames,
10157     /**
10158      * @cfg {Array} dayNames
10159      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10160      */
10161     dayNames : Date.dayNames,
10162     /**
10163      * @cfg {String} nextText
10164      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10165      */
10166     nextText: 'Next Month (Control+Right)',
10167     /**
10168      * @cfg {String} prevText
10169      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10170      */
10171     prevText: 'Previous Month (Control+Left)',
10172     /**
10173      * @cfg {String} monthYearText
10174      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10175      */
10176     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10177     /**
10178      * @cfg {Number} startDay
10179      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10180      */
10181     startDay : 0,
10182     /**
10183      * @cfg {Bool} showClear
10184      * Show a clear button (usefull for date form elements that can be blank.)
10185      */
10186     
10187     showClear: false,
10188     
10189     /**
10190      * Sets the value of the date field
10191      * @param {Date} value The date to set
10192      */
10193     setValue : function(value){
10194         var old = this.value;
10195         this.value = value.clearTime(true);
10196         if(this.el){
10197             this.update(this.value);
10198         }
10199     },
10200
10201     /**
10202      * Gets the current selected value of the date field
10203      * @return {Date} The selected date
10204      */
10205     getValue : function(){
10206         return this.value;
10207     },
10208
10209     // private
10210     focus : function(){
10211         if(this.el){
10212             this.update(this.activeDate);
10213         }
10214     },
10215
10216     // private
10217     onRender : function(container, position){
10218         var m = [
10219              '<table cellspacing="0">',
10220                 '<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>',
10221                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10222         var dn = this.dayNames;
10223         for(var i = 0; i < 7; i++){
10224             var d = this.startDay+i;
10225             if(d > 6){
10226                 d = d-7;
10227             }
10228             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10229         }
10230         m[m.length] = "</tr></thead><tbody><tr>";
10231         for(var i = 0; i < 42; i++) {
10232             if(i % 7 == 0 && i != 0){
10233                 m[m.length] = "</tr><tr>";
10234             }
10235             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10236         }
10237         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10238             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10239
10240         var el = document.createElement("div");
10241         el.className = "x-date-picker";
10242         el.innerHTML = m.join("");
10243
10244         container.dom.insertBefore(el, position);
10245
10246         this.el = Roo.get(el);
10247         this.eventEl = Roo.get(el.firstChild);
10248
10249         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10250             handler: this.showPrevMonth,
10251             scope: this,
10252             preventDefault:true,
10253             stopDefault:true
10254         });
10255
10256         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10257             handler: this.showNextMonth,
10258             scope: this,
10259             preventDefault:true,
10260             stopDefault:true
10261         });
10262
10263         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10264
10265         this.monthPicker = this.el.down('div.x-date-mp');
10266         this.monthPicker.enableDisplayMode('block');
10267         
10268         var kn = new Roo.KeyNav(this.eventEl, {
10269             "left" : function(e){
10270                 e.ctrlKey ?
10271                     this.showPrevMonth() :
10272                     this.update(this.activeDate.add("d", -1));
10273             },
10274
10275             "right" : function(e){
10276                 e.ctrlKey ?
10277                     this.showNextMonth() :
10278                     this.update(this.activeDate.add("d", 1));
10279             },
10280
10281             "up" : function(e){
10282                 e.ctrlKey ?
10283                     this.showNextYear() :
10284                     this.update(this.activeDate.add("d", -7));
10285             },
10286
10287             "down" : function(e){
10288                 e.ctrlKey ?
10289                     this.showPrevYear() :
10290                     this.update(this.activeDate.add("d", 7));
10291             },
10292
10293             "pageUp" : function(e){
10294                 this.showNextMonth();
10295             },
10296
10297             "pageDown" : function(e){
10298                 this.showPrevMonth();
10299             },
10300
10301             "enter" : function(e){
10302                 e.stopPropagation();
10303                 return true;
10304             },
10305
10306             scope : this
10307         });
10308
10309         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10310
10311         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10312
10313         this.el.unselectable();
10314         
10315         this.cells = this.el.select("table.x-date-inner tbody td");
10316         this.textNodes = this.el.query("table.x-date-inner tbody span");
10317
10318         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10319             text: "&#160;",
10320             tooltip: this.monthYearText
10321         });
10322
10323         this.mbtn.on('click', this.showMonthPicker, this);
10324         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10325
10326
10327         var today = (new Date()).dateFormat(this.format);
10328         
10329         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10330         if (this.showClear) {
10331             baseTb.add( new Roo.Toolbar.Fill());
10332         }
10333         baseTb.add({
10334             text: String.format(this.todayText, today),
10335             tooltip: String.format(this.todayTip, today),
10336             handler: this.selectToday,
10337             scope: this
10338         });
10339         
10340         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10341             
10342         //});
10343         if (this.showClear) {
10344             
10345             baseTb.add( new Roo.Toolbar.Fill());
10346             baseTb.add({
10347                 text: '&#160;',
10348                 cls: 'x-btn-icon x-btn-clear',
10349                 handler: function() {
10350                     //this.value = '';
10351                     this.fireEvent("select", this, '');
10352                 },
10353                 scope: this
10354             });
10355         }
10356         
10357         
10358         if(Roo.isIE){
10359             this.el.repaint();
10360         }
10361         this.update(this.value);
10362     },
10363
10364     createMonthPicker : function(){
10365         if(!this.monthPicker.dom.firstChild){
10366             var buf = ['<table border="0" cellspacing="0">'];
10367             for(var i = 0; i < 6; i++){
10368                 buf.push(
10369                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10370                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10371                     i == 0 ?
10372                     '<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>' :
10373                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10374                 );
10375             }
10376             buf.push(
10377                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10378                     this.okText,
10379                     '</button><button type="button" class="x-date-mp-cancel">',
10380                     this.cancelText,
10381                     '</button></td></tr>',
10382                 '</table>'
10383             );
10384             this.monthPicker.update(buf.join(''));
10385             this.monthPicker.on('click', this.onMonthClick, this);
10386             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10387
10388             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10389             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10390
10391             this.mpMonths.each(function(m, a, i){
10392                 i += 1;
10393                 if((i%2) == 0){
10394                     m.dom.xmonth = 5 + Math.round(i * .5);
10395                 }else{
10396                     m.dom.xmonth = Math.round((i-1) * .5);
10397                 }
10398             });
10399         }
10400     },
10401
10402     showMonthPicker : function(){
10403         this.createMonthPicker();
10404         var size = this.el.getSize();
10405         this.monthPicker.setSize(size);
10406         this.monthPicker.child('table').setSize(size);
10407
10408         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10409         this.updateMPMonth(this.mpSelMonth);
10410         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10411         this.updateMPYear(this.mpSelYear);
10412
10413         this.monthPicker.slideIn('t', {duration:.2});
10414     },
10415
10416     updateMPYear : function(y){
10417         this.mpyear = y;
10418         var ys = this.mpYears.elements;
10419         for(var i = 1; i <= 10; i++){
10420             var td = ys[i-1], y2;
10421             if((i%2) == 0){
10422                 y2 = y + Math.round(i * .5);
10423                 td.firstChild.innerHTML = y2;
10424                 td.xyear = y2;
10425             }else{
10426                 y2 = y - (5-Math.round(i * .5));
10427                 td.firstChild.innerHTML = y2;
10428                 td.xyear = y2;
10429             }
10430             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10431         }
10432     },
10433
10434     updateMPMonth : function(sm){
10435         this.mpMonths.each(function(m, a, i){
10436             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10437         });
10438     },
10439
10440     selectMPMonth: function(m){
10441         
10442     },
10443
10444     onMonthClick : function(e, t){
10445         e.stopEvent();
10446         var el = new Roo.Element(t), pn;
10447         if(el.is('button.x-date-mp-cancel')){
10448             this.hideMonthPicker();
10449         }
10450         else if(el.is('button.x-date-mp-ok')){
10451             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10452             this.hideMonthPicker();
10453         }
10454         else if(pn = el.up('td.x-date-mp-month', 2)){
10455             this.mpMonths.removeClass('x-date-mp-sel');
10456             pn.addClass('x-date-mp-sel');
10457             this.mpSelMonth = pn.dom.xmonth;
10458         }
10459         else if(pn = el.up('td.x-date-mp-year', 2)){
10460             this.mpYears.removeClass('x-date-mp-sel');
10461             pn.addClass('x-date-mp-sel');
10462             this.mpSelYear = pn.dom.xyear;
10463         }
10464         else if(el.is('a.x-date-mp-prev')){
10465             this.updateMPYear(this.mpyear-10);
10466         }
10467         else if(el.is('a.x-date-mp-next')){
10468             this.updateMPYear(this.mpyear+10);
10469         }
10470     },
10471
10472     onMonthDblClick : function(e, t){
10473         e.stopEvent();
10474         var el = new Roo.Element(t), pn;
10475         if(pn = el.up('td.x-date-mp-month', 2)){
10476             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10477             this.hideMonthPicker();
10478         }
10479         else if(pn = el.up('td.x-date-mp-year', 2)){
10480             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10481             this.hideMonthPicker();
10482         }
10483     },
10484
10485     hideMonthPicker : function(disableAnim){
10486         if(this.monthPicker){
10487             if(disableAnim === true){
10488                 this.monthPicker.hide();
10489             }else{
10490                 this.monthPicker.slideOut('t', {duration:.2});
10491             }
10492         }
10493     },
10494
10495     // private
10496     showPrevMonth : function(e){
10497         this.update(this.activeDate.add("mo", -1));
10498     },
10499
10500     // private
10501     showNextMonth : function(e){
10502         this.update(this.activeDate.add("mo", 1));
10503     },
10504
10505     // private
10506     showPrevYear : function(){
10507         this.update(this.activeDate.add("y", -1));
10508     },
10509
10510     // private
10511     showNextYear : function(){
10512         this.update(this.activeDate.add("y", 1));
10513     },
10514
10515     // private
10516     handleMouseWheel : function(e){
10517         var delta = e.getWheelDelta();
10518         if(delta > 0){
10519             this.showPrevMonth();
10520             e.stopEvent();
10521         } else if(delta < 0){
10522             this.showNextMonth();
10523             e.stopEvent();
10524         }
10525     },
10526
10527     // private
10528     handleDateClick : function(e, t){
10529         e.stopEvent();
10530         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10531             this.setValue(new Date(t.dateValue));
10532             this.fireEvent("select", this, this.value);
10533         }
10534     },
10535
10536     // private
10537     selectToday : function(){
10538         this.setValue(new Date().clearTime());
10539         this.fireEvent("select", this, this.value);
10540     },
10541
10542     // private
10543     update : function(date){
10544         var vd = this.activeDate;
10545         this.activeDate = date;
10546         if(vd && this.el){
10547             var t = date.getTime();
10548             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10549                 this.cells.removeClass("x-date-selected");
10550                 this.cells.each(function(c){
10551                    if(c.dom.firstChild.dateValue == t){
10552                        c.addClass("x-date-selected");
10553                        setTimeout(function(){
10554                             try{c.dom.firstChild.focus();}catch(e){}
10555                        }, 50);
10556                        return false;
10557                    }
10558                 });
10559                 return;
10560             }
10561         }
10562         var days = date.getDaysInMonth();
10563         var firstOfMonth = date.getFirstDateOfMonth();
10564         var startingPos = firstOfMonth.getDay()-this.startDay;
10565
10566         if(startingPos <= this.startDay){
10567             startingPos += 7;
10568         }
10569
10570         var pm = date.add("mo", -1);
10571         var prevStart = pm.getDaysInMonth()-startingPos;
10572
10573         var cells = this.cells.elements;
10574         var textEls = this.textNodes;
10575         days += startingPos;
10576
10577         // convert everything to numbers so it's fast
10578         var day = 86400000;
10579         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10580         var today = new Date().clearTime().getTime();
10581         var sel = date.clearTime().getTime();
10582         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10583         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10584         var ddMatch = this.disabledDatesRE;
10585         var ddText = this.disabledDatesText;
10586         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10587         var ddaysText = this.disabledDaysText;
10588         var format = this.format;
10589
10590         var setCellClass = function(cal, cell){
10591             cell.title = "";
10592             var t = d.getTime();
10593             cell.firstChild.dateValue = t;
10594             if(t == today){
10595                 cell.className += " x-date-today";
10596                 cell.title = cal.todayText;
10597             }
10598             if(t == sel){
10599                 cell.className += " x-date-selected";
10600                 setTimeout(function(){
10601                     try{cell.firstChild.focus();}catch(e){}
10602                 }, 50);
10603             }
10604             // disabling
10605             if(t < min) {
10606                 cell.className = " x-date-disabled";
10607                 cell.title = cal.minText;
10608                 return;
10609             }
10610             if(t > max) {
10611                 cell.className = " x-date-disabled";
10612                 cell.title = cal.maxText;
10613                 return;
10614             }
10615             if(ddays){
10616                 if(ddays.indexOf(d.getDay()) != -1){
10617                     cell.title = ddaysText;
10618                     cell.className = " x-date-disabled";
10619                 }
10620             }
10621             if(ddMatch && format){
10622                 var fvalue = d.dateFormat(format);
10623                 if(ddMatch.test(fvalue)){
10624                     cell.title = ddText.replace("%0", fvalue);
10625                     cell.className = " x-date-disabled";
10626                 }
10627             }
10628         };
10629
10630         var i = 0;
10631         for(; i < startingPos; i++) {
10632             textEls[i].innerHTML = (++prevStart);
10633             d.setDate(d.getDate()+1);
10634             cells[i].className = "x-date-prevday";
10635             setCellClass(this, cells[i]);
10636         }
10637         for(; i < days; i++){
10638             intDay = i - startingPos + 1;
10639             textEls[i].innerHTML = (intDay);
10640             d.setDate(d.getDate()+1);
10641             cells[i].className = "x-date-active";
10642             setCellClass(this, cells[i]);
10643         }
10644         var extraDays = 0;
10645         for(; i < 42; i++) {
10646              textEls[i].innerHTML = (++extraDays);
10647              d.setDate(d.getDate()+1);
10648              cells[i].className = "x-date-nextday";
10649              setCellClass(this, cells[i]);
10650         }
10651
10652         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10653
10654         if(!this.internalRender){
10655             var main = this.el.dom.firstChild;
10656             var w = main.offsetWidth;
10657             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10658             Roo.fly(main).setWidth(w);
10659             this.internalRender = true;
10660             // opera does not respect the auto grow header center column
10661             // then, after it gets a width opera refuses to recalculate
10662             // without a second pass
10663             if(Roo.isOpera && !this.secondPass){
10664                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10665                 this.secondPass = true;
10666                 this.update.defer(10, this, [date]);
10667             }
10668         }
10669     }
10670 });/*
10671  * Based on:
10672  * Ext JS Library 1.1.1
10673  * Copyright(c) 2006-2007, Ext JS, LLC.
10674  *
10675  * Originally Released Under LGPL - original licence link has changed is not relivant.
10676  *
10677  * Fork - LGPL
10678  * <script type="text/javascript">
10679  */
10680 /**
10681  * @class Roo.TabPanel
10682  * @extends Roo.util.Observable
10683  * A lightweight tab container.
10684  * <br><br>
10685  * Usage:
10686  * <pre><code>
10687 // basic tabs 1, built from existing content
10688 var tabs = new Roo.TabPanel("tabs1");
10689 tabs.addTab("script", "View Script");
10690 tabs.addTab("markup", "View Markup");
10691 tabs.activate("script");
10692
10693 // more advanced tabs, built from javascript
10694 var jtabs = new Roo.TabPanel("jtabs");
10695 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10696
10697 // set up the UpdateManager
10698 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10699 var updater = tab2.getUpdateManager();
10700 updater.setDefaultUrl("ajax1.htm");
10701 tab2.on('activate', updater.refresh, updater, true);
10702
10703 // Use setUrl for Ajax loading
10704 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10705 tab3.setUrl("ajax2.htm", null, true);
10706
10707 // Disabled tab
10708 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10709 tab4.disable();
10710
10711 jtabs.activate("jtabs-1");
10712  * </code></pre>
10713  * @constructor
10714  * Create a new TabPanel.
10715  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10716  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10717  */
10718 Roo.TabPanel = function(container, config){
10719     /**
10720     * The container element for this TabPanel.
10721     * @type Roo.Element
10722     */
10723     this.el = Roo.get(container, true);
10724     if(config){
10725         if(typeof config == "boolean"){
10726             this.tabPosition = config ? "bottom" : "top";
10727         }else{
10728             Roo.apply(this, config);
10729         }
10730     }
10731     if(this.tabPosition == "bottom"){
10732         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10733         this.el.addClass("x-tabs-bottom");
10734     }
10735     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10736     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10737     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10738     if(Roo.isIE){
10739         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10740     }
10741     if(this.tabPosition != "bottom"){
10742     /** The body element that contains {@link Roo.TabPanelItem} bodies.
10743      * @type Roo.Element
10744      */
10745       this.bodyEl = Roo.get(this.createBody(this.el.dom));
10746       this.el.addClass("x-tabs-top");
10747     }
10748     this.items = [];
10749
10750     this.bodyEl.setStyle("position", "relative");
10751
10752     this.active = null;
10753     this.activateDelegate = this.activate.createDelegate(this);
10754
10755     this.addEvents({
10756         /**
10757          * @event tabchange
10758          * Fires when the active tab changes
10759          * @param {Roo.TabPanel} this
10760          * @param {Roo.TabPanelItem} activePanel The new active tab
10761          */
10762         "tabchange": true,
10763         /**
10764          * @event beforetabchange
10765          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10766          * @param {Roo.TabPanel} this
10767          * @param {Object} e Set cancel to true on this object to cancel the tab change
10768          * @param {Roo.TabPanelItem} tab The tab being changed to
10769          */
10770         "beforetabchange" : true
10771     });
10772
10773     Roo.EventManager.onWindowResize(this.onResize, this);
10774     this.cpad = this.el.getPadding("lr");
10775     this.hiddenCount = 0;
10776
10777     Roo.TabPanel.superclass.constructor.call(this);
10778 };
10779
10780 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10781         /*
10782          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10783          */
10784     tabPosition : "top",
10785         /*
10786          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10787          */
10788     currentTabWidth : 0,
10789         /*
10790          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10791          */
10792     minTabWidth : 40,
10793         /*
10794          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10795          */
10796     maxTabWidth : 250,
10797         /*
10798          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10799          */
10800     preferredTabWidth : 175,
10801         /*
10802          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10803          */
10804     resizeTabs : false,
10805         /*
10806          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10807          */
10808     monitorResize : true,
10809
10810     /**
10811      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10812      * @param {String} id The id of the div to use <b>or create</b>
10813      * @param {String} text The text for the tab
10814      * @param {String} content (optional) Content to put in the TabPanelItem body
10815      * @param {Boolean} closable (optional) True to create a close icon on the tab
10816      * @return {Roo.TabPanelItem} The created TabPanelItem
10817      */
10818     addTab : function(id, text, content, closable){
10819         var item = new Roo.TabPanelItem(this, id, text, closable);
10820         this.addTabItem(item);
10821         if(content){
10822             item.setContent(content);
10823         }
10824         return item;
10825     },
10826
10827     /**
10828      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10829      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10830      * @return {Roo.TabPanelItem}
10831      */
10832     getTab : function(id){
10833         return this.items[id];
10834     },
10835
10836     /**
10837      * Hides the {@link Roo.TabPanelItem} with the specified id/index
10838      * @param {String/Number} id The id or index of the TabPanelItem to hide.
10839      */
10840     hideTab : function(id){
10841         var t = this.items[id];
10842         if(!t.isHidden()){
10843            t.setHidden(true);
10844            this.hiddenCount++;
10845            this.autoSizeTabs();
10846         }
10847     },
10848
10849     /**
10850      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
10851      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
10852      */
10853     unhideTab : function(id){
10854         var t = this.items[id];
10855         if(t.isHidden()){
10856            t.setHidden(false);
10857            this.hiddenCount--;
10858            this.autoSizeTabs();
10859         }
10860     },
10861
10862     /**
10863      * Adds an existing {@link Roo.TabPanelItem}.
10864      * @param {Roo.TabPanelItem} item The TabPanelItem to add
10865      */
10866     addTabItem : function(item){
10867         this.items[item.id] = item;
10868         this.items.push(item);
10869         if(this.resizeTabs){
10870            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
10871            this.autoSizeTabs();
10872         }else{
10873             item.autoSize();
10874         }
10875     },
10876
10877     /**
10878      * Removes a {@link Roo.TabPanelItem}.
10879      * @param {String/Number} id The id or index of the TabPanelItem to remove.
10880      */
10881     removeTab : function(id){
10882         var items = this.items;
10883         var tab = items[id];
10884         if(!tab) { return; }
10885         var index = items.indexOf(tab);
10886         if(this.active == tab && items.length > 1){
10887             var newTab = this.getNextAvailable(index);
10888             if(newTab) {
10889                 newTab.activate();
10890             }
10891         }
10892         this.stripEl.dom.removeChild(tab.pnode.dom);
10893         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
10894             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
10895         }
10896         items.splice(index, 1);
10897         delete this.items[tab.id];
10898         tab.fireEvent("close", tab);
10899         tab.purgeListeners();
10900         this.autoSizeTabs();
10901     },
10902
10903     getNextAvailable : function(start){
10904         var items = this.items;
10905         var index = start;
10906         // look for a next tab that will slide over to
10907         // replace the one being removed
10908         while(index < items.length){
10909             var item = items[++index];
10910             if(item && !item.isHidden()){
10911                 return item;
10912             }
10913         }
10914         // if one isn't found select the previous tab (on the left)
10915         index = start;
10916         while(index >= 0){
10917             var item = items[--index];
10918             if(item && !item.isHidden()){
10919                 return item;
10920             }
10921         }
10922         return null;
10923     },
10924
10925     /**
10926      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
10927      * @param {String/Number} id The id or index of the TabPanelItem to disable.
10928      */
10929     disableTab : function(id){
10930         var tab = this.items[id];
10931         if(tab && this.active != tab){
10932             tab.disable();
10933         }
10934     },
10935
10936     /**
10937      * Enables a {@link Roo.TabPanelItem} that is disabled.
10938      * @param {String/Number} id The id or index of the TabPanelItem to enable.
10939      */
10940     enableTab : function(id){
10941         var tab = this.items[id];
10942         tab.enable();
10943     },
10944
10945     /**
10946      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
10947      * @param {String/Number} id The id or index of the TabPanelItem to activate.
10948      * @return {Roo.TabPanelItem} The TabPanelItem.
10949      */
10950     activate : function(id){
10951         var tab = this.items[id];
10952         if(!tab){
10953             return null;
10954         }
10955         if(tab == this.active || tab.disabled){
10956             return tab;
10957         }
10958         var e = {};
10959         this.fireEvent("beforetabchange", this, e, tab);
10960         if(e.cancel !== true && !tab.disabled){
10961             if(this.active){
10962                 this.active.hide();
10963             }
10964             this.active = this.items[id];
10965             this.active.show();
10966             this.fireEvent("tabchange", this, this.active);
10967         }
10968         return tab;
10969     },
10970
10971     /**
10972      * Gets the active {@link Roo.TabPanelItem}.
10973      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
10974      */
10975     getActiveTab : function(){
10976         return this.active;
10977     },
10978
10979     /**
10980      * Updates the tab body element to fit the height of the container element
10981      * for overflow scrolling
10982      * @param {Number} targetHeight (optional) Override the starting height from the elements height
10983      */
10984     syncHeight : function(targetHeight){
10985         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
10986         var bm = this.bodyEl.getMargins();
10987         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
10988         this.bodyEl.setHeight(newHeight);
10989         return newHeight;
10990     },
10991
10992     onResize : function(){
10993         if(this.monitorResize){
10994             this.autoSizeTabs();
10995         }
10996     },
10997
10998     /**
10999      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11000      */
11001     beginUpdate : function(){
11002         this.updating = true;
11003     },
11004
11005     /**
11006      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11007      */
11008     endUpdate : function(){
11009         this.updating = false;
11010         this.autoSizeTabs();
11011     },
11012
11013     /**
11014      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11015      */
11016     autoSizeTabs : function(){
11017         var count = this.items.length;
11018         var vcount = count - this.hiddenCount;
11019         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11020         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11021         var availWidth = Math.floor(w / vcount);
11022         var b = this.stripBody;
11023         if(b.getWidth() > w){
11024             var tabs = this.items;
11025             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11026             if(availWidth < this.minTabWidth){
11027                 /*if(!this.sleft){    // incomplete scrolling code
11028                     this.createScrollButtons();
11029                 }
11030                 this.showScroll();
11031                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11032             }
11033         }else{
11034             if(this.currentTabWidth < this.preferredTabWidth){
11035                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11036             }
11037         }
11038     },
11039
11040     /**
11041      * Returns the number of tabs in this TabPanel.
11042      * @return {Number}
11043      */
11044      getCount : function(){
11045          return this.items.length;
11046      },
11047
11048     /**
11049      * Resizes all the tabs to the passed width
11050      * @param {Number} The new width
11051      */
11052     setTabWidth : function(width){
11053         this.currentTabWidth = width;
11054         for(var i = 0, len = this.items.length; i < len; i++) {
11055                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11056         }
11057     },
11058
11059     /**
11060      * Destroys this TabPanel
11061      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11062      */
11063     destroy : function(removeEl){
11064         Roo.EventManager.removeResizeListener(this.onResize, this);
11065         for(var i = 0, len = this.items.length; i < len; i++){
11066             this.items[i].purgeListeners();
11067         }
11068         if(removeEl === true){
11069             this.el.update("");
11070             this.el.remove();
11071         }
11072     }
11073 });
11074
11075 /**
11076  * @class Roo.TabPanelItem
11077  * @extends Roo.util.Observable
11078  * Represents an individual item (tab plus body) in a TabPanel.
11079  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11080  * @param {String} id The id of this TabPanelItem
11081  * @param {String} text The text for the tab of this TabPanelItem
11082  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11083  */
11084 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11085     /**
11086      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11087      * @type Roo.TabPanel
11088      */
11089     this.tabPanel = tabPanel;
11090     /**
11091      * The id for this TabPanelItem
11092      * @type String
11093      */
11094     this.id = id;
11095     /** @private */
11096     this.disabled = false;
11097     /** @private */
11098     this.text = text;
11099     /** @private */
11100     this.loaded = false;
11101     this.closable = closable;
11102
11103     /**
11104      * The body element for this TabPanelItem.
11105      * @type Roo.Element
11106      */
11107     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11108     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11109     this.bodyEl.setStyle("display", "block");
11110     this.bodyEl.setStyle("zoom", "1");
11111     this.hideAction();
11112
11113     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11114     /** @private */
11115     this.el = Roo.get(els.el, true);
11116     this.inner = Roo.get(els.inner, true);
11117     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11118     this.pnode = Roo.get(els.el.parentNode, true);
11119     this.el.on("mousedown", this.onTabMouseDown, this);
11120     this.el.on("click", this.onTabClick, this);
11121     /** @private */
11122     if(closable){
11123         var c = Roo.get(els.close, true);
11124         c.dom.title = this.closeText;
11125         c.addClassOnOver("close-over");
11126         c.on("click", this.closeClick, this);
11127      }
11128
11129     this.addEvents({
11130          /**
11131          * @event activate
11132          * Fires when this tab becomes the active tab.
11133          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11134          * @param {Roo.TabPanelItem} this
11135          */
11136         "activate": true,
11137         /**
11138          * @event beforeclose
11139          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11140          * @param {Roo.TabPanelItem} this
11141          * @param {Object} e Set cancel to true on this object to cancel the close.
11142          */
11143         "beforeclose": true,
11144         /**
11145          * @event close
11146          * Fires when this tab is closed.
11147          * @param {Roo.TabPanelItem} this
11148          */
11149          "close": true,
11150         /**
11151          * @event deactivate
11152          * Fires when this tab is no longer the active tab.
11153          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11154          * @param {Roo.TabPanelItem} this
11155          */
11156          "deactivate" : true
11157     });
11158     this.hidden = false;
11159
11160     Roo.TabPanelItem.superclass.constructor.call(this);
11161 };
11162
11163 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11164     purgeListeners : function(){
11165        Roo.util.Observable.prototype.purgeListeners.call(this);
11166        this.el.removeAllListeners();
11167     },
11168     /**
11169      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11170      */
11171     show : function(){
11172         this.pnode.addClass("on");
11173         this.showAction();
11174         if(Roo.isOpera){
11175             this.tabPanel.stripWrap.repaint();
11176         }
11177         this.fireEvent("activate", this.tabPanel, this);
11178     },
11179
11180     /**
11181      * Returns true if this tab is the active tab.
11182      * @return {Boolean}
11183      */
11184     isActive : function(){
11185         return this.tabPanel.getActiveTab() == this;
11186     },
11187
11188     /**
11189      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11190      */
11191     hide : function(){
11192         this.pnode.removeClass("on");
11193         this.hideAction();
11194         this.fireEvent("deactivate", this.tabPanel, this);
11195     },
11196
11197     hideAction : function(){
11198         this.bodyEl.hide();
11199         this.bodyEl.setStyle("position", "absolute");
11200         this.bodyEl.setLeft("-20000px");
11201         this.bodyEl.setTop("-20000px");
11202     },
11203
11204     showAction : function(){
11205         this.bodyEl.setStyle("position", "relative");
11206         this.bodyEl.setTop("");
11207         this.bodyEl.setLeft("");
11208         this.bodyEl.show();
11209     },
11210
11211     /**
11212      * Set the tooltip for the tab.
11213      * @param {String} tooltip The tab's tooltip
11214      */
11215     setTooltip : function(text){
11216         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11217             this.textEl.dom.qtip = text;
11218             this.textEl.dom.removeAttribute('title');
11219         }else{
11220             this.textEl.dom.title = text;
11221         }
11222     },
11223
11224     onTabClick : function(e){
11225         e.preventDefault();
11226         this.tabPanel.activate(this.id);
11227     },
11228
11229     onTabMouseDown : function(e){
11230         e.preventDefault();
11231         this.tabPanel.activate(this.id);
11232     },
11233
11234     getWidth : function(){
11235         return this.inner.getWidth();
11236     },
11237
11238     setWidth : function(width){
11239         var iwidth = width - this.pnode.getPadding("lr");
11240         this.inner.setWidth(iwidth);
11241         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11242         this.pnode.setWidth(width);
11243     },
11244
11245     /**
11246      * Show or hide the tab
11247      * @param {Boolean} hidden True to hide or false to show.
11248      */
11249     setHidden : function(hidden){
11250         this.hidden = hidden;
11251         this.pnode.setStyle("display", hidden ? "none" : "");
11252     },
11253
11254     /**
11255      * Returns true if this tab is "hidden"
11256      * @return {Boolean}
11257      */
11258     isHidden : function(){
11259         return this.hidden;
11260     },
11261
11262     /**
11263      * Returns the text for this tab
11264      * @return {String}
11265      */
11266     getText : function(){
11267         return this.text;
11268     },
11269
11270     autoSize : function(){
11271         //this.el.beginMeasure();
11272         this.textEl.setWidth(1);
11273         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11274         //this.el.endMeasure();
11275     },
11276
11277     /**
11278      * Sets the text for the tab (Note: this also sets the tooltip text)
11279      * @param {String} text The tab's text and tooltip
11280      */
11281     setText : function(text){
11282         this.text = text;
11283         this.textEl.update(text);
11284         this.setTooltip(text);
11285         if(!this.tabPanel.resizeTabs){
11286             this.autoSize();
11287         }
11288     },
11289     /**
11290      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11291      */
11292     activate : function(){
11293         this.tabPanel.activate(this.id);
11294     },
11295
11296     /**
11297      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11298      */
11299     disable : function(){
11300         if(this.tabPanel.active != this){
11301             this.disabled = true;
11302             this.pnode.addClass("disabled");
11303         }
11304     },
11305
11306     /**
11307      * Enables this TabPanelItem if it was previously disabled.
11308      */
11309     enable : function(){
11310         this.disabled = false;
11311         this.pnode.removeClass("disabled");
11312     },
11313
11314     /**
11315      * Sets the content for this TabPanelItem.
11316      * @param {String} content The content
11317      * @param {Boolean} loadScripts true to look for and load scripts
11318      */
11319     setContent : function(content, loadScripts){
11320         this.bodyEl.update(content, loadScripts);
11321     },
11322
11323     /**
11324      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11325      * @return {Roo.UpdateManager} The UpdateManager
11326      */
11327     getUpdateManager : function(){
11328         return this.bodyEl.getUpdateManager();
11329     },
11330
11331     /**
11332      * Set a URL to be used to load the content for this TabPanelItem.
11333      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11334      * @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)
11335      * @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)
11336      * @return {Roo.UpdateManager} The UpdateManager
11337      */
11338     setUrl : function(url, params, loadOnce){
11339         if(this.refreshDelegate){
11340             this.un('activate', this.refreshDelegate);
11341         }
11342         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11343         this.on("activate", this.refreshDelegate);
11344         return this.bodyEl.getUpdateManager();
11345     },
11346
11347     /** @private */
11348     _handleRefresh : function(url, params, loadOnce){
11349         if(!loadOnce || !this.loaded){
11350             var updater = this.bodyEl.getUpdateManager();
11351             updater.update(url, params, this._setLoaded.createDelegate(this));
11352         }
11353     },
11354
11355     /**
11356      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11357      *   Will fail silently if the setUrl method has not been called.
11358      *   This does not activate the panel, just updates its content.
11359      */
11360     refresh : function(){
11361         if(this.refreshDelegate){
11362            this.loaded = false;
11363            this.refreshDelegate();
11364         }
11365     },
11366
11367     /** @private */
11368     _setLoaded : function(){
11369         this.loaded = true;
11370     },
11371
11372     /** @private */
11373     closeClick : function(e){
11374         var o = {};
11375         e.stopEvent();
11376         this.fireEvent("beforeclose", this, o);
11377         if(o.cancel !== true){
11378             this.tabPanel.removeTab(this.id);
11379         }
11380     },
11381     /**
11382      * The text displayed in the tooltip for the close icon.
11383      * @type String
11384      */
11385     closeText : "Close this tab"
11386 });
11387
11388 /** @private */
11389 Roo.TabPanel.prototype.createStrip = function(container){
11390     var strip = document.createElement("div");
11391     strip.className = "x-tabs-wrap";
11392     container.appendChild(strip);
11393     return strip;
11394 };
11395 /** @private */
11396 Roo.TabPanel.prototype.createStripList = function(strip){
11397     // div wrapper for retard IE
11398     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>';
11399     return strip.firstChild.firstChild.firstChild.firstChild;
11400 };
11401 /** @private */
11402 Roo.TabPanel.prototype.createBody = function(container){
11403     var body = document.createElement("div");
11404     Roo.id(body, "tab-body");
11405     Roo.fly(body).addClass("x-tabs-body");
11406     container.appendChild(body);
11407     return body;
11408 };
11409 /** @private */
11410 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11411     var body = Roo.getDom(id);
11412     if(!body){
11413         body = document.createElement("div");
11414         body.id = id;
11415     }
11416     Roo.fly(body).addClass("x-tabs-item-body");
11417     bodyEl.insertBefore(body, bodyEl.firstChild);
11418     return body;
11419 };
11420 /** @private */
11421 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11422     var td = document.createElement("td");
11423     stripEl.appendChild(td);
11424     if(closable){
11425         td.className = "x-tabs-closable";
11426         if(!this.closeTpl){
11427             this.closeTpl = new Roo.Template(
11428                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11429                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11430                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11431             );
11432         }
11433         var el = this.closeTpl.overwrite(td, {"text": text});
11434         var close = el.getElementsByTagName("div")[0];
11435         var inner = el.getElementsByTagName("em")[0];
11436         return {"el": el, "close": close, "inner": inner};
11437     } else {
11438         if(!this.tabTpl){
11439             this.tabTpl = new Roo.Template(
11440                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11441                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11442             );
11443         }
11444         var el = this.tabTpl.overwrite(td, {"text": text});
11445         var inner = el.getElementsByTagName("em")[0];
11446         return {"el": el, "inner": inner};
11447     }
11448 };/*
11449  * Based on:
11450  * Ext JS Library 1.1.1
11451  * Copyright(c) 2006-2007, Ext JS, LLC.
11452  *
11453  * Originally Released Under LGPL - original licence link has changed is not relivant.
11454  *
11455  * Fork - LGPL
11456  * <script type="text/javascript">
11457  */
11458
11459 /**
11460  * @class Roo.Button
11461  * @extends Roo.util.Observable
11462  * Simple Button class
11463  * @cfg {String} text The button text
11464  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11465  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11466  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11467  * @cfg {Object} scope The scope of the handler
11468  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11469  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11470  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11471  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11472  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11473  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11474    applies if enableToggle = true)
11475  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11476  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11477   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11478  * @constructor
11479  * Create a new button
11480  * @param {Object} config The config object
11481  */
11482 Roo.Button = function(renderTo, config)
11483 {
11484     if (!config) {
11485         config = renderTo;
11486         renderTo = config.renderTo || false;
11487     }
11488     
11489     Roo.apply(this, config);
11490     this.addEvents({
11491         /**
11492              * @event click
11493              * Fires when this button is clicked
11494              * @param {Button} this
11495              * @param {EventObject} e The click event
11496              */
11497             "click" : true,
11498         /**
11499              * @event toggle
11500              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11501              * @param {Button} this
11502              * @param {Boolean} pressed
11503              */
11504             "toggle" : true,
11505         /**
11506              * @event mouseover
11507              * Fires when the mouse hovers over the button
11508              * @param {Button} this
11509              * @param {Event} e The event object
11510              */
11511         'mouseover' : true,
11512         /**
11513              * @event mouseout
11514              * Fires when the mouse exits the button
11515              * @param {Button} this
11516              * @param {Event} e The event object
11517              */
11518         'mouseout': true,
11519          /**
11520              * @event render
11521              * Fires when the button is rendered
11522              * @param {Button} this
11523              */
11524         'render': true
11525     });
11526     if(this.menu){
11527         this.menu = Roo.menu.MenuMgr.get(this.menu);
11528     }
11529     // register listeners first!!  - so render can be captured..
11530     Roo.util.Observable.call(this);
11531     if(renderTo){
11532         this.render(renderTo);
11533     }
11534     
11535   
11536 };
11537
11538 Roo.extend(Roo.Button, Roo.util.Observable, {
11539     /**
11540      * 
11541      */
11542     
11543     /**
11544      * Read-only. True if this button is hidden
11545      * @type Boolean
11546      */
11547     hidden : false,
11548     /**
11549      * Read-only. True if this button is disabled
11550      * @type Boolean
11551      */
11552     disabled : false,
11553     /**
11554      * Read-only. True if this button is pressed (only if enableToggle = true)
11555      * @type Boolean
11556      */
11557     pressed : false,
11558
11559     /**
11560      * @cfg {Number} tabIndex 
11561      * The DOM tabIndex for this button (defaults to undefined)
11562      */
11563     tabIndex : undefined,
11564
11565     /**
11566      * @cfg {Boolean} enableToggle
11567      * True to enable pressed/not pressed toggling (defaults to false)
11568      */
11569     enableToggle: false,
11570     /**
11571      * @cfg {Mixed} menu
11572      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11573      */
11574     menu : undefined,
11575     /**
11576      * @cfg {String} menuAlign
11577      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11578      */
11579     menuAlign : "tl-bl?",
11580
11581     /**
11582      * @cfg {String} iconCls
11583      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11584      */
11585     iconCls : undefined,
11586     /**
11587      * @cfg {String} type
11588      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11589      */
11590     type : 'button',
11591
11592     // private
11593     menuClassTarget: 'tr',
11594
11595     /**
11596      * @cfg {String} clickEvent
11597      * The type of event to map to the button's event handler (defaults to 'click')
11598      */
11599     clickEvent : 'click',
11600
11601     /**
11602      * @cfg {Boolean} handleMouseEvents
11603      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11604      */
11605     handleMouseEvents : true,
11606
11607     /**
11608      * @cfg {String} tooltipType
11609      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11610      */
11611     tooltipType : 'qtip',
11612
11613     /**
11614      * @cfg {String} cls
11615      * A CSS class to apply to the button's main element.
11616      */
11617     
11618     /**
11619      * @cfg {Roo.Template} template (Optional)
11620      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11621      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11622      * require code modifications if required elements (e.g. a button) aren't present.
11623      */
11624
11625     // private
11626     render : function(renderTo){
11627         var btn;
11628         if(this.hideParent){
11629             this.parentEl = Roo.get(renderTo);
11630         }
11631         if(!this.dhconfig){
11632             if(!this.template){
11633                 if(!Roo.Button.buttonTemplate){
11634                     // hideous table template
11635                     Roo.Button.buttonTemplate = new Roo.Template(
11636                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11637                         '<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>',
11638                         "</tr></tbody></table>");
11639                 }
11640                 this.template = Roo.Button.buttonTemplate;
11641             }
11642             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11643             var btnEl = btn.child("button:first");
11644             btnEl.on('focus', this.onFocus, this);
11645             btnEl.on('blur', this.onBlur, this);
11646             if(this.cls){
11647                 btn.addClass(this.cls);
11648             }
11649             if(this.icon){
11650                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11651             }
11652             if(this.iconCls){
11653                 btnEl.addClass(this.iconCls);
11654                 if(!this.cls){
11655                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11656                 }
11657             }
11658             if(this.tabIndex !== undefined){
11659                 btnEl.dom.tabIndex = this.tabIndex;
11660             }
11661             if(this.tooltip){
11662                 if(typeof this.tooltip == 'object'){
11663                     Roo.QuickTips.tips(Roo.apply({
11664                           target: btnEl.id
11665                     }, this.tooltip));
11666                 } else {
11667                     btnEl.dom[this.tooltipType] = this.tooltip;
11668                 }
11669             }
11670         }else{
11671             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11672         }
11673         this.el = btn;
11674         if(this.id){
11675             this.el.dom.id = this.el.id = this.id;
11676         }
11677         if(this.menu){
11678             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11679             this.menu.on("show", this.onMenuShow, this);
11680             this.menu.on("hide", this.onMenuHide, this);
11681         }
11682         btn.addClass("x-btn");
11683         if(Roo.isIE && !Roo.isIE7){
11684             this.autoWidth.defer(1, this);
11685         }else{
11686             this.autoWidth();
11687         }
11688         if(this.handleMouseEvents){
11689             btn.on("mouseover", this.onMouseOver, this);
11690             btn.on("mouseout", this.onMouseOut, this);
11691             btn.on("mousedown", this.onMouseDown, this);
11692         }
11693         btn.on(this.clickEvent, this.onClick, this);
11694         //btn.on("mouseup", this.onMouseUp, this);
11695         if(this.hidden){
11696             this.hide();
11697         }
11698         if(this.disabled){
11699             this.disable();
11700         }
11701         Roo.ButtonToggleMgr.register(this);
11702         if(this.pressed){
11703             this.el.addClass("x-btn-pressed");
11704         }
11705         if(this.repeat){
11706             var repeater = new Roo.util.ClickRepeater(btn,
11707                 typeof this.repeat == "object" ? this.repeat : {}
11708             );
11709             repeater.on("click", this.onClick,  this);
11710         }
11711         
11712         this.fireEvent('render', this);
11713         
11714     },
11715     /**
11716      * Returns the button's underlying element
11717      * @return {Roo.Element} The element
11718      */
11719     getEl : function(){
11720         return this.el;  
11721     },
11722     
11723     /**
11724      * Destroys this Button and removes any listeners.
11725      */
11726     destroy : function(){
11727         Roo.ButtonToggleMgr.unregister(this);
11728         this.el.removeAllListeners();
11729         this.purgeListeners();
11730         this.el.remove();
11731     },
11732
11733     // private
11734     autoWidth : function(){
11735         if(this.el){
11736             this.el.setWidth("auto");
11737             if(Roo.isIE7 && Roo.isStrict){
11738                 var ib = this.el.child('button');
11739                 if(ib && ib.getWidth() > 20){
11740                     ib.clip();
11741                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11742                 }
11743             }
11744             if(this.minWidth){
11745                 if(this.hidden){
11746                     this.el.beginMeasure();
11747                 }
11748                 if(this.el.getWidth() < this.minWidth){
11749                     this.el.setWidth(this.minWidth);
11750                 }
11751                 if(this.hidden){
11752                     this.el.endMeasure();
11753                 }
11754             }
11755         }
11756     },
11757
11758     /**
11759      * Assigns this button's click handler
11760      * @param {Function} handler The function to call when the button is clicked
11761      * @param {Object} scope (optional) Scope for the function passed in
11762      */
11763     setHandler : function(handler, scope){
11764         this.handler = handler;
11765         this.scope = scope;  
11766     },
11767     
11768     /**
11769      * Sets this button's text
11770      * @param {String} text The button text
11771      */
11772     setText : function(text){
11773         this.text = text;
11774         if(this.el){
11775             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11776         }
11777         this.autoWidth();
11778     },
11779     
11780     /**
11781      * Gets the text for this button
11782      * @return {String} The button text
11783      */
11784     getText : function(){
11785         return this.text;  
11786     },
11787     
11788     /**
11789      * Show this button
11790      */
11791     show: function(){
11792         this.hidden = false;
11793         if(this.el){
11794             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11795         }
11796     },
11797     
11798     /**
11799      * Hide this button
11800      */
11801     hide: function(){
11802         this.hidden = true;
11803         if(this.el){
11804             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11805         }
11806     },
11807     
11808     /**
11809      * Convenience function for boolean show/hide
11810      * @param {Boolean} visible True to show, false to hide
11811      */
11812     setVisible: function(visible){
11813         if(visible) {
11814             this.show();
11815         }else{
11816             this.hide();
11817         }
11818     },
11819     
11820     /**
11821      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11822      * @param {Boolean} state (optional) Force a particular state
11823      */
11824     toggle : function(state){
11825         state = state === undefined ? !this.pressed : state;
11826         if(state != this.pressed){
11827             if(state){
11828                 this.el.addClass("x-btn-pressed");
11829                 this.pressed = true;
11830                 this.fireEvent("toggle", this, true);
11831             }else{
11832                 this.el.removeClass("x-btn-pressed");
11833                 this.pressed = false;
11834                 this.fireEvent("toggle", this, false);
11835             }
11836             if(this.toggleHandler){
11837                 this.toggleHandler.call(this.scope || this, this, state);
11838             }
11839         }
11840     },
11841     
11842     /**
11843      * Focus the button
11844      */
11845     focus : function(){
11846         this.el.child('button:first').focus();
11847     },
11848     
11849     /**
11850      * Disable this button
11851      */
11852     disable : function(){
11853         if(this.el){
11854             this.el.addClass("x-btn-disabled");
11855         }
11856         this.disabled = true;
11857     },
11858     
11859     /**
11860      * Enable this button
11861      */
11862     enable : function(){
11863         if(this.el){
11864             this.el.removeClass("x-btn-disabled");
11865         }
11866         this.disabled = false;
11867     },
11868
11869     /**
11870      * Convenience function for boolean enable/disable
11871      * @param {Boolean} enabled True to enable, false to disable
11872      */
11873     setDisabled : function(v){
11874         this[v !== true ? "enable" : "disable"]();
11875     },
11876
11877     // private
11878     onClick : function(e){
11879         if(e){
11880             e.preventDefault();
11881         }
11882         if(e.button != 0){
11883             return;
11884         }
11885         if(!this.disabled){
11886             if(this.enableToggle){
11887                 this.toggle();
11888             }
11889             if(this.menu && !this.menu.isVisible()){
11890                 this.menu.show(this.el, this.menuAlign);
11891             }
11892             this.fireEvent("click", this, e);
11893             if(this.handler){
11894                 this.el.removeClass("x-btn-over");
11895                 this.handler.call(this.scope || this, this, e);
11896             }
11897         }
11898     },
11899     // private
11900     onMouseOver : function(e){
11901         if(!this.disabled){
11902             this.el.addClass("x-btn-over");
11903             this.fireEvent('mouseover', this, e);
11904         }
11905     },
11906     // private
11907     onMouseOut : function(e){
11908         if(!e.within(this.el,  true)){
11909             this.el.removeClass("x-btn-over");
11910             this.fireEvent('mouseout', this, e);
11911         }
11912     },
11913     // private
11914     onFocus : function(e){
11915         if(!this.disabled){
11916             this.el.addClass("x-btn-focus");
11917         }
11918     },
11919     // private
11920     onBlur : function(e){
11921         this.el.removeClass("x-btn-focus");
11922     },
11923     // private
11924     onMouseDown : function(e){
11925         if(!this.disabled && e.button == 0){
11926             this.el.addClass("x-btn-click");
11927             Roo.get(document).on('mouseup', this.onMouseUp, this);
11928         }
11929     },
11930     // private
11931     onMouseUp : function(e){
11932         if(e.button == 0){
11933             this.el.removeClass("x-btn-click");
11934             Roo.get(document).un('mouseup', this.onMouseUp, this);
11935         }
11936     },
11937     // private
11938     onMenuShow : function(e){
11939         this.el.addClass("x-btn-menu-active");
11940     },
11941     // private
11942     onMenuHide : function(e){
11943         this.el.removeClass("x-btn-menu-active");
11944     }   
11945 });
11946
11947 // Private utility class used by Button
11948 Roo.ButtonToggleMgr = function(){
11949    var groups = {};
11950    
11951    function toggleGroup(btn, state){
11952        if(state){
11953            var g = groups[btn.toggleGroup];
11954            for(var i = 0, l = g.length; i < l; i++){
11955                if(g[i] != btn){
11956                    g[i].toggle(false);
11957                }
11958            }
11959        }
11960    }
11961    
11962    return {
11963        register : function(btn){
11964            if(!btn.toggleGroup){
11965                return;
11966            }
11967            var g = groups[btn.toggleGroup];
11968            if(!g){
11969                g = groups[btn.toggleGroup] = [];
11970            }
11971            g.push(btn);
11972            btn.on("toggle", toggleGroup);
11973        },
11974        
11975        unregister : function(btn){
11976            if(!btn.toggleGroup){
11977                return;
11978            }
11979            var g = groups[btn.toggleGroup];
11980            if(g){
11981                g.remove(btn);
11982                btn.un("toggle", toggleGroup);
11983            }
11984        }
11985    };
11986 }();/*
11987  * Based on:
11988  * Ext JS Library 1.1.1
11989  * Copyright(c) 2006-2007, Ext JS, LLC.
11990  *
11991  * Originally Released Under LGPL - original licence link has changed is not relivant.
11992  *
11993  * Fork - LGPL
11994  * <script type="text/javascript">
11995  */
11996  
11997 /**
11998  * @class Roo.SplitButton
11999  * @extends Roo.Button
12000  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12001  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12002  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12003  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12004  * @cfg {String} arrowTooltip The title attribute of the arrow
12005  * @constructor
12006  * Create a new menu button
12007  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12008  * @param {Object} config The config object
12009  */
12010 Roo.SplitButton = function(renderTo, config){
12011     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12012     /**
12013      * @event arrowclick
12014      * Fires when this button's arrow is clicked
12015      * @param {SplitButton} this
12016      * @param {EventObject} e The click event
12017      */
12018     this.addEvents({"arrowclick":true});
12019 };
12020
12021 Roo.extend(Roo.SplitButton, Roo.Button, {
12022     render : function(renderTo){
12023         // this is one sweet looking template!
12024         var tpl = new Roo.Template(
12025             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12026             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12027             '<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>',
12028             "</tbody></table></td><td>",
12029             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12030             '<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>',
12031             "</tbody></table></td></tr></table>"
12032         );
12033         var btn = tpl.append(renderTo, [this.text, this.type], true);
12034         var btnEl = btn.child("button");
12035         if(this.cls){
12036             btn.addClass(this.cls);
12037         }
12038         if(this.icon){
12039             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12040         }
12041         if(this.iconCls){
12042             btnEl.addClass(this.iconCls);
12043             if(!this.cls){
12044                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12045             }
12046         }
12047         this.el = btn;
12048         if(this.handleMouseEvents){
12049             btn.on("mouseover", this.onMouseOver, this);
12050             btn.on("mouseout", this.onMouseOut, this);
12051             btn.on("mousedown", this.onMouseDown, this);
12052             btn.on("mouseup", this.onMouseUp, this);
12053         }
12054         btn.on(this.clickEvent, this.onClick, this);
12055         if(this.tooltip){
12056             if(typeof this.tooltip == 'object'){
12057                 Roo.QuickTips.tips(Roo.apply({
12058                       target: btnEl.id
12059                 }, this.tooltip));
12060             } else {
12061                 btnEl.dom[this.tooltipType] = this.tooltip;
12062             }
12063         }
12064         if(this.arrowTooltip){
12065             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12066         }
12067         if(this.hidden){
12068             this.hide();
12069         }
12070         if(this.disabled){
12071             this.disable();
12072         }
12073         if(this.pressed){
12074             this.el.addClass("x-btn-pressed");
12075         }
12076         if(Roo.isIE && !Roo.isIE7){
12077             this.autoWidth.defer(1, this);
12078         }else{
12079             this.autoWidth();
12080         }
12081         if(this.menu){
12082             this.menu.on("show", this.onMenuShow, this);
12083             this.menu.on("hide", this.onMenuHide, this);
12084         }
12085         this.fireEvent('render', this);
12086     },
12087
12088     // private
12089     autoWidth : function(){
12090         if(this.el){
12091             var tbl = this.el.child("table:first");
12092             var tbl2 = this.el.child("table:last");
12093             this.el.setWidth("auto");
12094             tbl.setWidth("auto");
12095             if(Roo.isIE7 && Roo.isStrict){
12096                 var ib = this.el.child('button:first');
12097                 if(ib && ib.getWidth() > 20){
12098                     ib.clip();
12099                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12100                 }
12101             }
12102             if(this.minWidth){
12103                 if(this.hidden){
12104                     this.el.beginMeasure();
12105                 }
12106                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12107                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12108                 }
12109                 if(this.hidden){
12110                     this.el.endMeasure();
12111                 }
12112             }
12113             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12114         } 
12115     },
12116     /**
12117      * Sets this button's click handler
12118      * @param {Function} handler The function to call when the button is clicked
12119      * @param {Object} scope (optional) Scope for the function passed above
12120      */
12121     setHandler : function(handler, scope){
12122         this.handler = handler;
12123         this.scope = scope;  
12124     },
12125     
12126     /**
12127      * Sets this button's arrow click handler
12128      * @param {Function} handler The function to call when the arrow is clicked
12129      * @param {Object} scope (optional) Scope for the function passed above
12130      */
12131     setArrowHandler : function(handler, scope){
12132         this.arrowHandler = handler;
12133         this.scope = scope;  
12134     },
12135     
12136     /**
12137      * Focus the button
12138      */
12139     focus : function(){
12140         if(this.el){
12141             this.el.child("button:first").focus();
12142         }
12143     },
12144
12145     // private
12146     onClick : function(e){
12147         e.preventDefault();
12148         if(!this.disabled){
12149             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12150                 if(this.menu && !this.menu.isVisible()){
12151                     this.menu.show(this.el, this.menuAlign);
12152                 }
12153                 this.fireEvent("arrowclick", this, e);
12154                 if(this.arrowHandler){
12155                     this.arrowHandler.call(this.scope || this, this, e);
12156                 }
12157             }else{
12158                 this.fireEvent("click", this, e);
12159                 if(this.handler){
12160                     this.handler.call(this.scope || this, this, e);
12161                 }
12162             }
12163         }
12164     },
12165     // private
12166     onMouseDown : function(e){
12167         if(!this.disabled){
12168             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12169         }
12170     },
12171     // private
12172     onMouseUp : function(e){
12173         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12174     }   
12175 });
12176
12177
12178 // backwards compat
12179 Roo.MenuButton = Roo.SplitButton;/*
12180  * Based on:
12181  * Ext JS Library 1.1.1
12182  * Copyright(c) 2006-2007, Ext JS, LLC.
12183  *
12184  * Originally Released Under LGPL - original licence link has changed is not relivant.
12185  *
12186  * Fork - LGPL
12187  * <script type="text/javascript">
12188  */
12189
12190 /**
12191  * @class Roo.Toolbar
12192  * Basic Toolbar class.
12193  * @constructor
12194  * Creates a new Toolbar
12195  * @param {Object} config The config object
12196  */ 
12197 Roo.Toolbar = function(container, buttons, config)
12198 {
12199     /// old consturctor format still supported..
12200     if(container instanceof Array){ // omit the container for later rendering
12201         buttons = container;
12202         config = buttons;
12203         container = null;
12204     }
12205     if (typeof(container) == 'object' && container.xtype) {
12206         config = container;
12207         container = config.container;
12208         buttons = config.buttons; // not really - use items!!
12209     }
12210     var xitems = [];
12211     if (config && config.items) {
12212         xitems = config.items;
12213         delete config.items;
12214     }
12215     Roo.apply(this, config);
12216     this.buttons = buttons;
12217     
12218     if(container){
12219         this.render(container);
12220     }
12221     Roo.each(xitems, function(b) {
12222         this.add(b);
12223     }, this);
12224     
12225 };
12226
12227 Roo.Toolbar.prototype = {
12228     /**
12229      * @cfg {Roo.data.Store} items
12230      * array of button configs or elements to add
12231      */
12232     
12233     /**
12234      * @cfg {String/HTMLElement/Element} container
12235      * The id or element that will contain the toolbar
12236      */
12237     // private
12238     render : function(ct){
12239         this.el = Roo.get(ct);
12240         if(this.cls){
12241             this.el.addClass(this.cls);
12242         }
12243         // using a table allows for vertical alignment
12244         // 100% width is needed by Safari...
12245         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12246         this.tr = this.el.child("tr", true);
12247         var autoId = 0;
12248         this.items = new Roo.util.MixedCollection(false, function(o){
12249             return o.id || ("item" + (++autoId));
12250         });
12251         if(this.buttons){
12252             this.add.apply(this, this.buttons);
12253             delete this.buttons;
12254         }
12255     },
12256
12257     /**
12258      * Adds element(s) to the toolbar -- this function takes a variable number of 
12259      * arguments of mixed type and adds them to the toolbar.
12260      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12261      * <ul>
12262      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12263      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12264      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12265      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12266      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12267      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12268      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12269      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12270      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12271      * </ul>
12272      * @param {Mixed} arg2
12273      * @param {Mixed} etc.
12274      */
12275     add : function(){
12276         var a = arguments, l = a.length;
12277         for(var i = 0; i < l; i++){
12278             this._add(a[i]);
12279         }
12280     },
12281     // private..
12282     _add : function(el) {
12283         
12284         if (el.xtype) {
12285             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12286         }
12287         
12288         if (el.applyTo){ // some kind of form field
12289             return this.addField(el);
12290         } 
12291         if (el.render){ // some kind of Toolbar.Item
12292             return this.addItem(el);
12293         }
12294         if (typeof el == "string"){ // string
12295             if(el == "separator" || el == "-"){
12296                 return this.addSeparator();
12297             }
12298             if (el == " "){
12299                 return this.addSpacer();
12300             }
12301             if(el == "->"){
12302                 return this.addFill();
12303             }
12304             return this.addText(el);
12305             
12306         }
12307         if(el.tagName){ // element
12308             return this.addElement(el);
12309         }
12310         if(typeof el == "object"){ // must be button config?
12311             return this.addButton(el);
12312         }
12313         // and now what?!?!
12314         return false;
12315         
12316     },
12317     
12318     /**
12319      * Add an Xtype element
12320      * @param {Object} xtype Xtype Object
12321      * @return {Object} created Object
12322      */
12323     addxtype : function(e){
12324         return this.add(e);  
12325     },
12326     
12327     /**
12328      * Returns the Element for this toolbar.
12329      * @return {Roo.Element}
12330      */
12331     getEl : function(){
12332         return this.el;  
12333     },
12334     
12335     /**
12336      * Adds a separator
12337      * @return {Roo.Toolbar.Item} The separator item
12338      */
12339     addSeparator : function(){
12340         return this.addItem(new Roo.Toolbar.Separator());
12341     },
12342
12343     /**
12344      * Adds a spacer element
12345      * @return {Roo.Toolbar.Spacer} The spacer item
12346      */
12347     addSpacer : function(){
12348         return this.addItem(new Roo.Toolbar.Spacer());
12349     },
12350
12351     /**
12352      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12353      * @return {Roo.Toolbar.Fill} The fill item
12354      */
12355     addFill : function(){
12356         return this.addItem(new Roo.Toolbar.Fill());
12357     },
12358
12359     /**
12360      * Adds any standard HTML element to the toolbar
12361      * @param {String/HTMLElement/Element} el The element or id of the element to add
12362      * @return {Roo.Toolbar.Item} The element's item
12363      */
12364     addElement : function(el){
12365         return this.addItem(new Roo.Toolbar.Item(el));
12366     },
12367     /**
12368      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12369      * @type Roo.util.MixedCollection  
12370      */
12371     items : false,
12372      
12373     /**
12374      * Adds any Toolbar.Item or subclass
12375      * @param {Roo.Toolbar.Item} item
12376      * @return {Roo.Toolbar.Item} The item
12377      */
12378     addItem : function(item){
12379         var td = this.nextBlock();
12380         item.render(td);
12381         this.items.add(item);
12382         return item;
12383     },
12384     
12385     /**
12386      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12387      * @param {Object/Array} config A button config or array of configs
12388      * @return {Roo.Toolbar.Button/Array}
12389      */
12390     addButton : function(config){
12391         if(config instanceof Array){
12392             var buttons = [];
12393             for(var i = 0, len = config.length; i < len; i++) {
12394                 buttons.push(this.addButton(config[i]));
12395             }
12396             return buttons;
12397         }
12398         var b = config;
12399         if(!(config instanceof Roo.Toolbar.Button)){
12400             b = config.split ?
12401                 new Roo.Toolbar.SplitButton(config) :
12402                 new Roo.Toolbar.Button(config);
12403         }
12404         var td = this.nextBlock();
12405         b.render(td);
12406         this.items.add(b);
12407         return b;
12408     },
12409     
12410     /**
12411      * Adds text to the toolbar
12412      * @param {String} text The text to add
12413      * @return {Roo.Toolbar.Item} The element's item
12414      */
12415     addText : function(text){
12416         return this.addItem(new Roo.Toolbar.TextItem(text));
12417     },
12418     
12419     /**
12420      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12421      * @param {Number} index The index where the item is to be inserted
12422      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12423      * @return {Roo.Toolbar.Button/Item}
12424      */
12425     insertButton : function(index, item){
12426         if(item instanceof Array){
12427             var buttons = [];
12428             for(var i = 0, len = item.length; i < len; i++) {
12429                buttons.push(this.insertButton(index + i, item[i]));
12430             }
12431             return buttons;
12432         }
12433         if (!(item instanceof Roo.Toolbar.Button)){
12434            item = new Roo.Toolbar.Button(item);
12435         }
12436         var td = document.createElement("td");
12437         this.tr.insertBefore(td, this.tr.childNodes[index]);
12438         item.render(td);
12439         this.items.insert(index, item);
12440         return item;
12441     },
12442     
12443     /**
12444      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12445      * @param {Object} config
12446      * @return {Roo.Toolbar.Item} The element's item
12447      */
12448     addDom : function(config, returnEl){
12449         var td = this.nextBlock();
12450         Roo.DomHelper.overwrite(td, config);
12451         var ti = new Roo.Toolbar.Item(td.firstChild);
12452         ti.render(td);
12453         this.items.add(ti);
12454         return ti;
12455     },
12456
12457     /**
12458      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12459      * @type Roo.util.MixedCollection  
12460      */
12461     fields : false,
12462     
12463     /**
12464      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
12465      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
12466      * @param {Roo.form.Field} field
12467      * @return {Roo.ToolbarItem}
12468      */
12469      
12470       
12471     addField : function(field) {
12472         if (!this.fields) {
12473             var autoId = 0;
12474             this.fields = new Roo.util.MixedCollection(false, function(o){
12475                 return o.id || ("item" + (++autoId));
12476             });
12477
12478         }
12479         
12480         var td = this.nextBlock();
12481         field.render(td);
12482         var ti = new Roo.Toolbar.Item(td.firstChild);
12483         ti.render(td);
12484         this.items.add(ti);
12485         this.fields.add(field);
12486         return ti;
12487     },
12488     /**
12489      * Hide the toolbar
12490      * @method hide
12491      */
12492      
12493       
12494     hide : function()
12495     {
12496         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12497         this.el.child('div').hide();
12498     },
12499     /**
12500      * Show the toolbar
12501      * @method show
12502      */
12503     show : function()
12504     {
12505         this.el.child('div').show();
12506     },
12507       
12508     // private
12509     nextBlock : function(){
12510         var td = document.createElement("td");
12511         this.tr.appendChild(td);
12512         return td;
12513     },
12514
12515     // private
12516     destroy : function(){
12517         if(this.items){ // rendered?
12518             Roo.destroy.apply(Roo, this.items.items);
12519         }
12520         if(this.fields){ // rendered?
12521             Roo.destroy.apply(Roo, this.fields.items);
12522         }
12523         Roo.Element.uncache(this.el, this.tr);
12524     }
12525 };
12526
12527 /**
12528  * @class Roo.Toolbar.Item
12529  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12530  * @constructor
12531  * Creates a new Item
12532  * @param {HTMLElement} el 
12533  */
12534 Roo.Toolbar.Item = function(el){
12535     this.el = Roo.getDom(el);
12536     this.id = Roo.id(this.el);
12537     this.hidden = false;
12538 };
12539
12540 Roo.Toolbar.Item.prototype = {
12541     
12542     /**
12543      * Get this item's HTML Element
12544      * @return {HTMLElement}
12545      */
12546     getEl : function(){
12547        return this.el;  
12548     },
12549
12550     // private
12551     render : function(td){
12552         this.td = td;
12553         td.appendChild(this.el);
12554     },
12555     
12556     /**
12557      * Removes and destroys this item.
12558      */
12559     destroy : function(){
12560         this.td.parentNode.removeChild(this.td);
12561     },
12562     
12563     /**
12564      * Shows this item.
12565      */
12566     show: function(){
12567         this.hidden = false;
12568         this.td.style.display = "";
12569     },
12570     
12571     /**
12572      * Hides this item.
12573      */
12574     hide: function(){
12575         this.hidden = true;
12576         this.td.style.display = "none";
12577     },
12578     
12579     /**
12580      * Convenience function for boolean show/hide.
12581      * @param {Boolean} visible true to show/false to hide
12582      */
12583     setVisible: function(visible){
12584         if(visible) {
12585             this.show();
12586         }else{
12587             this.hide();
12588         }
12589     },
12590     
12591     /**
12592      * Try to focus this item.
12593      */
12594     focus : function(){
12595         Roo.fly(this.el).focus();
12596     },
12597     
12598     /**
12599      * Disables this item.
12600      */
12601     disable : function(){
12602         Roo.fly(this.td).addClass("x-item-disabled");
12603         this.disabled = true;
12604         this.el.disabled = true;
12605     },
12606     
12607     /**
12608      * Enables this item.
12609      */
12610     enable : function(){
12611         Roo.fly(this.td).removeClass("x-item-disabled");
12612         this.disabled = false;
12613         this.el.disabled = false;
12614     }
12615 };
12616
12617
12618 /**
12619  * @class Roo.Toolbar.Separator
12620  * @extends Roo.Toolbar.Item
12621  * A simple toolbar separator class
12622  * @constructor
12623  * Creates a new Separator
12624  */
12625 Roo.Toolbar.Separator = function(){
12626     var s = document.createElement("span");
12627     s.className = "ytb-sep";
12628     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12629 };
12630 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12631     enable:Roo.emptyFn,
12632     disable:Roo.emptyFn,
12633     focus:Roo.emptyFn
12634 });
12635
12636 /**
12637  * @class Roo.Toolbar.Spacer
12638  * @extends Roo.Toolbar.Item
12639  * A simple element that adds extra horizontal space to a toolbar.
12640  * @constructor
12641  * Creates a new Spacer
12642  */
12643 Roo.Toolbar.Spacer = function(){
12644     var s = document.createElement("div");
12645     s.className = "ytb-spacer";
12646     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12647 };
12648 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12649     enable:Roo.emptyFn,
12650     disable:Roo.emptyFn,
12651     focus:Roo.emptyFn
12652 });
12653
12654 /**
12655  * @class Roo.Toolbar.Fill
12656  * @extends Roo.Toolbar.Spacer
12657  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12658  * @constructor
12659  * Creates a new Spacer
12660  */
12661 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12662     // private
12663     render : function(td){
12664         td.style.width = '100%';
12665         Roo.Toolbar.Fill.superclass.render.call(this, td);
12666     }
12667 });
12668
12669 /**
12670  * @class Roo.Toolbar.TextItem
12671  * @extends Roo.Toolbar.Item
12672  * A simple class that renders text directly into a toolbar.
12673  * @constructor
12674  * Creates a new TextItem
12675  * @param {String} text
12676  */
12677 Roo.Toolbar.TextItem = function(text){
12678     if (typeof(text) == 'object') {
12679         text = text.text;
12680     }
12681     var s = document.createElement("span");
12682     s.className = "ytb-text";
12683     s.innerHTML = text;
12684     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12685 };
12686 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12687     enable:Roo.emptyFn,
12688     disable:Roo.emptyFn,
12689     focus:Roo.emptyFn
12690 });
12691
12692 /**
12693  * @class Roo.Toolbar.Button
12694  * @extends Roo.Button
12695  * A button that renders into a toolbar.
12696  * @constructor
12697  * Creates a new Button
12698  * @param {Object} config A standard {@link Roo.Button} config object
12699  */
12700 Roo.Toolbar.Button = function(config){
12701     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12702 };
12703 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12704     render : function(td){
12705         this.td = td;
12706         Roo.Toolbar.Button.superclass.render.call(this, td);
12707     },
12708     
12709     /**
12710      * Removes and destroys this button
12711      */
12712     destroy : function(){
12713         Roo.Toolbar.Button.superclass.destroy.call(this);
12714         this.td.parentNode.removeChild(this.td);
12715     },
12716     
12717     /**
12718      * Shows this button
12719      */
12720     show: function(){
12721         this.hidden = false;
12722         this.td.style.display = "";
12723     },
12724     
12725     /**
12726      * Hides this button
12727      */
12728     hide: function(){
12729         this.hidden = true;
12730         this.td.style.display = "none";
12731     },
12732
12733     /**
12734      * Disables this item
12735      */
12736     disable : function(){
12737         Roo.fly(this.td).addClass("x-item-disabled");
12738         this.disabled = true;
12739     },
12740
12741     /**
12742      * Enables this item
12743      */
12744     enable : function(){
12745         Roo.fly(this.td).removeClass("x-item-disabled");
12746         this.disabled = false;
12747     }
12748 });
12749 // backwards compat
12750 Roo.ToolbarButton = Roo.Toolbar.Button;
12751
12752 /**
12753  * @class Roo.Toolbar.SplitButton
12754  * @extends Roo.SplitButton
12755  * A menu button that renders into a toolbar.
12756  * @constructor
12757  * Creates a new SplitButton
12758  * @param {Object} config A standard {@link Roo.SplitButton} config object
12759  */
12760 Roo.Toolbar.SplitButton = function(config){
12761     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12762 };
12763 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12764     render : function(td){
12765         this.td = td;
12766         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12767     },
12768     
12769     /**
12770      * Removes and destroys this button
12771      */
12772     destroy : function(){
12773         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12774         this.td.parentNode.removeChild(this.td);
12775     },
12776     
12777     /**
12778      * Shows this button
12779      */
12780     show: function(){
12781         this.hidden = false;
12782         this.td.style.display = "";
12783     },
12784     
12785     /**
12786      * Hides this button
12787      */
12788     hide: function(){
12789         this.hidden = true;
12790         this.td.style.display = "none";
12791     }
12792 });
12793
12794 // backwards compat
12795 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12796  * Based on:
12797  * Ext JS Library 1.1.1
12798  * Copyright(c) 2006-2007, Ext JS, LLC.
12799  *
12800  * Originally Released Under LGPL - original licence link has changed is not relivant.
12801  *
12802  * Fork - LGPL
12803  * <script type="text/javascript">
12804  */
12805  
12806 /**
12807  * @class Roo.PagingToolbar
12808  * @extends Roo.Toolbar
12809  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12810  * @constructor
12811  * Create a new PagingToolbar
12812  * @param {Object} config The config object
12813  */
12814 Roo.PagingToolbar = function(el, ds, config)
12815 {
12816     // old args format still supported... - xtype is prefered..
12817     if (typeof(el) == 'object' && el.xtype) {
12818         // created from xtype...
12819         config = el;
12820         ds = el.dataSource;
12821         el = config.container;
12822     }
12823     var items = [];
12824     if (config.items) {
12825         items = config.items;
12826         config.items = [];
12827     }
12828     
12829     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12830     this.ds = ds;
12831     this.cursor = 0;
12832     this.renderButtons(this.el);
12833     this.bind(ds);
12834     
12835     // supprot items array.
12836    
12837     Roo.each(items, function(e) {
12838         this.add(Roo.factory(e));
12839     },this);
12840     
12841 };
12842
12843 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
12844     /**
12845      * @cfg {Roo.data.Store} dataSource
12846      * The underlying data store providing the paged data
12847      */
12848     /**
12849      * @cfg {String/HTMLElement/Element} container
12850      * container The id or element that will contain the toolbar
12851      */
12852     /**
12853      * @cfg {Boolean} displayInfo
12854      * True to display the displayMsg (defaults to false)
12855      */
12856     /**
12857      * @cfg {Number} pageSize
12858      * The number of records to display per page (defaults to 20)
12859      */
12860     pageSize: 20,
12861     /**
12862      * @cfg {String} displayMsg
12863      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
12864      */
12865     displayMsg : 'Displaying {0} - {1} of {2}',
12866     /**
12867      * @cfg {String} emptyMsg
12868      * The message to display when no records are found (defaults to "No data to display")
12869      */
12870     emptyMsg : 'No data to display',
12871     /**
12872      * Customizable piece of the default paging text (defaults to "Page")
12873      * @type String
12874      */
12875     beforePageText : "Page",
12876     /**
12877      * Customizable piece of the default paging text (defaults to "of %0")
12878      * @type String
12879      */
12880     afterPageText : "of {0}",
12881     /**
12882      * Customizable piece of the default paging text (defaults to "First Page")
12883      * @type String
12884      */
12885     firstText : "First Page",
12886     /**
12887      * Customizable piece of the default paging text (defaults to "Previous Page")
12888      * @type String
12889      */
12890     prevText : "Previous Page",
12891     /**
12892      * Customizable piece of the default paging text (defaults to "Next Page")
12893      * @type String
12894      */
12895     nextText : "Next Page",
12896     /**
12897      * Customizable piece of the default paging text (defaults to "Last Page")
12898      * @type String
12899      */
12900     lastText : "Last Page",
12901     /**
12902      * Customizable piece of the default paging text (defaults to "Refresh")
12903      * @type String
12904      */
12905     refreshText : "Refresh",
12906
12907     // private
12908     renderButtons : function(el){
12909         Roo.PagingToolbar.superclass.render.call(this, el);
12910         this.first = this.addButton({
12911             tooltip: this.firstText,
12912             cls: "x-btn-icon x-grid-page-first",
12913             disabled: true,
12914             handler: this.onClick.createDelegate(this, ["first"])
12915         });
12916         this.prev = this.addButton({
12917             tooltip: this.prevText,
12918             cls: "x-btn-icon x-grid-page-prev",
12919             disabled: true,
12920             handler: this.onClick.createDelegate(this, ["prev"])
12921         });
12922         //this.addSeparator();
12923         this.add(this.beforePageText);
12924         this.field = Roo.get(this.addDom({
12925            tag: "input",
12926            type: "text",
12927            size: "3",
12928            value: "1",
12929            cls: "x-grid-page-number"
12930         }).el);
12931         this.field.on("keydown", this.onPagingKeydown, this);
12932         this.field.on("focus", function(){this.dom.select();});
12933         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
12934         this.field.setHeight(18);
12935         //this.addSeparator();
12936         this.next = this.addButton({
12937             tooltip: this.nextText,
12938             cls: "x-btn-icon x-grid-page-next",
12939             disabled: true,
12940             handler: this.onClick.createDelegate(this, ["next"])
12941         });
12942         this.last = this.addButton({
12943             tooltip: this.lastText,
12944             cls: "x-btn-icon x-grid-page-last",
12945             disabled: true,
12946             handler: this.onClick.createDelegate(this, ["last"])
12947         });
12948         //this.addSeparator();
12949         this.loading = this.addButton({
12950             tooltip: this.refreshText,
12951             cls: "x-btn-icon x-grid-loading",
12952             handler: this.onClick.createDelegate(this, ["refresh"])
12953         });
12954
12955         if(this.displayInfo){
12956             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
12957         }
12958     },
12959
12960     // private
12961     updateInfo : function(){
12962         if(this.displayEl){
12963             var count = this.ds.getCount();
12964             var msg = count == 0 ?
12965                 this.emptyMsg :
12966                 String.format(
12967                     this.displayMsg,
12968                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
12969                 );
12970             this.displayEl.update(msg);
12971         }
12972     },
12973
12974     // private
12975     onLoad : function(ds, r, o){
12976        this.cursor = o.params ? o.params.start : 0;
12977        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
12978
12979        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
12980        this.field.dom.value = ap;
12981        this.first.setDisabled(ap == 1);
12982        this.prev.setDisabled(ap == 1);
12983        this.next.setDisabled(ap == ps);
12984        this.last.setDisabled(ap == ps);
12985        this.loading.enable();
12986        this.updateInfo();
12987     },
12988
12989     // private
12990     getPageData : function(){
12991         var total = this.ds.getTotalCount();
12992         return {
12993             total : total,
12994             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
12995             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
12996         };
12997     },
12998
12999     // private
13000     onLoadError : function(){
13001         this.loading.enable();
13002     },
13003
13004     // private
13005     onPagingKeydown : function(e){
13006         var k = e.getKey();
13007         var d = this.getPageData();
13008         if(k == e.RETURN){
13009             var v = this.field.dom.value, pageNum;
13010             if(!v || isNaN(pageNum = parseInt(v, 10))){
13011                 this.field.dom.value = d.activePage;
13012                 return;
13013             }
13014             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13015             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13016             e.stopEvent();
13017         }
13018         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))
13019         {
13020           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13021           this.field.dom.value = pageNum;
13022           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13023           e.stopEvent();
13024         }
13025         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13026         {
13027           var v = this.field.dom.value, pageNum; 
13028           var increment = (e.shiftKey) ? 10 : 1;
13029           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13030             increment *= -1;
13031           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13032             this.field.dom.value = d.activePage;
13033             return;
13034           }
13035           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13036           {
13037             this.field.dom.value = parseInt(v, 10) + increment;
13038             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13039             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13040           }
13041           e.stopEvent();
13042         }
13043     },
13044
13045     // private
13046     beforeLoad : function(){
13047         if(this.loading){
13048             this.loading.disable();
13049         }
13050     },
13051
13052     // private
13053     onClick : function(which){
13054         var ds = this.ds;
13055         switch(which){
13056             case "first":
13057                 ds.load({params:{start: 0, limit: this.pageSize}});
13058             break;
13059             case "prev":
13060                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13061             break;
13062             case "next":
13063                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13064             break;
13065             case "last":
13066                 var total = ds.getTotalCount();
13067                 var extra = total % this.pageSize;
13068                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13069                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13070             break;
13071             case "refresh":
13072                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13073             break;
13074         }
13075     },
13076
13077     /**
13078      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13079      * @param {Roo.data.Store} store The data store to unbind
13080      */
13081     unbind : function(ds){
13082         ds.un("beforeload", this.beforeLoad, this);
13083         ds.un("load", this.onLoad, this);
13084         ds.un("loadexception", this.onLoadError, this);
13085         ds.un("remove", this.updateInfo, this);
13086         ds.un("add", this.updateInfo, this);
13087         this.ds = undefined;
13088     },
13089
13090     /**
13091      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13092      * @param {Roo.data.Store} store The data store to bind
13093      */
13094     bind : function(ds){
13095         ds.on("beforeload", this.beforeLoad, this);
13096         ds.on("load", this.onLoad, this);
13097         ds.on("loadexception", this.onLoadError, this);
13098         ds.on("remove", this.updateInfo, this);
13099         ds.on("add", this.updateInfo, this);
13100         this.ds = ds;
13101     }
13102 });/*
13103  * Based on:
13104  * Ext JS Library 1.1.1
13105  * Copyright(c) 2006-2007, Ext JS, LLC.
13106  *
13107  * Originally Released Under LGPL - original licence link has changed is not relivant.
13108  *
13109  * Fork - LGPL
13110  * <script type="text/javascript">
13111  */
13112
13113 /**
13114  * @class Roo.Resizable
13115  * @extends Roo.util.Observable
13116  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13117  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13118  * 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
13119  * the element will be wrapped for you automatically.</p>
13120  * <p>Here is the list of valid resize handles:</p>
13121  * <pre>
13122 Value   Description
13123 ------  -------------------
13124  'n'     north
13125  's'     south
13126  'e'     east
13127  'w'     west
13128  'nw'    northwest
13129  'sw'    southwest
13130  'se'    southeast
13131  'ne'    northeast
13132  'hd'    horizontal drag
13133  'all'   all
13134 </pre>
13135  * <p>Here's an example showing the creation of a typical Resizable:</p>
13136  * <pre><code>
13137 var resizer = new Roo.Resizable("element-id", {
13138     handles: 'all',
13139     minWidth: 200,
13140     minHeight: 100,
13141     maxWidth: 500,
13142     maxHeight: 400,
13143     pinned: true
13144 });
13145 resizer.on("resize", myHandler);
13146 </code></pre>
13147  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13148  * resizer.east.setDisplayed(false);</p>
13149  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13150  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13151  * resize operation's new size (defaults to [0, 0])
13152  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13153  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13154  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13155  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13156  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13157  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13158  * @cfg {Number} width The width of the element in pixels (defaults to null)
13159  * @cfg {Number} height The height of the element in pixels (defaults to null)
13160  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13161  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13162  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13163  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13164  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13165  * in favor of the handles config option (defaults to false)
13166  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13167  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13168  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13169  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13170  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13171  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13172  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13173  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13174  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13175  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13176  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13177  * @constructor
13178  * Create a new resizable component
13179  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13180  * @param {Object} config configuration options
13181   */
13182 Roo.Resizable = function(el, config)
13183 {
13184     this.el = Roo.get(el);
13185
13186     if(config && config.wrap){
13187         config.resizeChild = this.el;
13188         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13189         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13190         this.el.setStyle("overflow", "hidden");
13191         this.el.setPositioning(config.resizeChild.getPositioning());
13192         config.resizeChild.clearPositioning();
13193         if(!config.width || !config.height){
13194             var csize = config.resizeChild.getSize();
13195             this.el.setSize(csize.width, csize.height);
13196         }
13197         if(config.pinned && !config.adjustments){
13198             config.adjustments = "auto";
13199         }
13200     }
13201
13202     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13203     this.proxy.unselectable();
13204     this.proxy.enableDisplayMode('block');
13205
13206     Roo.apply(this, config);
13207
13208     if(this.pinned){
13209         this.disableTrackOver = true;
13210         this.el.addClass("x-resizable-pinned");
13211     }
13212     // if the element isn't positioned, make it relative
13213     var position = this.el.getStyle("position");
13214     if(position != "absolute" && position != "fixed"){
13215         this.el.setStyle("position", "relative");
13216     }
13217     if(!this.handles){ // no handles passed, must be legacy style
13218         this.handles = 's,e,se';
13219         if(this.multiDirectional){
13220             this.handles += ',n,w';
13221         }
13222     }
13223     if(this.handles == "all"){
13224         this.handles = "n s e w ne nw se sw";
13225     }
13226     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13227     var ps = Roo.Resizable.positions;
13228     for(var i = 0, len = hs.length; i < len; i++){
13229         if(hs[i] && ps[hs[i]]){
13230             var pos = ps[hs[i]];
13231             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13232         }
13233     }
13234     // legacy
13235     this.corner = this.southeast;
13236     
13237     // updateBox = the box can move..
13238     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13239         this.updateBox = true;
13240     }
13241
13242     this.activeHandle = null;
13243
13244     if(this.resizeChild){
13245         if(typeof this.resizeChild == "boolean"){
13246             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13247         }else{
13248             this.resizeChild = Roo.get(this.resizeChild, true);
13249         }
13250     }
13251     
13252     if(this.adjustments == "auto"){
13253         var rc = this.resizeChild;
13254         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13255         if(rc && (hw || hn)){
13256             rc.position("relative");
13257             rc.setLeft(hw ? hw.el.getWidth() : 0);
13258             rc.setTop(hn ? hn.el.getHeight() : 0);
13259         }
13260         this.adjustments = [
13261             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13262             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13263         ];
13264     }
13265
13266     if(this.draggable){
13267         this.dd = this.dynamic ?
13268             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13269         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13270     }
13271
13272     // public events
13273     this.addEvents({
13274         /**
13275          * @event beforeresize
13276          * Fired before resize is allowed. Set enabled to false to cancel resize.
13277          * @param {Roo.Resizable} this
13278          * @param {Roo.EventObject} e The mousedown event
13279          */
13280         "beforeresize" : true,
13281         /**
13282          * @event resize
13283          * Fired after a resize.
13284          * @param {Roo.Resizable} this
13285          * @param {Number} width The new width
13286          * @param {Number} height The new height
13287          * @param {Roo.EventObject} e The mouseup event
13288          */
13289         "resize" : true
13290     });
13291
13292     if(this.width !== null && this.height !== null){
13293         this.resizeTo(this.width, this.height);
13294     }else{
13295         this.updateChildSize();
13296     }
13297     if(Roo.isIE){
13298         this.el.dom.style.zoom = 1;
13299     }
13300     Roo.Resizable.superclass.constructor.call(this);
13301 };
13302
13303 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13304         resizeChild : false,
13305         adjustments : [0, 0],
13306         minWidth : 5,
13307         minHeight : 5,
13308         maxWidth : 10000,
13309         maxHeight : 10000,
13310         enabled : true,
13311         animate : false,
13312         duration : .35,
13313         dynamic : false,
13314         handles : false,
13315         multiDirectional : false,
13316         disableTrackOver : false,
13317         easing : 'easeOutStrong',
13318         widthIncrement : 0,
13319         heightIncrement : 0,
13320         pinned : false,
13321         width : null,
13322         height : null,
13323         preserveRatio : false,
13324         transparent: false,
13325         minX: 0,
13326         minY: 0,
13327         draggable: false,
13328
13329         /**
13330          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13331          */
13332         constrainTo: undefined,
13333         /**
13334          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13335          */
13336         resizeRegion: undefined,
13337
13338
13339     /**
13340      * Perform a manual resize
13341      * @param {Number} width
13342      * @param {Number} height
13343      */
13344     resizeTo : function(width, height){
13345         this.el.setSize(width, height);
13346         this.updateChildSize();
13347         this.fireEvent("resize", this, width, height, null);
13348     },
13349
13350     // private
13351     startSizing : function(e, handle){
13352         this.fireEvent("beforeresize", this, e);
13353         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13354
13355             if(!this.overlay){
13356                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13357                 this.overlay.unselectable();
13358                 this.overlay.enableDisplayMode("block");
13359                 this.overlay.on("mousemove", this.onMouseMove, this);
13360                 this.overlay.on("mouseup", this.onMouseUp, this);
13361             }
13362             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13363
13364             this.resizing = true;
13365             this.startBox = this.el.getBox();
13366             this.startPoint = e.getXY();
13367             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13368                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13369
13370             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13371             this.overlay.show();
13372
13373             if(this.constrainTo) {
13374                 var ct = Roo.get(this.constrainTo);
13375                 this.resizeRegion = ct.getRegion().adjust(
13376                     ct.getFrameWidth('t'),
13377                     ct.getFrameWidth('l'),
13378                     -ct.getFrameWidth('b'),
13379                     -ct.getFrameWidth('r')
13380                 );
13381             }
13382
13383             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13384             this.proxy.show();
13385             this.proxy.setBox(this.startBox);
13386             if(!this.dynamic){
13387                 this.proxy.setStyle('visibility', 'visible');
13388             }
13389         }
13390     },
13391
13392     // private
13393     onMouseDown : function(handle, e){
13394         if(this.enabled){
13395             e.stopEvent();
13396             this.activeHandle = handle;
13397             this.startSizing(e, handle);
13398         }
13399     },
13400
13401     // private
13402     onMouseUp : function(e){
13403         var size = this.resizeElement();
13404         this.resizing = false;
13405         this.handleOut();
13406         this.overlay.hide();
13407         this.proxy.hide();
13408         this.fireEvent("resize", this, size.width, size.height, e);
13409     },
13410
13411     // private
13412     updateChildSize : function(){
13413         if(this.resizeChild){
13414             var el = this.el;
13415             var child = this.resizeChild;
13416             var adj = this.adjustments;
13417             if(el.dom.offsetWidth){
13418                 var b = el.getSize(true);
13419                 child.setSize(b.width+adj[0], b.height+adj[1]);
13420             }
13421             // Second call here for IE
13422             // The first call enables instant resizing and
13423             // the second call corrects scroll bars if they
13424             // exist
13425             if(Roo.isIE){
13426                 setTimeout(function(){
13427                     if(el.dom.offsetWidth){
13428                         var b = el.getSize(true);
13429                         child.setSize(b.width+adj[0], b.height+adj[1]);
13430                     }
13431                 }, 10);
13432             }
13433         }
13434     },
13435
13436     // private
13437     snap : function(value, inc, min){
13438         if(!inc || !value) return value;
13439         var newValue = value;
13440         var m = value % inc;
13441         if(m > 0){
13442             if(m > (inc/2)){
13443                 newValue = value + (inc-m);
13444             }else{
13445                 newValue = value - m;
13446             }
13447         }
13448         return Math.max(min, newValue);
13449     },
13450
13451     // private
13452     resizeElement : function(){
13453         var box = this.proxy.getBox();
13454         if(this.updateBox){
13455             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13456         }else{
13457             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13458         }
13459         this.updateChildSize();
13460         if(!this.dynamic){
13461             this.proxy.hide();
13462         }
13463         return box;
13464     },
13465
13466     // private
13467     constrain : function(v, diff, m, mx){
13468         if(v - diff < m){
13469             diff = v - m;
13470         }else if(v - diff > mx){
13471             diff = mx - v;
13472         }
13473         return diff;
13474     },
13475
13476     // private
13477     onMouseMove : function(e){
13478         if(this.enabled){
13479             try{// try catch so if something goes wrong the user doesn't get hung
13480
13481             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13482                 return;
13483             }
13484
13485             //var curXY = this.startPoint;
13486             var curSize = this.curSize || this.startBox;
13487             var x = this.startBox.x, y = this.startBox.y;
13488             var ox = x, oy = y;
13489             var w = curSize.width, h = curSize.height;
13490             var ow = w, oh = h;
13491             var mw = this.minWidth, mh = this.minHeight;
13492             var mxw = this.maxWidth, mxh = this.maxHeight;
13493             var wi = this.widthIncrement;
13494             var hi = this.heightIncrement;
13495
13496             var eventXY = e.getXY();
13497             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13498             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13499
13500             var pos = this.activeHandle.position;
13501
13502             switch(pos){
13503                 case "east":
13504                     w += diffX;
13505                     w = Math.min(Math.max(mw, w), mxw);
13506                     break;
13507              
13508                 case "south":
13509                     h += diffY;
13510                     h = Math.min(Math.max(mh, h), mxh);
13511                     break;
13512                 case "southeast":
13513                     w += diffX;
13514                     h += diffY;
13515                     w = Math.min(Math.max(mw, w), mxw);
13516                     h = Math.min(Math.max(mh, h), mxh);
13517                     break;
13518                 case "north":
13519                     diffY = this.constrain(h, diffY, mh, mxh);
13520                     y += diffY;
13521                     h -= diffY;
13522                     break;
13523                 case "hdrag":
13524                     
13525                     if (wi) {
13526                         var adiffX = Math.abs(diffX);
13527                         var sub = (adiffX % wi); // how much 
13528                         if (sub > (wi/2)) { // far enough to snap
13529                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13530                         } else {
13531                             // remove difference.. 
13532                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13533                         }
13534                     }
13535                     x += diffX;
13536                     x = Math.max(this.minX, x);
13537                     break;
13538                 case "west":
13539                     diffX = this.constrain(w, diffX, mw, mxw);
13540                     x += diffX;
13541                     w -= diffX;
13542                     break;
13543                 case "northeast":
13544                     w += diffX;
13545                     w = Math.min(Math.max(mw, w), mxw);
13546                     diffY = this.constrain(h, diffY, mh, mxh);
13547                     y += diffY;
13548                     h -= diffY;
13549                     break;
13550                 case "northwest":
13551                     diffX = this.constrain(w, diffX, mw, mxw);
13552                     diffY = this.constrain(h, diffY, mh, mxh);
13553                     y += diffY;
13554                     h -= diffY;
13555                     x += diffX;
13556                     w -= diffX;
13557                     break;
13558                case "southwest":
13559                     diffX = this.constrain(w, diffX, mw, mxw);
13560                     h += diffY;
13561                     h = Math.min(Math.max(mh, h), mxh);
13562                     x += diffX;
13563                     w -= diffX;
13564                     break;
13565             }
13566
13567             var sw = this.snap(w, wi, mw);
13568             var sh = this.snap(h, hi, mh);
13569             if(sw != w || sh != h){
13570                 switch(pos){
13571                     case "northeast":
13572                         y -= sh - h;
13573                     break;
13574                     case "north":
13575                         y -= sh - h;
13576                         break;
13577                     case "southwest":
13578                         x -= sw - w;
13579                     break;
13580                     case "west":
13581                         x -= sw - w;
13582                         break;
13583                     case "northwest":
13584                         x -= sw - w;
13585                         y -= sh - h;
13586                     break;
13587                 }
13588                 w = sw;
13589                 h = sh;
13590             }
13591
13592             if(this.preserveRatio){
13593                 switch(pos){
13594                     case "southeast":
13595                     case "east":
13596                         h = oh * (w/ow);
13597                         h = Math.min(Math.max(mh, h), mxh);
13598                         w = ow * (h/oh);
13599                        break;
13600                     case "south":
13601                         w = ow * (h/oh);
13602                         w = Math.min(Math.max(mw, w), mxw);
13603                         h = oh * (w/ow);
13604                         break;
13605                     case "northeast":
13606                         w = ow * (h/oh);
13607                         w = Math.min(Math.max(mw, w), mxw);
13608                         h = oh * (w/ow);
13609                     break;
13610                     case "north":
13611                         var tw = w;
13612                         w = ow * (h/oh);
13613                         w = Math.min(Math.max(mw, w), mxw);
13614                         h = oh * (w/ow);
13615                         x += (tw - w) / 2;
13616                         break;
13617                     case "southwest":
13618                         h = oh * (w/ow);
13619                         h = Math.min(Math.max(mh, h), mxh);
13620                         var tw = w;
13621                         w = ow * (h/oh);
13622                         x += tw - w;
13623                         break;
13624                     case "west":
13625                         var th = h;
13626                         h = oh * (w/ow);
13627                         h = Math.min(Math.max(mh, h), mxh);
13628                         y += (th - h) / 2;
13629                         var tw = w;
13630                         w = ow * (h/oh);
13631                         x += tw - w;
13632                        break;
13633                     case "northwest":
13634                         var tw = w;
13635                         var th = h;
13636                         h = oh * (w/ow);
13637                         h = Math.min(Math.max(mh, h), mxh);
13638                         w = ow * (h/oh);
13639                         y += th - h;
13640                         x += tw - w;
13641                        break;
13642
13643                 }
13644             }
13645             if (pos == 'hdrag') {
13646                 w = ow;
13647             }
13648             this.proxy.setBounds(x, y, w, h);
13649             if(this.dynamic){
13650                 this.resizeElement();
13651             }
13652             }catch(e){}
13653         }
13654     },
13655
13656     // private
13657     handleOver : function(){
13658         if(this.enabled){
13659             this.el.addClass("x-resizable-over");
13660         }
13661     },
13662
13663     // private
13664     handleOut : function(){
13665         if(!this.resizing){
13666             this.el.removeClass("x-resizable-over");
13667         }
13668     },
13669
13670     /**
13671      * Returns the element this component is bound to.
13672      * @return {Roo.Element}
13673      */
13674     getEl : function(){
13675         return this.el;
13676     },
13677
13678     /**
13679      * Returns the resizeChild element (or null).
13680      * @return {Roo.Element}
13681      */
13682     getResizeChild : function(){
13683         return this.resizeChild;
13684     },
13685
13686     /**
13687      * Destroys this resizable. If the element was wrapped and
13688      * removeEl is not true then the element remains.
13689      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13690      */
13691     destroy : function(removeEl){
13692         this.proxy.remove();
13693         if(this.overlay){
13694             this.overlay.removeAllListeners();
13695             this.overlay.remove();
13696         }
13697         var ps = Roo.Resizable.positions;
13698         for(var k in ps){
13699             if(typeof ps[k] != "function" && this[ps[k]]){
13700                 var h = this[ps[k]];
13701                 h.el.removeAllListeners();
13702                 h.el.remove();
13703             }
13704         }
13705         if(removeEl){
13706             this.el.update("");
13707             this.el.remove();
13708         }
13709     }
13710 });
13711
13712 // private
13713 // hash to map config positions to true positions
13714 Roo.Resizable.positions = {
13715     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13716     hd: "hdrag"
13717 };
13718
13719 // private
13720 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13721     if(!this.tpl){
13722         // only initialize the template if resizable is used
13723         var tpl = Roo.DomHelper.createTemplate(
13724             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13725         );
13726         tpl.compile();
13727         Roo.Resizable.Handle.prototype.tpl = tpl;
13728     }
13729     this.position = pos;
13730     this.rz = rz;
13731     // show north drag fro topdra
13732     var handlepos = pos == 'hdrag' ? 'north' : pos;
13733     
13734     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13735     if (pos == 'hdrag') {
13736         this.el.setStyle('cursor', 'pointer');
13737     }
13738     this.el.unselectable();
13739     if(transparent){
13740         this.el.setOpacity(0);
13741     }
13742     this.el.on("mousedown", this.onMouseDown, this);
13743     if(!disableTrackOver){
13744         this.el.on("mouseover", this.onMouseOver, this);
13745         this.el.on("mouseout", this.onMouseOut, this);
13746     }
13747 };
13748
13749 // private
13750 Roo.Resizable.Handle.prototype = {
13751     afterResize : function(rz){
13752         // do nothing
13753     },
13754     // private
13755     onMouseDown : function(e){
13756         this.rz.onMouseDown(this, e);
13757     },
13758     // private
13759     onMouseOver : function(e){
13760         this.rz.handleOver(this, e);
13761     },
13762     // private
13763     onMouseOut : function(e){
13764         this.rz.handleOut(this, e);
13765     }
13766 };/*
13767  * Based on:
13768  * Ext JS Library 1.1.1
13769  * Copyright(c) 2006-2007, Ext JS, LLC.
13770  *
13771  * Originally Released Under LGPL - original licence link has changed is not relivant.
13772  *
13773  * Fork - LGPL
13774  * <script type="text/javascript">
13775  */
13776
13777 /**
13778  * @class Roo.Editor
13779  * @extends Roo.Component
13780  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13781  * @constructor
13782  * Create a new Editor
13783  * @param {Roo.form.Field} field The Field object (or descendant)
13784  * @param {Object} config The config object
13785  */
13786 Roo.Editor = function(field, config){
13787     Roo.Editor.superclass.constructor.call(this, config);
13788     this.field = field;
13789     this.addEvents({
13790         /**
13791              * @event beforestartedit
13792              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13793              * false from the handler of this event.
13794              * @param {Editor} this
13795              * @param {Roo.Element} boundEl The underlying element bound to this editor
13796              * @param {Mixed} value The field value being set
13797              */
13798         "beforestartedit" : true,
13799         /**
13800              * @event startedit
13801              * Fires when this editor is displayed
13802              * @param {Roo.Element} boundEl The underlying element bound to this editor
13803              * @param {Mixed} value The starting field value
13804              */
13805         "startedit" : true,
13806         /**
13807              * @event beforecomplete
13808              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13809              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13810              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13811              * event will not fire since no edit actually occurred.
13812              * @param {Editor} this
13813              * @param {Mixed} value The current field value
13814              * @param {Mixed} startValue The original field value
13815              */
13816         "beforecomplete" : true,
13817         /**
13818              * @event complete
13819              * Fires after editing is complete and any changed value has been written to the underlying field.
13820              * @param {Editor} this
13821              * @param {Mixed} value The current field value
13822              * @param {Mixed} startValue The original field value
13823              */
13824         "complete" : true,
13825         /**
13826          * @event specialkey
13827          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13828          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13829          * @param {Roo.form.Field} this
13830          * @param {Roo.EventObject} e The event object
13831          */
13832         "specialkey" : true
13833     });
13834 };
13835
13836 Roo.extend(Roo.Editor, Roo.Component, {
13837     /**
13838      * @cfg {Boolean/String} autosize
13839      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
13840      * or "height" to adopt the height only (defaults to false)
13841      */
13842     /**
13843      * @cfg {Boolean} revertInvalid
13844      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
13845      * validation fails (defaults to true)
13846      */
13847     /**
13848      * @cfg {Boolean} ignoreNoChange
13849      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
13850      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
13851      * will never be ignored.
13852      */
13853     /**
13854      * @cfg {Boolean} hideEl
13855      * False to keep the bound element visible while the editor is displayed (defaults to true)
13856      */
13857     /**
13858      * @cfg {Mixed} value
13859      * The data value of the underlying field (defaults to "")
13860      */
13861     value : "",
13862     /**
13863      * @cfg {String} alignment
13864      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
13865      */
13866     alignment: "c-c?",
13867     /**
13868      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
13869      * for bottom-right shadow (defaults to "frame")
13870      */
13871     shadow : "frame",
13872     /**
13873      * @cfg {Boolean} constrain True to constrain the editor to the viewport
13874      */
13875     constrain : false,
13876     /**
13877      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
13878      */
13879     completeOnEnter : false,
13880     /**
13881      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
13882      */
13883     cancelOnEsc : false,
13884     /**
13885      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
13886      */
13887     updateEl : false,
13888
13889     // private
13890     onRender : function(ct, position){
13891         this.el = new Roo.Layer({
13892             shadow: this.shadow,
13893             cls: "x-editor",
13894             parentEl : ct,
13895             shim : this.shim,
13896             shadowOffset:4,
13897             id: this.id,
13898             constrain: this.constrain
13899         });
13900         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
13901         if(this.field.msgTarget != 'title'){
13902             this.field.msgTarget = 'qtip';
13903         }
13904         this.field.render(this.el);
13905         if(Roo.isGecko){
13906             this.field.el.dom.setAttribute('autocomplete', 'off');
13907         }
13908         this.field.on("specialkey", this.onSpecialKey, this);
13909         if(this.swallowKeys){
13910             this.field.el.swallowEvent(['keydown','keypress']);
13911         }
13912         this.field.show();
13913         this.field.on("blur", this.onBlur, this);
13914         if(this.field.grow){
13915             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
13916         }
13917     },
13918
13919     onSpecialKey : function(field, e){
13920         //Roo.log('editor onSpecialKey');
13921         if(this.completeOnEnter && e.getKey() == e.ENTER){
13922             e.stopEvent();
13923             this.completeEdit();
13924         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
13925             this.cancelEdit();
13926         }else{
13927             this.fireEvent('specialkey', field, e);
13928         }
13929     },
13930
13931     /**
13932      * Starts the editing process and shows the editor.
13933      * @param {String/HTMLElement/Element} el The element to edit
13934      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
13935       * to the innerHTML of el.
13936      */
13937     startEdit : function(el, value){
13938         if(this.editing){
13939             this.completeEdit();
13940         }
13941         this.boundEl = Roo.get(el);
13942         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
13943         if(!this.rendered){
13944             this.render(this.parentEl || document.body);
13945         }
13946         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
13947             return;
13948         }
13949         this.startValue = v;
13950         this.field.setValue(v);
13951         if(this.autoSize){
13952             var sz = this.boundEl.getSize();
13953             switch(this.autoSize){
13954                 case "width":
13955                 this.setSize(sz.width,  "");
13956                 break;
13957                 case "height":
13958                 this.setSize("",  sz.height);
13959                 break;
13960                 default:
13961                 this.setSize(sz.width,  sz.height);
13962             }
13963         }
13964         this.el.alignTo(this.boundEl, this.alignment);
13965         this.editing = true;
13966         if(Roo.QuickTips){
13967             Roo.QuickTips.disable();
13968         }
13969         this.show();
13970     },
13971
13972     /**
13973      * Sets the height and width of this editor.
13974      * @param {Number} width The new width
13975      * @param {Number} height The new height
13976      */
13977     setSize : function(w, h){
13978         this.field.setSize(w, h);
13979         if(this.el){
13980             this.el.sync();
13981         }
13982     },
13983
13984     /**
13985      * Realigns the editor to the bound field based on the current alignment config value.
13986      */
13987     realign : function(){
13988         this.el.alignTo(this.boundEl, this.alignment);
13989     },
13990
13991     /**
13992      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
13993      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
13994      */
13995     completeEdit : function(remainVisible){
13996         if(!this.editing){
13997             return;
13998         }
13999         var v = this.getValue();
14000         if(this.revertInvalid !== false && !this.field.isValid()){
14001             v = this.startValue;
14002             this.cancelEdit(true);
14003         }
14004         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14005             this.editing = false;
14006             this.hide();
14007             return;
14008         }
14009         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14010             this.editing = false;
14011             if(this.updateEl && this.boundEl){
14012                 this.boundEl.update(v);
14013             }
14014             if(remainVisible !== true){
14015                 this.hide();
14016             }
14017             this.fireEvent("complete", this, v, this.startValue);
14018         }
14019     },
14020
14021     // private
14022     onShow : function(){
14023         this.el.show();
14024         if(this.hideEl !== false){
14025             this.boundEl.hide();
14026         }
14027         this.field.show();
14028         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14029             this.fixIEFocus = true;
14030             this.deferredFocus.defer(50, this);
14031         }else{
14032             this.field.focus();
14033         }
14034         this.fireEvent("startedit", this.boundEl, this.startValue);
14035     },
14036
14037     deferredFocus : function(){
14038         if(this.editing){
14039             this.field.focus();
14040         }
14041     },
14042
14043     /**
14044      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14045      * reverted to the original starting value.
14046      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14047      * cancel (defaults to false)
14048      */
14049     cancelEdit : function(remainVisible){
14050         if(this.editing){
14051             this.setValue(this.startValue);
14052             if(remainVisible !== true){
14053                 this.hide();
14054             }
14055         }
14056     },
14057
14058     // private
14059     onBlur : function(){
14060         if(this.allowBlur !== true && this.editing){
14061             this.completeEdit();
14062         }
14063     },
14064
14065     // private
14066     onHide : function(){
14067         if(this.editing){
14068             this.completeEdit();
14069             return;
14070         }
14071         this.field.blur();
14072         if(this.field.collapse){
14073             this.field.collapse();
14074         }
14075         this.el.hide();
14076         if(this.hideEl !== false){
14077             this.boundEl.show();
14078         }
14079         if(Roo.QuickTips){
14080             Roo.QuickTips.enable();
14081         }
14082     },
14083
14084     /**
14085      * Sets the data value of the editor
14086      * @param {Mixed} value Any valid value supported by the underlying field
14087      */
14088     setValue : function(v){
14089         this.field.setValue(v);
14090     },
14091
14092     /**
14093      * Gets the data value of the editor
14094      * @return {Mixed} The data value
14095      */
14096     getValue : function(){
14097         return this.field.getValue();
14098     }
14099 });/*
14100  * Based on:
14101  * Ext JS Library 1.1.1
14102  * Copyright(c) 2006-2007, Ext JS, LLC.
14103  *
14104  * Originally Released Under LGPL - original licence link has changed is not relivant.
14105  *
14106  * Fork - LGPL
14107  * <script type="text/javascript">
14108  */
14109  
14110 /**
14111  * @class Roo.BasicDialog
14112  * @extends Roo.util.Observable
14113  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14114  * <pre><code>
14115 var dlg = new Roo.BasicDialog("my-dlg", {
14116     height: 200,
14117     width: 300,
14118     minHeight: 100,
14119     minWidth: 150,
14120     modal: true,
14121     proxyDrag: true,
14122     shadow: true
14123 });
14124 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14125 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14126 dlg.addButton('Cancel', dlg.hide, dlg);
14127 dlg.show();
14128 </code></pre>
14129   <b>A Dialog should always be a direct child of the body element.</b>
14130  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14131  * @cfg {String} title Default text to display in the title bar (defaults to null)
14132  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14133  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14134  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14135  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14136  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14137  * (defaults to null with no animation)
14138  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14139  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14140  * property for valid values (defaults to 'all')
14141  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14142  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14143  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14144  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14145  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14146  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14147  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14148  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14149  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14150  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14151  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14152  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14153  * draggable = true (defaults to false)
14154  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14155  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14156  * shadow (defaults to false)
14157  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14158  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14159  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14160  * @cfg {Array} buttons Array of buttons
14161  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14162  * @constructor
14163  * Create a new BasicDialog.
14164  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14165  * @param {Object} config Configuration options
14166  */
14167 Roo.BasicDialog = function(el, config){
14168     this.el = Roo.get(el);
14169     var dh = Roo.DomHelper;
14170     if(!this.el && config && config.autoCreate){
14171         if(typeof config.autoCreate == "object"){
14172             if(!config.autoCreate.id){
14173                 config.autoCreate.id = el;
14174             }
14175             this.el = dh.append(document.body,
14176                         config.autoCreate, true);
14177         }else{
14178             this.el = dh.append(document.body,
14179                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14180         }
14181     }
14182     el = this.el;
14183     el.setDisplayed(true);
14184     el.hide = this.hideAction;
14185     this.id = el.id;
14186     el.addClass("x-dlg");
14187
14188     Roo.apply(this, config);
14189
14190     this.proxy = el.createProxy("x-dlg-proxy");
14191     this.proxy.hide = this.hideAction;
14192     this.proxy.setOpacity(.5);
14193     this.proxy.hide();
14194
14195     if(config.width){
14196         el.setWidth(config.width);
14197     }
14198     if(config.height){
14199         el.setHeight(config.height);
14200     }
14201     this.size = el.getSize();
14202     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14203         this.xy = [config.x,config.y];
14204     }else{
14205         this.xy = el.getCenterXY(true);
14206     }
14207     /** The header element @type Roo.Element */
14208     this.header = el.child("> .x-dlg-hd");
14209     /** The body element @type Roo.Element */
14210     this.body = el.child("> .x-dlg-bd");
14211     /** The footer element @type Roo.Element */
14212     this.footer = el.child("> .x-dlg-ft");
14213
14214     if(!this.header){
14215         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14216     }
14217     if(!this.body){
14218         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14219     }
14220
14221     this.header.unselectable();
14222     if(this.title){
14223         this.header.update(this.title);
14224     }
14225     // this element allows the dialog to be focused for keyboard event
14226     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14227     this.focusEl.swallowEvent("click", true);
14228
14229     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14230
14231     // wrap the body and footer for special rendering
14232     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14233     if(this.footer){
14234         this.bwrap.dom.appendChild(this.footer.dom);
14235     }
14236
14237     this.bg = this.el.createChild({
14238         tag: "div", cls:"x-dlg-bg",
14239         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14240     });
14241     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14242
14243
14244     if(this.autoScroll !== false && !this.autoTabs){
14245         this.body.setStyle("overflow", "auto");
14246     }
14247
14248     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14249
14250     if(this.closable !== false){
14251         this.el.addClass("x-dlg-closable");
14252         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14253         this.close.on("click", this.closeClick, this);
14254         this.close.addClassOnOver("x-dlg-close-over");
14255     }
14256     if(this.collapsible !== false){
14257         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14258         this.collapseBtn.on("click", this.collapseClick, this);
14259         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14260         this.header.on("dblclick", this.collapseClick, this);
14261     }
14262     if(this.resizable !== false){
14263         this.el.addClass("x-dlg-resizable");
14264         this.resizer = new Roo.Resizable(el, {
14265             minWidth: this.minWidth || 80,
14266             minHeight:this.minHeight || 80,
14267             handles: this.resizeHandles || "all",
14268             pinned: true
14269         });
14270         this.resizer.on("beforeresize", this.beforeResize, this);
14271         this.resizer.on("resize", this.onResize, this);
14272     }
14273     if(this.draggable !== false){
14274         el.addClass("x-dlg-draggable");
14275         if (!this.proxyDrag) {
14276             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14277         }
14278         else {
14279             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14280         }
14281         dd.setHandleElId(this.header.id);
14282         dd.endDrag = this.endMove.createDelegate(this);
14283         dd.startDrag = this.startMove.createDelegate(this);
14284         dd.onDrag = this.onDrag.createDelegate(this);
14285         dd.scroll = false;
14286         this.dd = dd;
14287     }
14288     if(this.modal){
14289         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14290         this.mask.enableDisplayMode("block");
14291         this.mask.hide();
14292         this.el.addClass("x-dlg-modal");
14293     }
14294     if(this.shadow){
14295         this.shadow = new Roo.Shadow({
14296             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14297             offset : this.shadowOffset
14298         });
14299     }else{
14300         this.shadowOffset = 0;
14301     }
14302     if(Roo.useShims && this.shim !== false){
14303         this.shim = this.el.createShim();
14304         this.shim.hide = this.hideAction;
14305         this.shim.hide();
14306     }else{
14307         this.shim = false;
14308     }
14309     if(this.autoTabs){
14310         this.initTabs();
14311     }
14312     if (this.buttons) { 
14313         var bts= this.buttons;
14314         this.buttons = [];
14315         Roo.each(bts, function(b) {
14316             this.addButton(b);
14317         }, this);
14318     }
14319     
14320     
14321     this.addEvents({
14322         /**
14323          * @event keydown
14324          * Fires when a key is pressed
14325          * @param {Roo.BasicDialog} this
14326          * @param {Roo.EventObject} e
14327          */
14328         "keydown" : true,
14329         /**
14330          * @event move
14331          * Fires when this dialog is moved by the user.
14332          * @param {Roo.BasicDialog} this
14333          * @param {Number} x The new page X
14334          * @param {Number} y The new page Y
14335          */
14336         "move" : true,
14337         /**
14338          * @event resize
14339          * Fires when this dialog is resized by the user.
14340          * @param {Roo.BasicDialog} this
14341          * @param {Number} width The new width
14342          * @param {Number} height The new height
14343          */
14344         "resize" : true,
14345         /**
14346          * @event beforehide
14347          * Fires before this dialog is hidden.
14348          * @param {Roo.BasicDialog} this
14349          */
14350         "beforehide" : true,
14351         /**
14352          * @event hide
14353          * Fires when this dialog is hidden.
14354          * @param {Roo.BasicDialog} this
14355          */
14356         "hide" : true,
14357         /**
14358          * @event beforeshow
14359          * Fires before this dialog is shown.
14360          * @param {Roo.BasicDialog} this
14361          */
14362         "beforeshow" : true,
14363         /**
14364          * @event show
14365          * Fires when this dialog is shown.
14366          * @param {Roo.BasicDialog} this
14367          */
14368         "show" : true
14369     });
14370     el.on("keydown", this.onKeyDown, this);
14371     el.on("mousedown", this.toFront, this);
14372     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14373     this.el.hide();
14374     Roo.DialogManager.register(this);
14375     Roo.BasicDialog.superclass.constructor.call(this);
14376 };
14377
14378 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14379     shadowOffset: Roo.isIE ? 6 : 5,
14380     minHeight: 80,
14381     minWidth: 200,
14382     minButtonWidth: 75,
14383     defaultButton: null,
14384     buttonAlign: "right",
14385     tabTag: 'div',
14386     firstShow: true,
14387
14388     /**
14389      * Sets the dialog title text
14390      * @param {String} text The title text to display
14391      * @return {Roo.BasicDialog} this
14392      */
14393     setTitle : function(text){
14394         this.header.update(text);
14395         return this;
14396     },
14397
14398     // private
14399     closeClick : function(){
14400         this.hide();
14401     },
14402
14403     // private
14404     collapseClick : function(){
14405         this[this.collapsed ? "expand" : "collapse"]();
14406     },
14407
14408     /**
14409      * Collapses the dialog to its minimized state (only the title bar is visible).
14410      * Equivalent to the user clicking the collapse dialog button.
14411      */
14412     collapse : function(){
14413         if(!this.collapsed){
14414             this.collapsed = true;
14415             this.el.addClass("x-dlg-collapsed");
14416             this.restoreHeight = this.el.getHeight();
14417             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14418         }
14419     },
14420
14421     /**
14422      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14423      * clicking the expand dialog button.
14424      */
14425     expand : function(){
14426         if(this.collapsed){
14427             this.collapsed = false;
14428             this.el.removeClass("x-dlg-collapsed");
14429             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14430         }
14431     },
14432
14433     /**
14434      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14435      * @return {Roo.TabPanel} The tabs component
14436      */
14437     initTabs : function(){
14438         var tabs = this.getTabs();
14439         while(tabs.getTab(0)){
14440             tabs.removeTab(0);
14441         }
14442         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14443             var dom = el.dom;
14444             tabs.addTab(Roo.id(dom), dom.title);
14445             dom.title = "";
14446         });
14447         tabs.activate(0);
14448         return tabs;
14449     },
14450
14451     // private
14452     beforeResize : function(){
14453         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14454     },
14455
14456     // private
14457     onResize : function(){
14458         this.refreshSize();
14459         this.syncBodyHeight();
14460         this.adjustAssets();
14461         this.focus();
14462         this.fireEvent("resize", this, this.size.width, this.size.height);
14463     },
14464
14465     // private
14466     onKeyDown : function(e){
14467         if(this.isVisible()){
14468             this.fireEvent("keydown", this, e);
14469         }
14470     },
14471
14472     /**
14473      * Resizes the dialog.
14474      * @param {Number} width
14475      * @param {Number} height
14476      * @return {Roo.BasicDialog} this
14477      */
14478     resizeTo : function(width, height){
14479         this.el.setSize(width, height);
14480         this.size = {width: width, height: height};
14481         this.syncBodyHeight();
14482         if(this.fixedcenter){
14483             this.center();
14484         }
14485         if(this.isVisible()){
14486             this.constrainXY();
14487             this.adjustAssets();
14488         }
14489         this.fireEvent("resize", this, width, height);
14490         return this;
14491     },
14492
14493
14494     /**
14495      * Resizes the dialog to fit the specified content size.
14496      * @param {Number} width
14497      * @param {Number} height
14498      * @return {Roo.BasicDialog} this
14499      */
14500     setContentSize : function(w, h){
14501         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14502         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14503         //if(!this.el.isBorderBox()){
14504             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14505             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14506         //}
14507         if(this.tabs){
14508             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14509             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14510         }
14511         this.resizeTo(w, h);
14512         return this;
14513     },
14514
14515     /**
14516      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14517      * executed in response to a particular key being pressed while the dialog is active.
14518      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14519      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14520      * @param {Function} fn The function to call
14521      * @param {Object} scope (optional) The scope of the function
14522      * @return {Roo.BasicDialog} this
14523      */
14524     addKeyListener : function(key, fn, scope){
14525         var keyCode, shift, ctrl, alt;
14526         if(typeof key == "object" && !(key instanceof Array)){
14527             keyCode = key["key"];
14528             shift = key["shift"];
14529             ctrl = key["ctrl"];
14530             alt = key["alt"];
14531         }else{
14532             keyCode = key;
14533         }
14534         var handler = function(dlg, e){
14535             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14536                 var k = e.getKey();
14537                 if(keyCode instanceof Array){
14538                     for(var i = 0, len = keyCode.length; i < len; i++){
14539                         if(keyCode[i] == k){
14540                           fn.call(scope || window, dlg, k, e);
14541                           return;
14542                         }
14543                     }
14544                 }else{
14545                     if(k == keyCode){
14546                         fn.call(scope || window, dlg, k, e);
14547                     }
14548                 }
14549             }
14550         };
14551         this.on("keydown", handler);
14552         return this;
14553     },
14554
14555     /**
14556      * Returns the TabPanel component (creates it if it doesn't exist).
14557      * Note: If you wish to simply check for the existence of tabs without creating them,
14558      * check for a null 'tabs' property.
14559      * @return {Roo.TabPanel} The tabs component
14560      */
14561     getTabs : function(){
14562         if(!this.tabs){
14563             this.el.addClass("x-dlg-auto-tabs");
14564             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14565             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14566         }
14567         return this.tabs;
14568     },
14569
14570     /**
14571      * Adds a button to the footer section of the dialog.
14572      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14573      * object or a valid Roo.DomHelper element config
14574      * @param {Function} handler The function called when the button is clicked
14575      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14576      * @return {Roo.Button} The new button
14577      */
14578     addButton : function(config, handler, scope){
14579         var dh = Roo.DomHelper;
14580         if(!this.footer){
14581             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14582         }
14583         if(!this.btnContainer){
14584             var tb = this.footer.createChild({
14585
14586                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14587                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14588             }, null, true);
14589             this.btnContainer = tb.firstChild.firstChild.firstChild;
14590         }
14591         var bconfig = {
14592             handler: handler,
14593             scope: scope,
14594             minWidth: this.minButtonWidth,
14595             hideParent:true
14596         };
14597         if(typeof config == "string"){
14598             bconfig.text = config;
14599         }else{
14600             if(config.tag){
14601                 bconfig.dhconfig = config;
14602             }else{
14603                 Roo.apply(bconfig, config);
14604             }
14605         }
14606         var fc = false;
14607         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14608             bconfig.position = Math.max(0, bconfig.position);
14609             fc = this.btnContainer.childNodes[bconfig.position];
14610         }
14611          
14612         var btn = new Roo.Button(
14613             fc ? 
14614                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14615                 : this.btnContainer.appendChild(document.createElement("td")),
14616             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14617             bconfig
14618         );
14619         this.syncBodyHeight();
14620         if(!this.buttons){
14621             /**
14622              * Array of all the buttons that have been added to this dialog via addButton
14623              * @type Array
14624              */
14625             this.buttons = [];
14626         }
14627         this.buttons.push(btn);
14628         return btn;
14629     },
14630
14631     /**
14632      * Sets the default button to be focused when the dialog is displayed.
14633      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14634      * @return {Roo.BasicDialog} this
14635      */
14636     setDefaultButton : function(btn){
14637         this.defaultButton = btn;
14638         return this;
14639     },
14640
14641     // private
14642     getHeaderFooterHeight : function(safe){
14643         var height = 0;
14644         if(this.header){
14645            height += this.header.getHeight();
14646         }
14647         if(this.footer){
14648            var fm = this.footer.getMargins();
14649             height += (this.footer.getHeight()+fm.top+fm.bottom);
14650         }
14651         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14652         height += this.centerBg.getPadding("tb");
14653         return height;
14654     },
14655
14656     // private
14657     syncBodyHeight : function(){
14658         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14659         var height = this.size.height - this.getHeaderFooterHeight(false);
14660         bd.setHeight(height-bd.getMargins("tb"));
14661         var hh = this.header.getHeight();
14662         var h = this.size.height-hh;
14663         cb.setHeight(h);
14664         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14665         bw.setHeight(h-cb.getPadding("tb"));
14666         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14667         bd.setWidth(bw.getWidth(true));
14668         if(this.tabs){
14669             this.tabs.syncHeight();
14670             if(Roo.isIE){
14671                 this.tabs.el.repaint();
14672             }
14673         }
14674     },
14675
14676     /**
14677      * Restores the previous state of the dialog if Roo.state is configured.
14678      * @return {Roo.BasicDialog} this
14679      */
14680     restoreState : function(){
14681         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14682         if(box && box.width){
14683             this.xy = [box.x, box.y];
14684             this.resizeTo(box.width, box.height);
14685         }
14686         return this;
14687     },
14688
14689     // private
14690     beforeShow : function(){
14691         this.expand();
14692         if(this.fixedcenter){
14693             this.xy = this.el.getCenterXY(true);
14694         }
14695         if(this.modal){
14696             Roo.get(document.body).addClass("x-body-masked");
14697             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14698             this.mask.show();
14699         }
14700         this.constrainXY();
14701     },
14702
14703     // private
14704     animShow : function(){
14705         var b = Roo.get(this.animateTarget).getBox();
14706         this.proxy.setSize(b.width, b.height);
14707         this.proxy.setLocation(b.x, b.y);
14708         this.proxy.show();
14709         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14710                     true, .35, this.showEl.createDelegate(this));
14711     },
14712
14713     /**
14714      * Shows the dialog.
14715      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14716      * @return {Roo.BasicDialog} this
14717      */
14718     show : function(animateTarget){
14719         if (this.fireEvent("beforeshow", this) === false){
14720             return;
14721         }
14722         if(this.syncHeightBeforeShow){
14723             this.syncBodyHeight();
14724         }else if(this.firstShow){
14725             this.firstShow = false;
14726             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14727         }
14728         this.animateTarget = animateTarget || this.animateTarget;
14729         if(!this.el.isVisible()){
14730             this.beforeShow();
14731             if(this.animateTarget && Roo.get(this.animateTarget)){
14732                 this.animShow();
14733             }else{
14734                 this.showEl();
14735             }
14736         }
14737         return this;
14738     },
14739
14740     // private
14741     showEl : function(){
14742         this.proxy.hide();
14743         this.el.setXY(this.xy);
14744         this.el.show();
14745         this.adjustAssets(true);
14746         this.toFront();
14747         this.focus();
14748         // IE peekaboo bug - fix found by Dave Fenwick
14749         if(Roo.isIE){
14750             this.el.repaint();
14751         }
14752         this.fireEvent("show", this);
14753     },
14754
14755     /**
14756      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14757      * dialog itself will receive focus.
14758      */
14759     focus : function(){
14760         if(this.defaultButton){
14761             this.defaultButton.focus();
14762         }else{
14763             this.focusEl.focus();
14764         }
14765     },
14766
14767     // private
14768     constrainXY : function(){
14769         if(this.constraintoviewport !== false){
14770             if(!this.viewSize){
14771                 if(this.container){
14772                     var s = this.container.getSize();
14773                     this.viewSize = [s.width, s.height];
14774                 }else{
14775                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14776                 }
14777             }
14778             var s = Roo.get(this.container||document).getScroll();
14779
14780             var x = this.xy[0], y = this.xy[1];
14781             var w = this.size.width, h = this.size.height;
14782             var vw = this.viewSize[0], vh = this.viewSize[1];
14783             // only move it if it needs it
14784             var moved = false;
14785             // first validate right/bottom
14786             if(x + w > vw+s.left){
14787                 x = vw - w;
14788                 moved = true;
14789             }
14790             if(y + h > vh+s.top){
14791                 y = vh - h;
14792                 moved = true;
14793             }
14794             // then make sure top/left isn't negative
14795             if(x < s.left){
14796                 x = s.left;
14797                 moved = true;
14798             }
14799             if(y < s.top){
14800                 y = s.top;
14801                 moved = true;
14802             }
14803             if(moved){
14804                 // cache xy
14805                 this.xy = [x, y];
14806                 if(this.isVisible()){
14807                     this.el.setLocation(x, y);
14808                     this.adjustAssets();
14809                 }
14810             }
14811         }
14812     },
14813
14814     // private
14815     onDrag : function(){
14816         if(!this.proxyDrag){
14817             this.xy = this.el.getXY();
14818             this.adjustAssets();
14819         }
14820     },
14821
14822     // private
14823     adjustAssets : function(doShow){
14824         var x = this.xy[0], y = this.xy[1];
14825         var w = this.size.width, h = this.size.height;
14826         if(doShow === true){
14827             if(this.shadow){
14828                 this.shadow.show(this.el);
14829             }
14830             if(this.shim){
14831                 this.shim.show();
14832             }
14833         }
14834         if(this.shadow && this.shadow.isVisible()){
14835             this.shadow.show(this.el);
14836         }
14837         if(this.shim && this.shim.isVisible()){
14838             this.shim.setBounds(x, y, w, h);
14839         }
14840     },
14841
14842     // private
14843     adjustViewport : function(w, h){
14844         if(!w || !h){
14845             w = Roo.lib.Dom.getViewWidth();
14846             h = Roo.lib.Dom.getViewHeight();
14847         }
14848         // cache the size
14849         this.viewSize = [w, h];
14850         if(this.modal && this.mask.isVisible()){
14851             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
14852             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14853         }
14854         if(this.isVisible()){
14855             this.constrainXY();
14856         }
14857     },
14858
14859     /**
14860      * Destroys this dialog and all its supporting elements (including any tabs, shim,
14861      * shadow, proxy, mask, etc.)  Also removes all event listeners.
14862      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
14863      */
14864     destroy : function(removeEl){
14865         if(this.isVisible()){
14866             this.animateTarget = null;
14867             this.hide();
14868         }
14869         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
14870         if(this.tabs){
14871             this.tabs.destroy(removeEl);
14872         }
14873         Roo.destroy(
14874              this.shim,
14875              this.proxy,
14876              this.resizer,
14877              this.close,
14878              this.mask
14879         );
14880         if(this.dd){
14881             this.dd.unreg();
14882         }
14883         if(this.buttons){
14884            for(var i = 0, len = this.buttons.length; i < len; i++){
14885                this.buttons[i].destroy();
14886            }
14887         }
14888         this.el.removeAllListeners();
14889         if(removeEl === true){
14890             this.el.update("");
14891             this.el.remove();
14892         }
14893         Roo.DialogManager.unregister(this);
14894     },
14895
14896     // private
14897     startMove : function(){
14898         if(this.proxyDrag){
14899             this.proxy.show();
14900         }
14901         if(this.constraintoviewport !== false){
14902             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
14903         }
14904     },
14905
14906     // private
14907     endMove : function(){
14908         if(!this.proxyDrag){
14909             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
14910         }else{
14911             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
14912             this.proxy.hide();
14913         }
14914         this.refreshSize();
14915         this.adjustAssets();
14916         this.focus();
14917         this.fireEvent("move", this, this.xy[0], this.xy[1]);
14918     },
14919
14920     /**
14921      * Brings this dialog to the front of any other visible dialogs
14922      * @return {Roo.BasicDialog} this
14923      */
14924     toFront : function(){
14925         Roo.DialogManager.bringToFront(this);
14926         return this;
14927     },
14928
14929     /**
14930      * Sends this dialog to the back (under) of any other visible dialogs
14931      * @return {Roo.BasicDialog} this
14932      */
14933     toBack : function(){
14934         Roo.DialogManager.sendToBack(this);
14935         return this;
14936     },
14937
14938     /**
14939      * Centers this dialog in the viewport
14940      * @return {Roo.BasicDialog} this
14941      */
14942     center : function(){
14943         var xy = this.el.getCenterXY(true);
14944         this.moveTo(xy[0], xy[1]);
14945         return this;
14946     },
14947
14948     /**
14949      * Moves the dialog's top-left corner to the specified point
14950      * @param {Number} x
14951      * @param {Number} y
14952      * @return {Roo.BasicDialog} this
14953      */
14954     moveTo : function(x, y){
14955         this.xy = [x,y];
14956         if(this.isVisible()){
14957             this.el.setXY(this.xy);
14958             this.adjustAssets();
14959         }
14960         return this;
14961     },
14962
14963     /**
14964      * Aligns the dialog to the specified element
14965      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14966      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
14967      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14968      * @return {Roo.BasicDialog} this
14969      */
14970     alignTo : function(element, position, offsets){
14971         this.xy = this.el.getAlignToXY(element, position, offsets);
14972         if(this.isVisible()){
14973             this.el.setXY(this.xy);
14974             this.adjustAssets();
14975         }
14976         return this;
14977     },
14978
14979     /**
14980      * Anchors an element to another element and realigns it when the window is resized.
14981      * @param {String/HTMLElement/Roo.Element} element The element to align to.
14982      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
14983      * @param {Array} offsets (optional) Offset the positioning by [x, y]
14984      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
14985      * is a number, it is used as the buffer delay (defaults to 50ms).
14986      * @return {Roo.BasicDialog} this
14987      */
14988     anchorTo : function(el, alignment, offsets, monitorScroll){
14989         var action = function(){
14990             this.alignTo(el, alignment, offsets);
14991         };
14992         Roo.EventManager.onWindowResize(action, this);
14993         var tm = typeof monitorScroll;
14994         if(tm != 'undefined'){
14995             Roo.EventManager.on(window, 'scroll', action, this,
14996                 {buffer: tm == 'number' ? monitorScroll : 50});
14997         }
14998         action.call(this);
14999         return this;
15000     },
15001
15002     /**
15003      * Returns true if the dialog is visible
15004      * @return {Boolean}
15005      */
15006     isVisible : function(){
15007         return this.el.isVisible();
15008     },
15009
15010     // private
15011     animHide : function(callback){
15012         var b = Roo.get(this.animateTarget).getBox();
15013         this.proxy.show();
15014         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15015         this.el.hide();
15016         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15017                     this.hideEl.createDelegate(this, [callback]));
15018     },
15019
15020     /**
15021      * Hides the dialog.
15022      * @param {Function} callback (optional) Function to call when the dialog is hidden
15023      * @return {Roo.BasicDialog} this
15024      */
15025     hide : function(callback){
15026         if (this.fireEvent("beforehide", this) === false){
15027             return;
15028         }
15029         if(this.shadow){
15030             this.shadow.hide();
15031         }
15032         if(this.shim) {
15033           this.shim.hide();
15034         }
15035         // sometimes animateTarget seems to get set.. causing problems...
15036         // this just double checks..
15037         if(this.animateTarget && Roo.get(this.animateTarget)) {
15038            this.animHide(callback);
15039         }else{
15040             this.el.hide();
15041             this.hideEl(callback);
15042         }
15043         return this;
15044     },
15045
15046     // private
15047     hideEl : function(callback){
15048         this.proxy.hide();
15049         if(this.modal){
15050             this.mask.hide();
15051             Roo.get(document.body).removeClass("x-body-masked");
15052         }
15053         this.fireEvent("hide", this);
15054         if(typeof callback == "function"){
15055             callback();
15056         }
15057     },
15058
15059     // private
15060     hideAction : function(){
15061         this.setLeft("-10000px");
15062         this.setTop("-10000px");
15063         this.setStyle("visibility", "hidden");
15064     },
15065
15066     // private
15067     refreshSize : function(){
15068         this.size = this.el.getSize();
15069         this.xy = this.el.getXY();
15070         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15071     },
15072
15073     // private
15074     // z-index is managed by the DialogManager and may be overwritten at any time
15075     setZIndex : function(index){
15076         if(this.modal){
15077             this.mask.setStyle("z-index", index);
15078         }
15079         if(this.shim){
15080             this.shim.setStyle("z-index", ++index);
15081         }
15082         if(this.shadow){
15083             this.shadow.setZIndex(++index);
15084         }
15085         this.el.setStyle("z-index", ++index);
15086         if(this.proxy){
15087             this.proxy.setStyle("z-index", ++index);
15088         }
15089         if(this.resizer){
15090             this.resizer.proxy.setStyle("z-index", ++index);
15091         }
15092
15093         this.lastZIndex = index;
15094     },
15095
15096     /**
15097      * Returns the element for this dialog
15098      * @return {Roo.Element} The underlying dialog Element
15099      */
15100     getEl : function(){
15101         return this.el;
15102     }
15103 });
15104
15105 /**
15106  * @class Roo.DialogManager
15107  * Provides global access to BasicDialogs that have been created and
15108  * support for z-indexing (layering) multiple open dialogs.
15109  */
15110 Roo.DialogManager = function(){
15111     var list = {};
15112     var accessList = [];
15113     var front = null;
15114
15115     // private
15116     var sortDialogs = function(d1, d2){
15117         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15118     };
15119
15120     // private
15121     var orderDialogs = function(){
15122         accessList.sort(sortDialogs);
15123         var seed = Roo.DialogManager.zseed;
15124         for(var i = 0, len = accessList.length; i < len; i++){
15125             var dlg = accessList[i];
15126             if(dlg){
15127                 dlg.setZIndex(seed + (i*10));
15128             }
15129         }
15130     };
15131
15132     return {
15133         /**
15134          * The starting z-index for BasicDialogs (defaults to 9000)
15135          * @type Number The z-index value
15136          */
15137         zseed : 9000,
15138
15139         // private
15140         register : function(dlg){
15141             list[dlg.id] = dlg;
15142             accessList.push(dlg);
15143         },
15144
15145         // private
15146         unregister : function(dlg){
15147             delete list[dlg.id];
15148             var i=0;
15149             var len=0;
15150             if(!accessList.indexOf){
15151                 for(  i = 0, len = accessList.length; i < len; i++){
15152                     if(accessList[i] == dlg){
15153                         accessList.splice(i, 1);
15154                         return;
15155                     }
15156                 }
15157             }else{
15158                  i = accessList.indexOf(dlg);
15159                 if(i != -1){
15160                     accessList.splice(i, 1);
15161                 }
15162             }
15163         },
15164
15165         /**
15166          * Gets a registered dialog by id
15167          * @param {String/Object} id The id of the dialog or a dialog
15168          * @return {Roo.BasicDialog} this
15169          */
15170         get : function(id){
15171             return typeof id == "object" ? id : list[id];
15172         },
15173
15174         /**
15175          * Brings the specified dialog to the front
15176          * @param {String/Object} dlg The id of the dialog or a dialog
15177          * @return {Roo.BasicDialog} this
15178          */
15179         bringToFront : function(dlg){
15180             dlg = this.get(dlg);
15181             if(dlg != front){
15182                 front = dlg;
15183                 dlg._lastAccess = new Date().getTime();
15184                 orderDialogs();
15185             }
15186             return dlg;
15187         },
15188
15189         /**
15190          * Sends the specified dialog to the back
15191          * @param {String/Object} dlg The id of the dialog or a dialog
15192          * @return {Roo.BasicDialog} this
15193          */
15194         sendToBack : function(dlg){
15195             dlg = this.get(dlg);
15196             dlg._lastAccess = -(new Date().getTime());
15197             orderDialogs();
15198             return dlg;
15199         },
15200
15201         /**
15202          * Hides all dialogs
15203          */
15204         hideAll : function(){
15205             for(var id in list){
15206                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15207                     list[id].hide();
15208                 }
15209             }
15210         }
15211     };
15212 }();
15213
15214 /**
15215  * @class Roo.LayoutDialog
15216  * @extends Roo.BasicDialog
15217  * Dialog which provides adjustments for working with a layout in a Dialog.
15218  * Add your necessary layout config options to the dialog's config.<br>
15219  * Example usage (including a nested layout):
15220  * <pre><code>
15221 if(!dialog){
15222     dialog = new Roo.LayoutDialog("download-dlg", {
15223         modal: true,
15224         width:600,
15225         height:450,
15226         shadow:true,
15227         minWidth:500,
15228         minHeight:350,
15229         autoTabs:true,
15230         proxyDrag:true,
15231         // layout config merges with the dialog config
15232         center:{
15233             tabPosition: "top",
15234             alwaysShowTabs: true
15235         }
15236     });
15237     dialog.addKeyListener(27, dialog.hide, dialog);
15238     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15239     dialog.addButton("Build It!", this.getDownload, this);
15240
15241     // we can even add nested layouts
15242     var innerLayout = new Roo.BorderLayout("dl-inner", {
15243         east: {
15244             initialSize: 200,
15245             autoScroll:true,
15246             split:true
15247         },
15248         center: {
15249             autoScroll:true
15250         }
15251     });
15252     innerLayout.beginUpdate();
15253     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15254     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15255     innerLayout.endUpdate(true);
15256
15257     var layout = dialog.getLayout();
15258     layout.beginUpdate();
15259     layout.add("center", new Roo.ContentPanel("standard-panel",
15260                         {title: "Download the Source", fitToFrame:true}));
15261     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15262                {title: "Build your own roo.js"}));
15263     layout.getRegion("center").showPanel(sp);
15264     layout.endUpdate();
15265 }
15266 </code></pre>
15267     * @constructor
15268     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15269     * @param {Object} config configuration options
15270   */
15271 Roo.LayoutDialog = function(el, cfg){
15272     
15273     var config=  cfg;
15274     if (typeof(cfg) == 'undefined') {
15275         config = Roo.apply({}, el);
15276         // not sure why we use documentElement here.. - it should always be body.
15277         // IE7 borks horribly if we use documentElement.
15278         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
15279         //config.autoCreate = true;
15280     }
15281     
15282     
15283     config.autoTabs = false;
15284     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15285     this.body.setStyle({overflow:"hidden", position:"relative"});
15286     this.layout = new Roo.BorderLayout(this.body.dom, config);
15287     this.layout.monitorWindowResize = false;
15288     this.el.addClass("x-dlg-auto-layout");
15289     // fix case when center region overwrites center function
15290     this.center = Roo.BasicDialog.prototype.center;
15291     this.on("show", this.layout.layout, this.layout, true);
15292     if (config.items) {
15293         var xitems = config.items;
15294         delete config.items;
15295         Roo.each(xitems, this.addxtype, this);
15296     }
15297     
15298     
15299 };
15300 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15301     /**
15302      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15303      * @deprecated
15304      */
15305     endUpdate : function(){
15306         this.layout.endUpdate();
15307     },
15308
15309     /**
15310      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15311      *  @deprecated
15312      */
15313     beginUpdate : function(){
15314         this.layout.beginUpdate();
15315     },
15316
15317     /**
15318      * Get the BorderLayout for this dialog
15319      * @return {Roo.BorderLayout}
15320      */
15321     getLayout : function(){
15322         return this.layout;
15323     },
15324
15325     showEl : function(){
15326         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15327         if(Roo.isIE7){
15328             this.layout.layout();
15329         }
15330     },
15331
15332     // private
15333     // Use the syncHeightBeforeShow config option to control this automatically
15334     syncBodyHeight : function(){
15335         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15336         if(this.layout){this.layout.layout();}
15337     },
15338     
15339       /**
15340      * Add an xtype element (actually adds to the layout.)
15341      * @return {Object} xdata xtype object data.
15342      */
15343     
15344     addxtype : function(c) {
15345         return this.layout.addxtype(c);
15346     }
15347 });/*
15348  * Based on:
15349  * Ext JS Library 1.1.1
15350  * Copyright(c) 2006-2007, Ext JS, LLC.
15351  *
15352  * Originally Released Under LGPL - original licence link has changed is not relivant.
15353  *
15354  * Fork - LGPL
15355  * <script type="text/javascript">
15356  */
15357  
15358 /**
15359  * @class Roo.MessageBox
15360  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15361  * Example usage:
15362  *<pre><code>
15363 // Basic alert:
15364 Roo.Msg.alert('Status', 'Changes saved successfully.');
15365
15366 // Prompt for user data:
15367 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15368     if (btn == 'ok'){
15369         // process text value...
15370     }
15371 });
15372
15373 // Show a dialog using config options:
15374 Roo.Msg.show({
15375    title:'Save Changes?',
15376    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15377    buttons: Roo.Msg.YESNOCANCEL,
15378    fn: processResult,
15379    animEl: 'elId'
15380 });
15381 </code></pre>
15382  * @singleton
15383  */
15384 Roo.MessageBox = function(){
15385     var dlg, opt, mask, waitTimer;
15386     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15387     var buttons, activeTextEl, bwidth;
15388
15389     // private
15390     var handleButton = function(button){
15391         dlg.hide();
15392         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15393     };
15394
15395     // private
15396     var handleHide = function(){
15397         if(opt && opt.cls){
15398             dlg.el.removeClass(opt.cls);
15399         }
15400         if(waitTimer){
15401             Roo.TaskMgr.stop(waitTimer);
15402             waitTimer = null;
15403         }
15404     };
15405
15406     // private
15407     var updateButtons = function(b){
15408         var width = 0;
15409         if(!b){
15410             buttons["ok"].hide();
15411             buttons["cancel"].hide();
15412             buttons["yes"].hide();
15413             buttons["no"].hide();
15414             dlg.footer.dom.style.display = 'none';
15415             return width;
15416         }
15417         dlg.footer.dom.style.display = '';
15418         for(var k in buttons){
15419             if(typeof buttons[k] != "function"){
15420                 if(b[k]){
15421                     buttons[k].show();
15422                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15423                     width += buttons[k].el.getWidth()+15;
15424                 }else{
15425                     buttons[k].hide();
15426                 }
15427             }
15428         }
15429         return width;
15430     };
15431
15432     // private
15433     var handleEsc = function(d, k, e){
15434         if(opt && opt.closable !== false){
15435             dlg.hide();
15436         }
15437         if(e){
15438             e.stopEvent();
15439         }
15440     };
15441
15442     return {
15443         /**
15444          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15445          * @return {Roo.BasicDialog} The BasicDialog element
15446          */
15447         getDialog : function(){
15448            if(!dlg){
15449                 dlg = new Roo.BasicDialog("x-msg-box", {
15450                     autoCreate : true,
15451                     shadow: true,
15452                     draggable: true,
15453                     resizable:false,
15454                     constraintoviewport:false,
15455                     fixedcenter:true,
15456                     collapsible : false,
15457                     shim:true,
15458                     modal: true,
15459                     width:400, height:100,
15460                     buttonAlign:"center",
15461                     closeClick : function(){
15462                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15463                             handleButton("no");
15464                         }else{
15465                             handleButton("cancel");
15466                         }
15467                     }
15468                 });
15469                 dlg.on("hide", handleHide);
15470                 mask = dlg.mask;
15471                 dlg.addKeyListener(27, handleEsc);
15472                 buttons = {};
15473                 var bt = this.buttonText;
15474                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15475                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15476                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15477                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15478                 bodyEl = dlg.body.createChild({
15479
15480                     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>'
15481                 });
15482                 msgEl = bodyEl.dom.firstChild;
15483                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15484                 textboxEl.enableDisplayMode();
15485                 textboxEl.addKeyListener([10,13], function(){
15486                     if(dlg.isVisible() && opt && opt.buttons){
15487                         if(opt.buttons.ok){
15488                             handleButton("ok");
15489                         }else if(opt.buttons.yes){
15490                             handleButton("yes");
15491                         }
15492                     }
15493                 });
15494                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15495                 textareaEl.enableDisplayMode();
15496                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15497                 progressEl.enableDisplayMode();
15498                 var pf = progressEl.dom.firstChild;
15499                 if (pf) {
15500                     pp = Roo.get(pf.firstChild);
15501                     pp.setHeight(pf.offsetHeight);
15502                 }
15503                 
15504             }
15505             return dlg;
15506         },
15507
15508         /**
15509          * Updates the message box body text
15510          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15511          * the XHTML-compliant non-breaking space character '&amp;#160;')
15512          * @return {Roo.MessageBox} This message box
15513          */
15514         updateText : function(text){
15515             if(!dlg.isVisible() && !opt.width){
15516                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15517             }
15518             msgEl.innerHTML = text || '&#160;';
15519             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
15520                         Math.max(opt.minWidth || this.minWidth, bwidth));
15521             if(opt.prompt){
15522                 activeTextEl.setWidth(w);
15523             }
15524             if(dlg.isVisible()){
15525                 dlg.fixedcenter = false;
15526             }
15527             dlg.setContentSize(w, bodyEl.getHeight());
15528             if(dlg.isVisible()){
15529                 dlg.fixedcenter = true;
15530             }
15531             return this;
15532         },
15533
15534         /**
15535          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15536          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15537          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15538          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15539          * @return {Roo.MessageBox} This message box
15540          */
15541         updateProgress : function(value, text){
15542             if(text){
15543                 this.updateText(text);
15544             }
15545             if (pp) { // weird bug on my firefox - for some reason this is not defined
15546                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15547             }
15548             return this;
15549         },        
15550
15551         /**
15552          * Returns true if the message box is currently displayed
15553          * @return {Boolean} True if the message box is visible, else false
15554          */
15555         isVisible : function(){
15556             return dlg && dlg.isVisible();  
15557         },
15558
15559         /**
15560          * Hides the message box if it is displayed
15561          */
15562         hide : function(){
15563             if(this.isVisible()){
15564                 dlg.hide();
15565             }  
15566         },
15567
15568         /**
15569          * Displays a new message box, or reinitializes an existing message box, based on the config options
15570          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15571          * The following config object properties are supported:
15572          * <pre>
15573 Property    Type             Description
15574 ----------  ---------------  ------------------------------------------------------------------------------------
15575 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15576                                    closes (defaults to undefined)
15577 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15578                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15579 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15580                                    progress and wait dialogs will ignore this property and always hide the
15581                                    close button as they can only be closed programmatically.
15582 cls               String           A custom CSS class to apply to the message box element
15583 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15584                                    displayed (defaults to 75)
15585 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15586                                    function will be btn (the name of the button that was clicked, if applicable,
15587                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15588                                    Progress and wait dialogs will ignore this option since they do not respond to
15589                                    user actions and can only be closed programmatically, so any required function
15590                                    should be called by the same code after it closes the dialog.
15591 icon              String           A CSS class that provides a background image to be used as an icon for
15592                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15593 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15594 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15595 modal             Boolean          False to allow user interaction with the page while the message box is
15596                                    displayed (defaults to true)
15597 msg               String           A string that will replace the existing message box body text (defaults
15598                                    to the XHTML-compliant non-breaking space character '&#160;')
15599 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15600 progress          Boolean          True to display a progress bar (defaults to false)
15601 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15602 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15603 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15604 title             String           The title text
15605 value             String           The string value to set into the active textbox element if displayed
15606 wait              Boolean          True to display a progress bar (defaults to false)
15607 width             Number           The width of the dialog in pixels
15608 </pre>
15609          *
15610          * Example usage:
15611          * <pre><code>
15612 Roo.Msg.show({
15613    title: 'Address',
15614    msg: 'Please enter your address:',
15615    width: 300,
15616    buttons: Roo.MessageBox.OKCANCEL,
15617    multiline: true,
15618    fn: saveAddress,
15619    animEl: 'addAddressBtn'
15620 });
15621 </code></pre>
15622          * @param {Object} config Configuration options
15623          * @return {Roo.MessageBox} This message box
15624          */
15625         show : function(options){
15626             if(this.isVisible()){
15627                 this.hide();
15628             }
15629             var d = this.getDialog();
15630             opt = options;
15631             d.setTitle(opt.title || "&#160;");
15632             d.close.setDisplayed(opt.closable !== false);
15633             activeTextEl = textboxEl;
15634             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15635             if(opt.prompt){
15636                 if(opt.multiline){
15637                     textboxEl.hide();
15638                     textareaEl.show();
15639                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15640                         opt.multiline : this.defaultTextHeight);
15641                     activeTextEl = textareaEl;
15642                 }else{
15643                     textboxEl.show();
15644                     textareaEl.hide();
15645                 }
15646             }else{
15647                 textboxEl.hide();
15648                 textareaEl.hide();
15649             }
15650             progressEl.setDisplayed(opt.progress === true);
15651             this.updateProgress(0);
15652             activeTextEl.dom.value = opt.value || "";
15653             if(opt.prompt){
15654                 dlg.setDefaultButton(activeTextEl);
15655             }else{
15656                 var bs = opt.buttons;
15657                 var db = null;
15658                 if(bs && bs.ok){
15659                     db = buttons["ok"];
15660                 }else if(bs && bs.yes){
15661                     db = buttons["yes"];
15662                 }
15663                 dlg.setDefaultButton(db);
15664             }
15665             bwidth = updateButtons(opt.buttons);
15666             this.updateText(opt.msg);
15667             if(opt.cls){
15668                 d.el.addClass(opt.cls);
15669             }
15670             d.proxyDrag = opt.proxyDrag === true;
15671             d.modal = opt.modal !== false;
15672             d.mask = opt.modal !== false ? mask : false;
15673             if(!d.isVisible()){
15674                 // force it to the end of the z-index stack so it gets a cursor in FF
15675                 document.body.appendChild(dlg.el.dom);
15676                 d.animateTarget = null;
15677                 d.show(options.animEl);
15678             }
15679             return this;
15680         },
15681
15682         /**
15683          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15684          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15685          * and closing the message box when the process is complete.
15686          * @param {String} title The title bar text
15687          * @param {String} msg The message box body text
15688          * @return {Roo.MessageBox} This message box
15689          */
15690         progress : function(title, msg){
15691             this.show({
15692                 title : title,
15693                 msg : msg,
15694                 buttons: false,
15695                 progress:true,
15696                 closable:false,
15697                 minWidth: this.minProgressWidth,
15698                 modal : true
15699             });
15700             return this;
15701         },
15702
15703         /**
15704          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15705          * If a callback function is passed it will be called after the user clicks the button, and the
15706          * id of the button that was clicked will be passed as the only parameter to the callback
15707          * (could also be the top-right close button).
15708          * @param {String} title The title bar text
15709          * @param {String} msg The message box body text
15710          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15711          * @param {Object} scope (optional) The scope of the callback function
15712          * @return {Roo.MessageBox} This message box
15713          */
15714         alert : function(title, msg, fn, scope){
15715             this.show({
15716                 title : title,
15717                 msg : msg,
15718                 buttons: this.OK,
15719                 fn: fn,
15720                 scope : scope,
15721                 modal : true
15722             });
15723             return this;
15724         },
15725
15726         /**
15727          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15728          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15729          * You are responsible for closing the message box when the process is complete.
15730          * @param {String} msg The message box body text
15731          * @param {String} title (optional) The title bar text
15732          * @return {Roo.MessageBox} This message box
15733          */
15734         wait : function(msg, title){
15735             this.show({
15736                 title : title,
15737                 msg : msg,
15738                 buttons: false,
15739                 closable:false,
15740                 progress:true,
15741                 modal:true,
15742                 width:300,
15743                 wait:true
15744             });
15745             waitTimer = Roo.TaskMgr.start({
15746                 run: function(i){
15747                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15748                 },
15749                 interval: 1000
15750             });
15751             return this;
15752         },
15753
15754         /**
15755          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15756          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15757          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15758          * @param {String} title The title bar text
15759          * @param {String} msg The message box body text
15760          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15761          * @param {Object} scope (optional) The scope of the callback function
15762          * @return {Roo.MessageBox} This message box
15763          */
15764         confirm : function(title, msg, fn, scope){
15765             this.show({
15766                 title : title,
15767                 msg : msg,
15768                 buttons: this.YESNO,
15769                 fn: fn,
15770                 scope : scope,
15771                 modal : true
15772             });
15773             return this;
15774         },
15775
15776         /**
15777          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15778          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15779          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15780          * (could also be the top-right close button) and the text that was entered will be passed as the two
15781          * parameters to the callback.
15782          * @param {String} title The title bar text
15783          * @param {String} msg The message box body text
15784          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15785          * @param {Object} scope (optional) The scope of the callback function
15786          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15787          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15788          * @return {Roo.MessageBox} This message box
15789          */
15790         prompt : function(title, msg, fn, scope, multiline){
15791             this.show({
15792                 title : title,
15793                 msg : msg,
15794                 buttons: this.OKCANCEL,
15795                 fn: fn,
15796                 minWidth:250,
15797                 scope : scope,
15798                 prompt:true,
15799                 multiline: multiline,
15800                 modal : true
15801             });
15802             return this;
15803         },
15804
15805         /**
15806          * Button config that displays a single OK button
15807          * @type Object
15808          */
15809         OK : {ok:true},
15810         /**
15811          * Button config that displays Yes and No buttons
15812          * @type Object
15813          */
15814         YESNO : {yes:true, no:true},
15815         /**
15816          * Button config that displays OK and Cancel buttons
15817          * @type Object
15818          */
15819         OKCANCEL : {ok:true, cancel:true},
15820         /**
15821          * Button config that displays Yes, No and Cancel buttons
15822          * @type Object
15823          */
15824         YESNOCANCEL : {yes:true, no:true, cancel:true},
15825
15826         /**
15827          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
15828          * @type Number
15829          */
15830         defaultTextHeight : 75,
15831         /**
15832          * The maximum width in pixels of the message box (defaults to 600)
15833          * @type Number
15834          */
15835         maxWidth : 600,
15836         /**
15837          * The minimum width in pixels of the message box (defaults to 100)
15838          * @type Number
15839          */
15840         minWidth : 100,
15841         /**
15842          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
15843          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
15844          * @type Number
15845          */
15846         minProgressWidth : 250,
15847         /**
15848          * An object containing the default button text strings that can be overriden for localized language support.
15849          * Supported properties are: ok, cancel, yes and no.
15850          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
15851          * @type Object
15852          */
15853         buttonText : {
15854             ok : "OK",
15855             cancel : "Cancel",
15856             yes : "Yes",
15857             no : "No"
15858         }
15859     };
15860 }();
15861
15862 /**
15863  * Shorthand for {@link Roo.MessageBox}
15864  */
15865 Roo.Msg = Roo.MessageBox;/*
15866  * Based on:
15867  * Ext JS Library 1.1.1
15868  * Copyright(c) 2006-2007, Ext JS, LLC.
15869  *
15870  * Originally Released Under LGPL - original licence link has changed is not relivant.
15871  *
15872  * Fork - LGPL
15873  * <script type="text/javascript">
15874  */
15875 /**
15876  * @class Roo.QuickTips
15877  * Provides attractive and customizable tooltips for any element.
15878  * @singleton
15879  */
15880 Roo.QuickTips = function(){
15881     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
15882     var ce, bd, xy, dd;
15883     var visible = false, disabled = true, inited = false;
15884     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
15885     
15886     var onOver = function(e){
15887         if(disabled){
15888             return;
15889         }
15890         var t = e.getTarget();
15891         if(!t || t.nodeType !== 1 || t == document || t == document.body){
15892             return;
15893         }
15894         if(ce && t == ce.el){
15895             clearTimeout(hideProc);
15896             return;
15897         }
15898         if(t && tagEls[t.id]){
15899             tagEls[t.id].el = t;
15900             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
15901             return;
15902         }
15903         var ttp, et = Roo.fly(t);
15904         var ns = cfg.namespace;
15905         if(tm.interceptTitles && t.title){
15906             ttp = t.title;
15907             t.qtip = ttp;
15908             t.removeAttribute("title");
15909             e.preventDefault();
15910         }else{
15911             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
15912         }
15913         if(ttp){
15914             showProc = show.defer(tm.showDelay, tm, [{
15915                 el: t, 
15916                 text: ttp, 
15917                 width: et.getAttributeNS(ns, cfg.width),
15918                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
15919                 title: et.getAttributeNS(ns, cfg.title),
15920                     cls: et.getAttributeNS(ns, cfg.cls)
15921             }]);
15922         }
15923     };
15924     
15925     var onOut = function(e){
15926         clearTimeout(showProc);
15927         var t = e.getTarget();
15928         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
15929             hideProc = setTimeout(hide, tm.hideDelay);
15930         }
15931     };
15932     
15933     var onMove = function(e){
15934         if(disabled){
15935             return;
15936         }
15937         xy = e.getXY();
15938         xy[1] += 18;
15939         if(tm.trackMouse && ce){
15940             el.setXY(xy);
15941         }
15942     };
15943     
15944     var onDown = function(e){
15945         clearTimeout(showProc);
15946         clearTimeout(hideProc);
15947         if(!e.within(el)){
15948             if(tm.hideOnClick){
15949                 hide();
15950                 tm.disable();
15951                 tm.enable.defer(100, tm);
15952             }
15953         }
15954     };
15955     
15956     var getPad = function(){
15957         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
15958     };
15959
15960     var show = function(o){
15961         if(disabled){
15962             return;
15963         }
15964         clearTimeout(dismissProc);
15965         ce = o;
15966         if(removeCls){ // in case manually hidden
15967             el.removeClass(removeCls);
15968             removeCls = null;
15969         }
15970         if(ce.cls){
15971             el.addClass(ce.cls);
15972             removeCls = ce.cls;
15973         }
15974         if(ce.title){
15975             tipTitle.update(ce.title);
15976             tipTitle.show();
15977         }else{
15978             tipTitle.update('');
15979             tipTitle.hide();
15980         }
15981         el.dom.style.width  = tm.maxWidth+'px';
15982         //tipBody.dom.style.width = '';
15983         tipBodyText.update(o.text);
15984         var p = getPad(), w = ce.width;
15985         if(!w){
15986             var td = tipBodyText.dom;
15987             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
15988             if(aw > tm.maxWidth){
15989                 w = tm.maxWidth;
15990             }else if(aw < tm.minWidth){
15991                 w = tm.minWidth;
15992             }else{
15993                 w = aw;
15994             }
15995         }
15996         //tipBody.setWidth(w);
15997         el.setWidth(parseInt(w, 10) + p);
15998         if(ce.autoHide === false){
15999             close.setDisplayed(true);
16000             if(dd){
16001                 dd.unlock();
16002             }
16003         }else{
16004             close.setDisplayed(false);
16005             if(dd){
16006                 dd.lock();
16007             }
16008         }
16009         if(xy){
16010             el.avoidY = xy[1]-18;
16011             el.setXY(xy);
16012         }
16013         if(tm.animate){
16014             el.setOpacity(.1);
16015             el.setStyle("visibility", "visible");
16016             el.fadeIn({callback: afterShow});
16017         }else{
16018             afterShow();
16019         }
16020     };
16021     
16022     var afterShow = function(){
16023         if(ce){
16024             el.show();
16025             esc.enable();
16026             if(tm.autoDismiss && ce.autoHide !== false){
16027                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16028             }
16029         }
16030     };
16031     
16032     var hide = function(noanim){
16033         clearTimeout(dismissProc);
16034         clearTimeout(hideProc);
16035         ce = null;
16036         if(el.isVisible()){
16037             esc.disable();
16038             if(noanim !== true && tm.animate){
16039                 el.fadeOut({callback: afterHide});
16040             }else{
16041                 afterHide();
16042             } 
16043         }
16044     };
16045     
16046     var afterHide = function(){
16047         el.hide();
16048         if(removeCls){
16049             el.removeClass(removeCls);
16050             removeCls = null;
16051         }
16052     };
16053     
16054     return {
16055         /**
16056         * @cfg {Number} minWidth
16057         * The minimum width of the quick tip (defaults to 40)
16058         */
16059        minWidth : 40,
16060         /**
16061         * @cfg {Number} maxWidth
16062         * The maximum width of the quick tip (defaults to 300)
16063         */
16064        maxWidth : 300,
16065         /**
16066         * @cfg {Boolean} interceptTitles
16067         * True to automatically use the element's DOM title value if available (defaults to false)
16068         */
16069        interceptTitles : false,
16070         /**
16071         * @cfg {Boolean} trackMouse
16072         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16073         */
16074        trackMouse : false,
16075         /**
16076         * @cfg {Boolean} hideOnClick
16077         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16078         */
16079        hideOnClick : true,
16080         /**
16081         * @cfg {Number} showDelay
16082         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16083         */
16084        showDelay : 500,
16085         /**
16086         * @cfg {Number} hideDelay
16087         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16088         */
16089        hideDelay : 200,
16090         /**
16091         * @cfg {Boolean} autoHide
16092         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16093         * Used in conjunction with hideDelay.
16094         */
16095        autoHide : true,
16096         /**
16097         * @cfg {Boolean}
16098         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16099         * (defaults to true).  Used in conjunction with autoDismissDelay.
16100         */
16101        autoDismiss : true,
16102         /**
16103         * @cfg {Number}
16104         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16105         */
16106        autoDismissDelay : 5000,
16107        /**
16108         * @cfg {Boolean} animate
16109         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16110         */
16111        animate : false,
16112
16113        /**
16114         * @cfg {String} title
16115         * Title text to display (defaults to '').  This can be any valid HTML markup.
16116         */
16117         title: '',
16118        /**
16119         * @cfg {String} text
16120         * Body text to display (defaults to '').  This can be any valid HTML markup.
16121         */
16122         text : '',
16123        /**
16124         * @cfg {String} cls
16125         * A CSS class to apply to the base quick tip element (defaults to '').
16126         */
16127         cls : '',
16128        /**
16129         * @cfg {Number} width
16130         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16131         * minWidth or maxWidth.
16132         */
16133         width : null,
16134
16135     /**
16136      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16137      * or display QuickTips in a page.
16138      */
16139        init : function(){
16140           tm = Roo.QuickTips;
16141           cfg = tm.tagConfig;
16142           if(!inited){
16143               if(!Roo.isReady){ // allow calling of init() before onReady
16144                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16145                   return;
16146               }
16147               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16148               el.fxDefaults = {stopFx: true};
16149               // maximum custom styling
16150               //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>');
16151               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>');              
16152               tipTitle = el.child('h3');
16153               tipTitle.enableDisplayMode("block");
16154               tipBody = el.child('div.x-tip-bd');
16155               tipBodyText = el.child('div.x-tip-bd-inner');
16156               //bdLeft = el.child('div.x-tip-bd-left');
16157               //bdRight = el.child('div.x-tip-bd-right');
16158               close = el.child('div.x-tip-close');
16159               close.enableDisplayMode("block");
16160               close.on("click", hide);
16161               var d = Roo.get(document);
16162               d.on("mousedown", onDown);
16163               d.on("mouseover", onOver);
16164               d.on("mouseout", onOut);
16165               d.on("mousemove", onMove);
16166               esc = d.addKeyListener(27, hide);
16167               esc.disable();
16168               if(Roo.dd.DD){
16169                   dd = el.initDD("default", null, {
16170                       onDrag : function(){
16171                           el.sync();  
16172                       }
16173                   });
16174                   dd.setHandleElId(tipTitle.id);
16175                   dd.lock();
16176               }
16177               inited = true;
16178           }
16179           this.enable(); 
16180        },
16181
16182     /**
16183      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16184      * are supported:
16185      * <pre>
16186 Property    Type                   Description
16187 ----------  ---------------------  ------------------------------------------------------------------------
16188 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16189      * </ul>
16190      * @param {Object} config The config object
16191      */
16192        register : function(config){
16193            var cs = config instanceof Array ? config : arguments;
16194            for(var i = 0, len = cs.length; i < len; i++) {
16195                var c = cs[i];
16196                var target = c.target;
16197                if(target){
16198                    if(target instanceof Array){
16199                        for(var j = 0, jlen = target.length; j < jlen; j++){
16200                            tagEls[target[j]] = c;
16201                        }
16202                    }else{
16203                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16204                    }
16205                }
16206            }
16207        },
16208
16209     /**
16210      * Removes this quick tip from its element and destroys it.
16211      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16212      */
16213        unregister : function(el){
16214            delete tagEls[Roo.id(el)];
16215        },
16216
16217     /**
16218      * Enable this quick tip.
16219      */
16220        enable : function(){
16221            if(inited && disabled){
16222                locks.pop();
16223                if(locks.length < 1){
16224                    disabled = false;
16225                }
16226            }
16227        },
16228
16229     /**
16230      * Disable this quick tip.
16231      */
16232        disable : function(){
16233           disabled = true;
16234           clearTimeout(showProc);
16235           clearTimeout(hideProc);
16236           clearTimeout(dismissProc);
16237           if(ce){
16238               hide(true);
16239           }
16240           locks.push(1);
16241        },
16242
16243     /**
16244      * Returns true if the quick tip is enabled, else false.
16245      */
16246        isEnabled : function(){
16247             return !disabled;
16248        },
16249
16250         // private
16251        tagConfig : {
16252            namespace : "ext",
16253            attribute : "qtip",
16254            width : "width",
16255            target : "target",
16256            title : "qtitle",
16257            hide : "hide",
16258            cls : "qclass"
16259        }
16260    };
16261 }();
16262
16263 // backwards compat
16264 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16265  * Based on:
16266  * Ext JS Library 1.1.1
16267  * Copyright(c) 2006-2007, Ext JS, LLC.
16268  *
16269  * Originally Released Under LGPL - original licence link has changed is not relivant.
16270  *
16271  * Fork - LGPL
16272  * <script type="text/javascript">
16273  */
16274  
16275
16276 /**
16277  * @class Roo.tree.TreePanel
16278  * @extends Roo.data.Tree
16279
16280  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16281  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16282  * @cfg {Boolean} enableDD true to enable drag and drop
16283  * @cfg {Boolean} enableDrag true to enable just drag
16284  * @cfg {Boolean} enableDrop true to enable just drop
16285  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16286  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16287  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16288  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16289  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16290  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16291  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16292  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16293  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16294  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16295  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16296  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16297  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16298  * @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>
16299  * @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>
16300  * 
16301  * @constructor
16302  * @param {String/HTMLElement/Element} el The container element
16303  * @param {Object} config
16304  */
16305 Roo.tree.TreePanel = function(el, config){
16306     var root = false;
16307     var loader = false;
16308     if (config.root) {
16309         root = config.root;
16310         delete config.root;
16311     }
16312     if (config.loader) {
16313         loader = config.loader;
16314         delete config.loader;
16315     }
16316     
16317     Roo.apply(this, config);
16318     Roo.tree.TreePanel.superclass.constructor.call(this);
16319     this.el = Roo.get(el);
16320     this.el.addClass('x-tree');
16321     //console.log(root);
16322     if (root) {
16323         this.setRootNode( Roo.factory(root, Roo.tree));
16324     }
16325     if (loader) {
16326         this.loader = Roo.factory(loader, Roo.tree);
16327     }
16328    /**
16329     * Read-only. The id of the container element becomes this TreePanel's id.
16330     */
16331    this.id = this.el.id;
16332    this.addEvents({
16333         /**
16334         * @event beforeload
16335         * Fires before a node is loaded, return false to cancel
16336         * @param {Node} node The node being loaded
16337         */
16338         "beforeload" : true,
16339         /**
16340         * @event load
16341         * Fires when a node is loaded
16342         * @param {Node} node The node that was loaded
16343         */
16344         "load" : true,
16345         /**
16346         * @event textchange
16347         * Fires when the text for a node is changed
16348         * @param {Node} node The node
16349         * @param {String} text The new text
16350         * @param {String} oldText The old text
16351         */
16352         "textchange" : true,
16353         /**
16354         * @event beforeexpand
16355         * Fires before a node is expanded, return false to cancel.
16356         * @param {Node} node The node
16357         * @param {Boolean} deep
16358         * @param {Boolean} anim
16359         */
16360         "beforeexpand" : true,
16361         /**
16362         * @event beforecollapse
16363         * Fires before a node is collapsed, return false to cancel.
16364         * @param {Node} node The node
16365         * @param {Boolean} deep
16366         * @param {Boolean} anim
16367         */
16368         "beforecollapse" : true,
16369         /**
16370         * @event expand
16371         * Fires when a node is expanded
16372         * @param {Node} node The node
16373         */
16374         "expand" : true,
16375         /**
16376         * @event disabledchange
16377         * Fires when the disabled status of a node changes
16378         * @param {Node} node The node
16379         * @param {Boolean} disabled
16380         */
16381         "disabledchange" : true,
16382         /**
16383         * @event collapse
16384         * Fires when a node is collapsed
16385         * @param {Node} node The node
16386         */
16387         "collapse" : true,
16388         /**
16389         * @event beforeclick
16390         * Fires before click processing on a node. Return false to cancel the default action.
16391         * @param {Node} node The node
16392         * @param {Roo.EventObject} e The event object
16393         */
16394         "beforeclick":true,
16395         /**
16396         * @event checkchange
16397         * Fires when a node with a checkbox's checked property changes
16398         * @param {Node} this This node
16399         * @param {Boolean} checked
16400         */
16401         "checkchange":true,
16402         /**
16403         * @event click
16404         * Fires when a node is clicked
16405         * @param {Node} node The node
16406         * @param {Roo.EventObject} e The event object
16407         */
16408         "click":true,
16409         /**
16410         * @event dblclick
16411         * Fires when a node is double clicked
16412         * @param {Node} node The node
16413         * @param {Roo.EventObject} e The event object
16414         */
16415         "dblclick":true,
16416         /**
16417         * @event contextmenu
16418         * Fires when a node is right clicked
16419         * @param {Node} node The node
16420         * @param {Roo.EventObject} e The event object
16421         */
16422         "contextmenu":true,
16423         /**
16424         * @event beforechildrenrendered
16425         * Fires right before the child nodes for a node are rendered
16426         * @param {Node} node The node
16427         */
16428         "beforechildrenrendered":true,
16429        /**
16430              * @event startdrag
16431              * Fires when a node starts being dragged
16432              * @param {Roo.tree.TreePanel} this
16433              * @param {Roo.tree.TreeNode} node
16434              * @param {event} e The raw browser event
16435              */ 
16436             "startdrag" : true,
16437             /**
16438              * @event enddrag
16439              * Fires when a drag operation is complete
16440              * @param {Roo.tree.TreePanel} this
16441              * @param {Roo.tree.TreeNode} node
16442              * @param {event} e The raw browser event
16443              */
16444             "enddrag" : true,
16445             /**
16446              * @event dragdrop
16447              * Fires when a dragged node is dropped on a valid DD target
16448              * @param {Roo.tree.TreePanel} this
16449              * @param {Roo.tree.TreeNode} node
16450              * @param {DD} dd The dd it was dropped on
16451              * @param {event} e The raw browser event
16452              */
16453             "dragdrop" : true,
16454             /**
16455              * @event beforenodedrop
16456              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16457              * passed to handlers has the following properties:<br />
16458              * <ul style="padding:5px;padding-left:16px;">
16459              * <li>tree - The TreePanel</li>
16460              * <li>target - The node being targeted for the drop</li>
16461              * <li>data - The drag data from the drag source</li>
16462              * <li>point - The point of the drop - append, above or below</li>
16463              * <li>source - The drag source</li>
16464              * <li>rawEvent - Raw mouse event</li>
16465              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16466              * to be inserted by setting them on this object.</li>
16467              * <li>cancel - Set this to true to cancel the drop.</li>
16468              * </ul>
16469              * @param {Object} dropEvent
16470              */
16471             "beforenodedrop" : true,
16472             /**
16473              * @event nodedrop
16474              * Fires after a DD object is dropped on a node in this tree. The dropEvent
16475              * passed to handlers has the following properties:<br />
16476              * <ul style="padding:5px;padding-left:16px;">
16477              * <li>tree - The TreePanel</li>
16478              * <li>target - The node being targeted for the drop</li>
16479              * <li>data - The drag data from the drag source</li>
16480              * <li>point - The point of the drop - append, above or below</li>
16481              * <li>source - The drag source</li>
16482              * <li>rawEvent - Raw mouse event</li>
16483              * <li>dropNode - Dropped node(s).</li>
16484              * </ul>
16485              * @param {Object} dropEvent
16486              */
16487             "nodedrop" : true,
16488              /**
16489              * @event nodedragover
16490              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16491              * passed to handlers has the following properties:<br />
16492              * <ul style="padding:5px;padding-left:16px;">
16493              * <li>tree - The TreePanel</li>
16494              * <li>target - The node being targeted for the drop</li>
16495              * <li>data - The drag data from the drag source</li>
16496              * <li>point - The point of the drop - append, above or below</li>
16497              * <li>source - The drag source</li>
16498              * <li>rawEvent - Raw mouse event</li>
16499              * <li>dropNode - Drop node(s) provided by the source.</li>
16500              * <li>cancel - Set this to true to signal drop not allowed.</li>
16501              * </ul>
16502              * @param {Object} dragOverEvent
16503              */
16504             "nodedragover" : true
16505         
16506    });
16507    if(this.singleExpand){
16508        this.on("beforeexpand", this.restrictExpand, this);
16509    }
16510 };
16511 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16512     rootVisible : true,
16513     animate: Roo.enableFx,
16514     lines : true,
16515     enableDD : false,
16516     hlDrop : Roo.enableFx,
16517   
16518     renderer: false,
16519     
16520     rendererTip: false,
16521     // private
16522     restrictExpand : function(node){
16523         var p = node.parentNode;
16524         if(p){
16525             if(p.expandedChild && p.expandedChild.parentNode == p){
16526                 p.expandedChild.collapse();
16527             }
16528             p.expandedChild = node;
16529         }
16530     },
16531
16532     // private override
16533     setRootNode : function(node){
16534         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16535         if(!this.rootVisible){
16536             node.ui = new Roo.tree.RootTreeNodeUI(node);
16537         }
16538         return node;
16539     },
16540
16541     /**
16542      * Returns the container element for this TreePanel
16543      */
16544     getEl : function(){
16545         return this.el;
16546     },
16547
16548     /**
16549      * Returns the default TreeLoader for this TreePanel
16550      */
16551     getLoader : function(){
16552         return this.loader;
16553     },
16554
16555     /**
16556      * Expand all nodes
16557      */
16558     expandAll : function(){
16559         this.root.expand(true);
16560     },
16561
16562     /**
16563      * Collapse all nodes
16564      */
16565     collapseAll : function(){
16566         this.root.collapse(true);
16567     },
16568
16569     /**
16570      * Returns the selection model used by this TreePanel
16571      */
16572     getSelectionModel : function(){
16573         if(!this.selModel){
16574             this.selModel = new Roo.tree.DefaultSelectionModel();
16575         }
16576         return this.selModel;
16577     },
16578
16579     /**
16580      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16581      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16582      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16583      * @return {Array}
16584      */
16585     getChecked : function(a, startNode){
16586         startNode = startNode || this.root;
16587         var r = [];
16588         var f = function(){
16589             if(this.attributes.checked){
16590                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16591             }
16592         }
16593         startNode.cascade(f);
16594         return r;
16595     },
16596
16597     /**
16598      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16599      * @param {String} path
16600      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16601      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16602      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16603      */
16604     expandPath : function(path, attr, callback){
16605         attr = attr || "id";
16606         var keys = path.split(this.pathSeparator);
16607         var curNode = this.root;
16608         if(curNode.attributes[attr] != keys[1]){ // invalid root
16609             if(callback){
16610                 callback(false, null);
16611             }
16612             return;
16613         }
16614         var index = 1;
16615         var f = function(){
16616             if(++index == keys.length){
16617                 if(callback){
16618                     callback(true, curNode);
16619                 }
16620                 return;
16621             }
16622             var c = curNode.findChild(attr, keys[index]);
16623             if(!c){
16624                 if(callback){
16625                     callback(false, curNode);
16626                 }
16627                 return;
16628             }
16629             curNode = c;
16630             c.expand(false, false, f);
16631         };
16632         curNode.expand(false, false, f);
16633     },
16634
16635     /**
16636      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16637      * @param {String} path
16638      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16639      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16640      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16641      */
16642     selectPath : function(path, attr, callback){
16643         attr = attr || "id";
16644         var keys = path.split(this.pathSeparator);
16645         var v = keys.pop();
16646         if(keys.length > 0){
16647             var f = function(success, node){
16648                 if(success && node){
16649                     var n = node.findChild(attr, v);
16650                     if(n){
16651                         n.select();
16652                         if(callback){
16653                             callback(true, n);
16654                         }
16655                     }else if(callback){
16656                         callback(false, n);
16657                     }
16658                 }else{
16659                     if(callback){
16660                         callback(false, n);
16661                     }
16662                 }
16663             };
16664             this.expandPath(keys.join(this.pathSeparator), attr, f);
16665         }else{
16666             this.root.select();
16667             if(callback){
16668                 callback(true, this.root);
16669             }
16670         }
16671     },
16672
16673     getTreeEl : function(){
16674         return this.el;
16675     },
16676
16677     /**
16678      * Trigger rendering of this TreePanel
16679      */
16680     render : function(){
16681         if (this.innerCt) {
16682             return this; // stop it rendering more than once!!
16683         }
16684         
16685         this.innerCt = this.el.createChild({tag:"ul",
16686                cls:"x-tree-root-ct " +
16687                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16688
16689         if(this.containerScroll){
16690             Roo.dd.ScrollManager.register(this.el);
16691         }
16692         if((this.enableDD || this.enableDrop) && !this.dropZone){
16693            /**
16694             * The dropZone used by this tree if drop is enabled
16695             * @type Roo.tree.TreeDropZone
16696             */
16697              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16698                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16699            });
16700         }
16701         if((this.enableDD || this.enableDrag) && !this.dragZone){
16702            /**
16703             * The dragZone used by this tree if drag is enabled
16704             * @type Roo.tree.TreeDragZone
16705             */
16706             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16707                ddGroup: this.ddGroup || "TreeDD",
16708                scroll: this.ddScroll
16709            });
16710         }
16711         this.getSelectionModel().init(this);
16712         if (!this.root) {
16713             console.log("ROOT not set in tree");
16714             return;
16715         }
16716         this.root.render();
16717         if(!this.rootVisible){
16718             this.root.renderChildren();
16719         }
16720         return this;
16721     }
16722 });/*
16723  * Based on:
16724  * Ext JS Library 1.1.1
16725  * Copyright(c) 2006-2007, Ext JS, LLC.
16726  *
16727  * Originally Released Under LGPL - original licence link has changed is not relivant.
16728  *
16729  * Fork - LGPL
16730  * <script type="text/javascript">
16731  */
16732  
16733
16734 /**
16735  * @class Roo.tree.DefaultSelectionModel
16736  * @extends Roo.util.Observable
16737  * The default single selection for a TreePanel.
16738  */
16739 Roo.tree.DefaultSelectionModel = function(){
16740    this.selNode = null;
16741    
16742    this.addEvents({
16743        /**
16744         * @event selectionchange
16745         * Fires when the selected node changes
16746         * @param {DefaultSelectionModel} this
16747         * @param {TreeNode} node the new selection
16748         */
16749        "selectionchange" : true,
16750
16751        /**
16752         * @event beforeselect
16753         * Fires before the selected node changes, return false to cancel the change
16754         * @param {DefaultSelectionModel} this
16755         * @param {TreeNode} node the new selection
16756         * @param {TreeNode} node the old selection
16757         */
16758        "beforeselect" : true
16759    });
16760 };
16761
16762 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16763     init : function(tree){
16764         this.tree = tree;
16765         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16766         tree.on("click", this.onNodeClick, this);
16767     },
16768     
16769     onNodeClick : function(node, e){
16770         if (e.ctrlKey && this.selNode == node)  {
16771             this.unselect(node);
16772             return;
16773         }
16774         this.select(node);
16775     },
16776     
16777     /**
16778      * Select a node.
16779      * @param {TreeNode} node The node to select
16780      * @return {TreeNode} The selected node
16781      */
16782     select : function(node){
16783         var last = this.selNode;
16784         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
16785             if(last){
16786                 last.ui.onSelectedChange(false);
16787             }
16788             this.selNode = node;
16789             node.ui.onSelectedChange(true);
16790             this.fireEvent("selectionchange", this, node, last);
16791         }
16792         return node;
16793     },
16794     
16795     /**
16796      * Deselect a node.
16797      * @param {TreeNode} node The node to unselect
16798      */
16799     unselect : function(node){
16800         if(this.selNode == node){
16801             this.clearSelections();
16802         }    
16803     },
16804     
16805     /**
16806      * Clear all selections
16807      */
16808     clearSelections : function(){
16809         var n = this.selNode;
16810         if(n){
16811             n.ui.onSelectedChange(false);
16812             this.selNode = null;
16813             this.fireEvent("selectionchange", this, null);
16814         }
16815         return n;
16816     },
16817     
16818     /**
16819      * Get the selected node
16820      * @return {TreeNode} The selected node
16821      */
16822     getSelectedNode : function(){
16823         return this.selNode;    
16824     },
16825     
16826     /**
16827      * Returns true if the node is selected
16828      * @param {TreeNode} node The node to check
16829      * @return {Boolean}
16830      */
16831     isSelected : function(node){
16832         return this.selNode == node;  
16833     },
16834
16835     /**
16836      * Selects the node above the selected node in the tree, intelligently walking the nodes
16837      * @return TreeNode The new selection
16838      */
16839     selectPrevious : function(){
16840         var s = this.selNode || this.lastSelNode;
16841         if(!s){
16842             return null;
16843         }
16844         var ps = s.previousSibling;
16845         if(ps){
16846             if(!ps.isExpanded() || ps.childNodes.length < 1){
16847                 return this.select(ps);
16848             } else{
16849                 var lc = ps.lastChild;
16850                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
16851                     lc = lc.lastChild;
16852                 }
16853                 return this.select(lc);
16854             }
16855         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
16856             return this.select(s.parentNode);
16857         }
16858         return null;
16859     },
16860
16861     /**
16862      * Selects the node above the selected node in the tree, intelligently walking the nodes
16863      * @return TreeNode The new selection
16864      */
16865     selectNext : function(){
16866         var s = this.selNode || this.lastSelNode;
16867         if(!s){
16868             return null;
16869         }
16870         if(s.firstChild && s.isExpanded()){
16871              return this.select(s.firstChild);
16872          }else if(s.nextSibling){
16873              return this.select(s.nextSibling);
16874          }else if(s.parentNode){
16875             var newS = null;
16876             s.parentNode.bubble(function(){
16877                 if(this.nextSibling){
16878                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
16879                     return false;
16880                 }
16881             });
16882             return newS;
16883          }
16884         return null;
16885     },
16886
16887     onKeyDown : function(e){
16888         var s = this.selNode || this.lastSelNode;
16889         // undesirable, but required
16890         var sm = this;
16891         if(!s){
16892             return;
16893         }
16894         var k = e.getKey();
16895         switch(k){
16896              case e.DOWN:
16897                  e.stopEvent();
16898                  this.selectNext();
16899              break;
16900              case e.UP:
16901                  e.stopEvent();
16902                  this.selectPrevious();
16903              break;
16904              case e.RIGHT:
16905                  e.preventDefault();
16906                  if(s.hasChildNodes()){
16907                      if(!s.isExpanded()){
16908                          s.expand();
16909                      }else if(s.firstChild){
16910                          this.select(s.firstChild, e);
16911                      }
16912                  }
16913              break;
16914              case e.LEFT:
16915                  e.preventDefault();
16916                  if(s.hasChildNodes() && s.isExpanded()){
16917                      s.collapse();
16918                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
16919                      this.select(s.parentNode, e);
16920                  }
16921              break;
16922         };
16923     }
16924 });
16925
16926 /**
16927  * @class Roo.tree.MultiSelectionModel
16928  * @extends Roo.util.Observable
16929  * Multi selection for a TreePanel.
16930  */
16931 Roo.tree.MultiSelectionModel = function(){
16932    this.selNodes = [];
16933    this.selMap = {};
16934    this.addEvents({
16935        /**
16936         * @event selectionchange
16937         * Fires when the selected nodes change
16938         * @param {MultiSelectionModel} this
16939         * @param {Array} nodes Array of the selected nodes
16940         */
16941        "selectionchange" : true
16942    });
16943 };
16944
16945 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
16946     init : function(tree){
16947         this.tree = tree;
16948         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16949         tree.on("click", this.onNodeClick, this);
16950     },
16951     
16952     onNodeClick : function(node, e){
16953         this.select(node, e, e.ctrlKey);
16954     },
16955     
16956     /**
16957      * Select a node.
16958      * @param {TreeNode} node The node to select
16959      * @param {EventObject} e (optional) An event associated with the selection
16960      * @param {Boolean} keepExisting True to retain existing selections
16961      * @return {TreeNode} The selected node
16962      */
16963     select : function(node, e, keepExisting){
16964         if(keepExisting !== true){
16965             this.clearSelections(true);
16966         }
16967         if(this.isSelected(node)){
16968             this.lastSelNode = node;
16969             return node;
16970         }
16971         this.selNodes.push(node);
16972         this.selMap[node.id] = node;
16973         this.lastSelNode = node;
16974         node.ui.onSelectedChange(true);
16975         this.fireEvent("selectionchange", this, this.selNodes);
16976         return node;
16977     },
16978     
16979     /**
16980      * Deselect a node.
16981      * @param {TreeNode} node The node to unselect
16982      */
16983     unselect : function(node){
16984         if(this.selMap[node.id]){
16985             node.ui.onSelectedChange(false);
16986             var sn = this.selNodes;
16987             var index = -1;
16988             if(sn.indexOf){
16989                 index = sn.indexOf(node);
16990             }else{
16991                 for(var i = 0, len = sn.length; i < len; i++){
16992                     if(sn[i] == node){
16993                         index = i;
16994                         break;
16995                     }
16996                 }
16997             }
16998             if(index != -1){
16999                 this.selNodes.splice(index, 1);
17000             }
17001             delete this.selMap[node.id];
17002             this.fireEvent("selectionchange", this, this.selNodes);
17003         }
17004     },
17005     
17006     /**
17007      * Clear all selections
17008      */
17009     clearSelections : function(suppressEvent){
17010         var sn = this.selNodes;
17011         if(sn.length > 0){
17012             for(var i = 0, len = sn.length; i < len; i++){
17013                 sn[i].ui.onSelectedChange(false);
17014             }
17015             this.selNodes = [];
17016             this.selMap = {};
17017             if(suppressEvent !== true){
17018                 this.fireEvent("selectionchange", this, this.selNodes);
17019             }
17020         }
17021     },
17022     
17023     /**
17024      * Returns true if the node is selected
17025      * @param {TreeNode} node The node to check
17026      * @return {Boolean}
17027      */
17028     isSelected : function(node){
17029         return this.selMap[node.id] ? true : false;  
17030     },
17031     
17032     /**
17033      * Returns an array of the selected nodes
17034      * @return {Array}
17035      */
17036     getSelectedNodes : function(){
17037         return this.selNodes;    
17038     },
17039
17040     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17041
17042     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17043
17044     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17045 });/*
17046  * Based on:
17047  * Ext JS Library 1.1.1
17048  * Copyright(c) 2006-2007, Ext JS, LLC.
17049  *
17050  * Originally Released Under LGPL - original licence link has changed is not relivant.
17051  *
17052  * Fork - LGPL
17053  * <script type="text/javascript">
17054  */
17055  
17056 /**
17057  * @class Roo.tree.TreeNode
17058  * @extends Roo.data.Node
17059  * @cfg {String} text The text for this node
17060  * @cfg {Boolean} expanded true to start the node expanded
17061  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17062  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17063  * @cfg {Boolean} disabled true to start the node disabled
17064  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17065  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17066  * @cfg {String} cls A css class to be added to the node
17067  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17068  * @cfg {String} href URL of the link used for the node (defaults to #)
17069  * @cfg {String} hrefTarget target frame for the link
17070  * @cfg {String} qtip An Ext QuickTip for the node
17071  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17072  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17073  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17074  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17075  * (defaults to undefined with no checkbox rendered)
17076  * @constructor
17077  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17078  */
17079 Roo.tree.TreeNode = function(attributes){
17080     attributes = attributes || {};
17081     if(typeof attributes == "string"){
17082         attributes = {text: attributes};
17083     }
17084     this.childrenRendered = false;
17085     this.rendered = false;
17086     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17087     this.expanded = attributes.expanded === true;
17088     this.isTarget = attributes.isTarget !== false;
17089     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17090     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17091
17092     /**
17093      * Read-only. The text for this node. To change it use setText().
17094      * @type String
17095      */
17096     this.text = attributes.text;
17097     /**
17098      * True if this node is disabled.
17099      * @type Boolean
17100      */
17101     this.disabled = attributes.disabled === true;
17102
17103     this.addEvents({
17104         /**
17105         * @event textchange
17106         * Fires when the text for this node is changed
17107         * @param {Node} this This node
17108         * @param {String} text The new text
17109         * @param {String} oldText The old text
17110         */
17111         "textchange" : true,
17112         /**
17113         * @event beforeexpand
17114         * Fires before this node is expanded, return false to cancel.
17115         * @param {Node} this This node
17116         * @param {Boolean} deep
17117         * @param {Boolean} anim
17118         */
17119         "beforeexpand" : true,
17120         /**
17121         * @event beforecollapse
17122         * Fires before this node is collapsed, return false to cancel.
17123         * @param {Node} this This node
17124         * @param {Boolean} deep
17125         * @param {Boolean} anim
17126         */
17127         "beforecollapse" : true,
17128         /**
17129         * @event expand
17130         * Fires when this node is expanded
17131         * @param {Node} this This node
17132         */
17133         "expand" : true,
17134         /**
17135         * @event disabledchange
17136         * Fires when the disabled status of this node changes
17137         * @param {Node} this This node
17138         * @param {Boolean} disabled
17139         */
17140         "disabledchange" : true,
17141         /**
17142         * @event collapse
17143         * Fires when this node is collapsed
17144         * @param {Node} this This node
17145         */
17146         "collapse" : true,
17147         /**
17148         * @event beforeclick
17149         * Fires before click processing. Return false to cancel the default action.
17150         * @param {Node} this This node
17151         * @param {Roo.EventObject} e The event object
17152         */
17153         "beforeclick":true,
17154         /**
17155         * @event checkchange
17156         * Fires when a node with a checkbox's checked property changes
17157         * @param {Node} this This node
17158         * @param {Boolean} checked
17159         */
17160         "checkchange":true,
17161         /**
17162         * @event click
17163         * Fires when this node is clicked
17164         * @param {Node} this This node
17165         * @param {Roo.EventObject} e The event object
17166         */
17167         "click":true,
17168         /**
17169         * @event dblclick
17170         * Fires when this node is double clicked
17171         * @param {Node} this This node
17172         * @param {Roo.EventObject} e The event object
17173         */
17174         "dblclick":true,
17175         /**
17176         * @event contextmenu
17177         * Fires when this node is right clicked
17178         * @param {Node} this This node
17179         * @param {Roo.EventObject} e The event object
17180         */
17181         "contextmenu":true,
17182         /**
17183         * @event beforechildrenrendered
17184         * Fires right before the child nodes for this node are rendered
17185         * @param {Node} this This node
17186         */
17187         "beforechildrenrendered":true
17188     });
17189
17190     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17191
17192     /**
17193      * Read-only. The UI for this node
17194      * @type TreeNodeUI
17195      */
17196     this.ui = new uiClass(this);
17197 };
17198 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17199     preventHScroll: true,
17200     /**
17201      * Returns true if this node is expanded
17202      * @return {Boolean}
17203      */
17204     isExpanded : function(){
17205         return this.expanded;
17206     },
17207
17208     /**
17209      * Returns the UI object for this node
17210      * @return {TreeNodeUI}
17211      */
17212     getUI : function(){
17213         return this.ui;
17214     },
17215
17216     // private override
17217     setFirstChild : function(node){
17218         var of = this.firstChild;
17219         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17220         if(this.childrenRendered && of && node != of){
17221             of.renderIndent(true, true);
17222         }
17223         if(this.rendered){
17224             this.renderIndent(true, true);
17225         }
17226     },
17227
17228     // private override
17229     setLastChild : function(node){
17230         var ol = this.lastChild;
17231         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17232         if(this.childrenRendered && ol && node != ol){
17233             ol.renderIndent(true, true);
17234         }
17235         if(this.rendered){
17236             this.renderIndent(true, true);
17237         }
17238     },
17239
17240     // these methods are overridden to provide lazy rendering support
17241     // private override
17242     appendChild : function(){
17243         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17244         if(node && this.childrenRendered){
17245             node.render();
17246         }
17247         this.ui.updateExpandIcon();
17248         return node;
17249     },
17250
17251     // private override
17252     removeChild : function(node){
17253         this.ownerTree.getSelectionModel().unselect(node);
17254         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17255         // if it's been rendered remove dom node
17256         if(this.childrenRendered){
17257             node.ui.remove();
17258         }
17259         if(this.childNodes.length < 1){
17260             this.collapse(false, false);
17261         }else{
17262             this.ui.updateExpandIcon();
17263         }
17264         if(!this.firstChild) {
17265             this.childrenRendered = false;
17266         }
17267         return node;
17268     },
17269
17270     // private override
17271     insertBefore : function(node, refNode){
17272         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17273         if(newNode && refNode && this.childrenRendered){
17274             node.render();
17275         }
17276         this.ui.updateExpandIcon();
17277         return newNode;
17278     },
17279
17280     /**
17281      * Sets the text for this node
17282      * @param {String} text
17283      */
17284     setText : function(text){
17285         var oldText = this.text;
17286         this.text = text;
17287         this.attributes.text = text;
17288         if(this.rendered){ // event without subscribing
17289             this.ui.onTextChange(this, text, oldText);
17290         }
17291         this.fireEvent("textchange", this, text, oldText);
17292     },
17293
17294     /**
17295      * Triggers selection of this node
17296      */
17297     select : function(){
17298         this.getOwnerTree().getSelectionModel().select(this);
17299     },
17300
17301     /**
17302      * Triggers deselection of this node
17303      */
17304     unselect : function(){
17305         this.getOwnerTree().getSelectionModel().unselect(this);
17306     },
17307
17308     /**
17309      * Returns true if this node is selected
17310      * @return {Boolean}
17311      */
17312     isSelected : function(){
17313         return this.getOwnerTree().getSelectionModel().isSelected(this);
17314     },
17315
17316     /**
17317      * Expand this node.
17318      * @param {Boolean} deep (optional) True to expand all children as well
17319      * @param {Boolean} anim (optional) false to cancel the default animation
17320      * @param {Function} callback (optional) A callback to be called when
17321      * expanding this node completes (does not wait for deep expand to complete).
17322      * Called with 1 parameter, this node.
17323      */
17324     expand : function(deep, anim, callback){
17325         if(!this.expanded){
17326             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17327                 return;
17328             }
17329             if(!this.childrenRendered){
17330                 this.renderChildren();
17331             }
17332             this.expanded = true;
17333             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17334                 this.ui.animExpand(function(){
17335                     this.fireEvent("expand", this);
17336                     if(typeof callback == "function"){
17337                         callback(this);
17338                     }
17339                     if(deep === true){
17340                         this.expandChildNodes(true);
17341                     }
17342                 }.createDelegate(this));
17343                 return;
17344             }else{
17345                 this.ui.expand();
17346                 this.fireEvent("expand", this);
17347                 if(typeof callback == "function"){
17348                     callback(this);
17349                 }
17350             }
17351         }else{
17352            if(typeof callback == "function"){
17353                callback(this);
17354            }
17355         }
17356         if(deep === true){
17357             this.expandChildNodes(true);
17358         }
17359     },
17360
17361     isHiddenRoot : function(){
17362         return this.isRoot && !this.getOwnerTree().rootVisible;
17363     },
17364
17365     /**
17366      * Collapse this node.
17367      * @param {Boolean} deep (optional) True to collapse all children as well
17368      * @param {Boolean} anim (optional) false to cancel the default animation
17369      */
17370     collapse : function(deep, anim){
17371         if(this.expanded && !this.isHiddenRoot()){
17372             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17373                 return;
17374             }
17375             this.expanded = false;
17376             if((this.getOwnerTree().animate && anim !== false) || anim){
17377                 this.ui.animCollapse(function(){
17378                     this.fireEvent("collapse", this);
17379                     if(deep === true){
17380                         this.collapseChildNodes(true);
17381                     }
17382                 }.createDelegate(this));
17383                 return;
17384             }else{
17385                 this.ui.collapse();
17386                 this.fireEvent("collapse", this);
17387             }
17388         }
17389         if(deep === true){
17390             var cs = this.childNodes;
17391             for(var i = 0, len = cs.length; i < len; i++) {
17392                 cs[i].collapse(true, false);
17393             }
17394         }
17395     },
17396
17397     // private
17398     delayedExpand : function(delay){
17399         if(!this.expandProcId){
17400             this.expandProcId = this.expand.defer(delay, this);
17401         }
17402     },
17403
17404     // private
17405     cancelExpand : function(){
17406         if(this.expandProcId){
17407             clearTimeout(this.expandProcId);
17408         }
17409         this.expandProcId = false;
17410     },
17411
17412     /**
17413      * Toggles expanded/collapsed state of the node
17414      */
17415     toggle : function(){
17416         if(this.expanded){
17417             this.collapse();
17418         }else{
17419             this.expand();
17420         }
17421     },
17422
17423     /**
17424      * Ensures all parent nodes are expanded
17425      */
17426     ensureVisible : function(callback){
17427         var tree = this.getOwnerTree();
17428         tree.expandPath(this.parentNode.getPath(), false, function(){
17429             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17430             Roo.callback(callback);
17431         }.createDelegate(this));
17432     },
17433
17434     /**
17435      * Expand all child nodes
17436      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17437      */
17438     expandChildNodes : function(deep){
17439         var cs = this.childNodes;
17440         for(var i = 0, len = cs.length; i < len; i++) {
17441                 cs[i].expand(deep);
17442         }
17443     },
17444
17445     /**
17446      * Collapse all child nodes
17447      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17448      */
17449     collapseChildNodes : function(deep){
17450         var cs = this.childNodes;
17451         for(var i = 0, len = cs.length; i < len; i++) {
17452                 cs[i].collapse(deep);
17453         }
17454     },
17455
17456     /**
17457      * Disables this node
17458      */
17459     disable : function(){
17460         this.disabled = true;
17461         this.unselect();
17462         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17463             this.ui.onDisableChange(this, true);
17464         }
17465         this.fireEvent("disabledchange", this, true);
17466     },
17467
17468     /**
17469      * Enables this node
17470      */
17471     enable : function(){
17472         this.disabled = false;
17473         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17474             this.ui.onDisableChange(this, false);
17475         }
17476         this.fireEvent("disabledchange", this, false);
17477     },
17478
17479     // private
17480     renderChildren : function(suppressEvent){
17481         if(suppressEvent !== false){
17482             this.fireEvent("beforechildrenrendered", this);
17483         }
17484         var cs = this.childNodes;
17485         for(var i = 0, len = cs.length; i < len; i++){
17486             cs[i].render(true);
17487         }
17488         this.childrenRendered = true;
17489     },
17490
17491     // private
17492     sort : function(fn, scope){
17493         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17494         if(this.childrenRendered){
17495             var cs = this.childNodes;
17496             for(var i = 0, len = cs.length; i < len; i++){
17497                 cs[i].render(true);
17498             }
17499         }
17500     },
17501
17502     // private
17503     render : function(bulkRender){
17504         this.ui.render(bulkRender);
17505         if(!this.rendered){
17506             this.rendered = true;
17507             if(this.expanded){
17508                 this.expanded = false;
17509                 this.expand(false, false);
17510             }
17511         }
17512     },
17513
17514     // private
17515     renderIndent : function(deep, refresh){
17516         if(refresh){
17517             this.ui.childIndent = null;
17518         }
17519         this.ui.renderIndent();
17520         if(deep === true && this.childrenRendered){
17521             var cs = this.childNodes;
17522             for(var i = 0, len = cs.length; i < len; i++){
17523                 cs[i].renderIndent(true, refresh);
17524             }
17525         }
17526     }
17527 });/*
17528  * Based on:
17529  * Ext JS Library 1.1.1
17530  * Copyright(c) 2006-2007, Ext JS, LLC.
17531  *
17532  * Originally Released Under LGPL - original licence link has changed is not relivant.
17533  *
17534  * Fork - LGPL
17535  * <script type="text/javascript">
17536  */
17537  
17538 /**
17539  * @class Roo.tree.AsyncTreeNode
17540  * @extends Roo.tree.TreeNode
17541  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17542  * @constructor
17543  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17544  */
17545  Roo.tree.AsyncTreeNode = function(config){
17546     this.loaded = false;
17547     this.loading = false;
17548     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17549     /**
17550     * @event beforeload
17551     * Fires before this node is loaded, return false to cancel
17552     * @param {Node} this This node
17553     */
17554     this.addEvents({'beforeload':true, 'load': true});
17555     /**
17556     * @event load
17557     * Fires when this node is loaded
17558     * @param {Node} this This node
17559     */
17560     /**
17561      * The loader used by this node (defaults to using the tree's defined loader)
17562      * @type TreeLoader
17563      * @property loader
17564      */
17565 };
17566 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17567     expand : function(deep, anim, callback){
17568         if(this.loading){ // if an async load is already running, waiting til it's done
17569             var timer;
17570             var f = function(){
17571                 if(!this.loading){ // done loading
17572                     clearInterval(timer);
17573                     this.expand(deep, anim, callback);
17574                 }
17575             }.createDelegate(this);
17576             timer = setInterval(f, 200);
17577             return;
17578         }
17579         if(!this.loaded){
17580             if(this.fireEvent("beforeload", this) === false){
17581                 return;
17582             }
17583             this.loading = true;
17584             this.ui.beforeLoad(this);
17585             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17586             if(loader){
17587                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17588                 return;
17589             }
17590         }
17591         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17592     },
17593     
17594     /**
17595      * Returns true if this node is currently loading
17596      * @return {Boolean}
17597      */
17598     isLoading : function(){
17599         return this.loading;  
17600     },
17601     
17602     loadComplete : function(deep, anim, callback){
17603         this.loading = false;
17604         this.loaded = true;
17605         this.ui.afterLoad(this);
17606         this.fireEvent("load", this);
17607         this.expand(deep, anim, callback);
17608     },
17609     
17610     /**
17611      * Returns true if this node has been loaded
17612      * @return {Boolean}
17613      */
17614     isLoaded : function(){
17615         return this.loaded;
17616     },
17617     
17618     hasChildNodes : function(){
17619         if(!this.isLeaf() && !this.loaded){
17620             return true;
17621         }else{
17622             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17623         }
17624     },
17625
17626     /**
17627      * Trigger a reload for this node
17628      * @param {Function} callback
17629      */
17630     reload : function(callback){
17631         this.collapse(false, false);
17632         while(this.firstChild){
17633             this.removeChild(this.firstChild);
17634         }
17635         this.childrenRendered = false;
17636         this.loaded = false;
17637         if(this.isHiddenRoot()){
17638             this.expanded = false;
17639         }
17640         this.expand(false, false, callback);
17641     }
17642 });/*
17643  * Based on:
17644  * Ext JS Library 1.1.1
17645  * Copyright(c) 2006-2007, Ext JS, LLC.
17646  *
17647  * Originally Released Under LGPL - original licence link has changed is not relivant.
17648  *
17649  * Fork - LGPL
17650  * <script type="text/javascript">
17651  */
17652  
17653 /**
17654  * @class Roo.tree.TreeNodeUI
17655  * @constructor
17656  * @param {Object} node The node to render
17657  * The TreeNode UI implementation is separate from the
17658  * tree implementation. Unless you are customizing the tree UI,
17659  * you should never have to use this directly.
17660  */
17661 Roo.tree.TreeNodeUI = function(node){
17662     this.node = node;
17663     this.rendered = false;
17664     this.animating = false;
17665     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17666 };
17667
17668 Roo.tree.TreeNodeUI.prototype = {
17669     removeChild : function(node){
17670         if(this.rendered){
17671             this.ctNode.removeChild(node.ui.getEl());
17672         }
17673     },
17674
17675     beforeLoad : function(){
17676          this.addClass("x-tree-node-loading");
17677     },
17678
17679     afterLoad : function(){
17680          this.removeClass("x-tree-node-loading");
17681     },
17682
17683     onTextChange : function(node, text, oldText){
17684         if(this.rendered){
17685             this.textNode.innerHTML = text;
17686         }
17687     },
17688
17689     onDisableChange : function(node, state){
17690         this.disabled = state;
17691         if(state){
17692             this.addClass("x-tree-node-disabled");
17693         }else{
17694             this.removeClass("x-tree-node-disabled");
17695         }
17696     },
17697
17698     onSelectedChange : function(state){
17699         if(state){
17700             this.focus();
17701             this.addClass("x-tree-selected");
17702         }else{
17703             //this.blur();
17704             this.removeClass("x-tree-selected");
17705         }
17706     },
17707
17708     onMove : function(tree, node, oldParent, newParent, index, refNode){
17709         this.childIndent = null;
17710         if(this.rendered){
17711             var targetNode = newParent.ui.getContainer();
17712             if(!targetNode){//target not rendered
17713                 this.holder = document.createElement("div");
17714                 this.holder.appendChild(this.wrap);
17715                 return;
17716             }
17717             var insertBefore = refNode ? refNode.ui.getEl() : null;
17718             if(insertBefore){
17719                 targetNode.insertBefore(this.wrap, insertBefore);
17720             }else{
17721                 targetNode.appendChild(this.wrap);
17722             }
17723             this.node.renderIndent(true);
17724         }
17725     },
17726
17727     addClass : function(cls){
17728         if(this.elNode){
17729             Roo.fly(this.elNode).addClass(cls);
17730         }
17731     },
17732
17733     removeClass : function(cls){
17734         if(this.elNode){
17735             Roo.fly(this.elNode).removeClass(cls);
17736         }
17737     },
17738
17739     remove : function(){
17740         if(this.rendered){
17741             this.holder = document.createElement("div");
17742             this.holder.appendChild(this.wrap);
17743         }
17744     },
17745
17746     fireEvent : function(){
17747         return this.node.fireEvent.apply(this.node, arguments);
17748     },
17749
17750     initEvents : function(){
17751         this.node.on("move", this.onMove, this);
17752         var E = Roo.EventManager;
17753         var a = this.anchor;
17754
17755         var el = Roo.fly(a, '_treeui');
17756
17757         if(Roo.isOpera){ // opera render bug ignores the CSS
17758             el.setStyle("text-decoration", "none");
17759         }
17760
17761         el.on("click", this.onClick, this);
17762         el.on("dblclick", this.onDblClick, this);
17763
17764         if(this.checkbox){
17765             Roo.EventManager.on(this.checkbox,
17766                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
17767         }
17768
17769         el.on("contextmenu", this.onContextMenu, this);
17770
17771         var icon = Roo.fly(this.iconNode);
17772         icon.on("click", this.onClick, this);
17773         icon.on("dblclick", this.onDblClick, this);
17774         icon.on("contextmenu", this.onContextMenu, this);
17775         E.on(this.ecNode, "click", this.ecClick, this, true);
17776
17777         if(this.node.disabled){
17778             this.addClass("x-tree-node-disabled");
17779         }
17780         if(this.node.hidden){
17781             this.addClass("x-tree-node-disabled");
17782         }
17783         var ot = this.node.getOwnerTree();
17784         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
17785         if(dd && (!this.node.isRoot || ot.rootVisible)){
17786             Roo.dd.Registry.register(this.elNode, {
17787                 node: this.node,
17788                 handles: this.getDDHandles(),
17789                 isHandle: false
17790             });
17791         }
17792     },
17793
17794     getDDHandles : function(){
17795         return [this.iconNode, this.textNode];
17796     },
17797
17798     hide : function(){
17799         if(this.rendered){
17800             this.wrap.style.display = "none";
17801         }
17802     },
17803
17804     show : function(){
17805         if(this.rendered){
17806             this.wrap.style.display = "";
17807         }
17808     },
17809
17810     onContextMenu : function(e){
17811         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
17812             e.preventDefault();
17813             this.focus();
17814             this.fireEvent("contextmenu", this.node, e);
17815         }
17816     },
17817
17818     onClick : function(e){
17819         if(this.dropping){
17820             e.stopEvent();
17821             return;
17822         }
17823         if(this.fireEvent("beforeclick", this.node, e) !== false){
17824             if(!this.disabled && this.node.attributes.href){
17825                 this.fireEvent("click", this.node, e);
17826                 return;
17827             }
17828             e.preventDefault();
17829             if(this.disabled){
17830                 return;
17831             }
17832
17833             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
17834                 this.node.toggle();
17835             }
17836
17837             this.fireEvent("click", this.node, e);
17838         }else{
17839             e.stopEvent();
17840         }
17841     },
17842
17843     onDblClick : function(e){
17844         e.preventDefault();
17845         if(this.disabled){
17846             return;
17847         }
17848         if(this.checkbox){
17849             this.toggleCheck();
17850         }
17851         if(!this.animating && this.node.hasChildNodes()){
17852             this.node.toggle();
17853         }
17854         this.fireEvent("dblclick", this.node, e);
17855     },
17856
17857     onCheckChange : function(){
17858         var checked = this.checkbox.checked;
17859         this.node.attributes.checked = checked;
17860         this.fireEvent('checkchange', this.node, checked);
17861     },
17862
17863     ecClick : function(e){
17864         if(!this.animating && this.node.hasChildNodes()){
17865             this.node.toggle();
17866         }
17867     },
17868
17869     startDrop : function(){
17870         this.dropping = true;
17871     },
17872
17873     // delayed drop so the click event doesn't get fired on a drop
17874     endDrop : function(){
17875        setTimeout(function(){
17876            this.dropping = false;
17877        }.createDelegate(this), 50);
17878     },
17879
17880     expand : function(){
17881         this.updateExpandIcon();
17882         this.ctNode.style.display = "";
17883     },
17884
17885     focus : function(){
17886         if(!this.node.preventHScroll){
17887             try{this.anchor.focus();
17888             }catch(e){}
17889         }else if(!Roo.isIE){
17890             try{
17891                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
17892                 var l = noscroll.scrollLeft;
17893                 this.anchor.focus();
17894                 noscroll.scrollLeft = l;
17895             }catch(e){}
17896         }
17897     },
17898
17899     toggleCheck : function(value){
17900         var cb = this.checkbox;
17901         if(cb){
17902             cb.checked = (value === undefined ? !cb.checked : value);
17903         }
17904     },
17905
17906     blur : function(){
17907         try{
17908             this.anchor.blur();
17909         }catch(e){}
17910     },
17911
17912     animExpand : function(callback){
17913         var ct = Roo.get(this.ctNode);
17914         ct.stopFx();
17915         if(!this.node.hasChildNodes()){
17916             this.updateExpandIcon();
17917             this.ctNode.style.display = "";
17918             Roo.callback(callback);
17919             return;
17920         }
17921         this.animating = true;
17922         this.updateExpandIcon();
17923
17924         ct.slideIn('t', {
17925            callback : function(){
17926                this.animating = false;
17927                Roo.callback(callback);
17928             },
17929             scope: this,
17930             duration: this.node.ownerTree.duration || .25
17931         });
17932     },
17933
17934     highlight : function(){
17935         var tree = this.node.getOwnerTree();
17936         Roo.fly(this.wrap).highlight(
17937             tree.hlColor || "C3DAF9",
17938             {endColor: tree.hlBaseColor}
17939         );
17940     },
17941
17942     collapse : function(){
17943         this.updateExpandIcon();
17944         this.ctNode.style.display = "none";
17945     },
17946
17947     animCollapse : function(callback){
17948         var ct = Roo.get(this.ctNode);
17949         ct.enableDisplayMode('block');
17950         ct.stopFx();
17951
17952         this.animating = true;
17953         this.updateExpandIcon();
17954
17955         ct.slideOut('t', {
17956             callback : function(){
17957                this.animating = false;
17958                Roo.callback(callback);
17959             },
17960             scope: this,
17961             duration: this.node.ownerTree.duration || .25
17962         });
17963     },
17964
17965     getContainer : function(){
17966         return this.ctNode;
17967     },
17968
17969     getEl : function(){
17970         return this.wrap;
17971     },
17972
17973     appendDDGhost : function(ghostNode){
17974         ghostNode.appendChild(this.elNode.cloneNode(true));
17975     },
17976
17977     getDDRepairXY : function(){
17978         return Roo.lib.Dom.getXY(this.iconNode);
17979     },
17980
17981     onRender : function(){
17982         this.render();
17983     },
17984
17985     render : function(bulkRender){
17986         var n = this.node, a = n.attributes;
17987         var targetNode = n.parentNode ?
17988               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
17989
17990         if(!this.rendered){
17991             this.rendered = true;
17992
17993             this.renderElements(n, a, targetNode, bulkRender);
17994
17995             if(a.qtip){
17996                if(this.textNode.setAttributeNS){
17997                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
17998                    if(a.qtipTitle){
17999                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18000                    }
18001                }else{
18002                    this.textNode.setAttribute("ext:qtip", a.qtip);
18003                    if(a.qtipTitle){
18004                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18005                    }
18006                }
18007             }else if(a.qtipCfg){
18008                 a.qtipCfg.target = Roo.id(this.textNode);
18009                 Roo.QuickTips.register(a.qtipCfg);
18010             }
18011             this.initEvents();
18012             if(!this.node.expanded){
18013                 this.updateExpandIcon();
18014             }
18015         }else{
18016             if(bulkRender === true) {
18017                 targetNode.appendChild(this.wrap);
18018             }
18019         }
18020     },
18021
18022     renderElements : function(n, a, targetNode, bulkRender){
18023         // add some indent caching, this helps performance when rendering a large tree
18024         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18025         var t = n.getOwnerTree();
18026         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18027         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18028         var cb = typeof a.checked == 'boolean';
18029         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18030         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18031             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18032             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18033             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18034             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18035             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18036              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18037                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18038             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18039             "</li>"];
18040
18041         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18042             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18043                                 n.nextSibling.ui.getEl(), buf.join(""));
18044         }else{
18045             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18046         }
18047
18048         this.elNode = this.wrap.childNodes[0];
18049         this.ctNode = this.wrap.childNodes[1];
18050         var cs = this.elNode.childNodes;
18051         this.indentNode = cs[0];
18052         this.ecNode = cs[1];
18053         this.iconNode = cs[2];
18054         var index = 3;
18055         if(cb){
18056             this.checkbox = cs[3];
18057             index++;
18058         }
18059         this.anchor = cs[index];
18060         this.textNode = cs[index].firstChild;
18061     },
18062
18063     getAnchor : function(){
18064         return this.anchor;
18065     },
18066
18067     getTextEl : function(){
18068         return this.textNode;
18069     },
18070
18071     getIconEl : function(){
18072         return this.iconNode;
18073     },
18074
18075     isChecked : function(){
18076         return this.checkbox ? this.checkbox.checked : false;
18077     },
18078
18079     updateExpandIcon : function(){
18080         if(this.rendered){
18081             var n = this.node, c1, c2;
18082             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18083             var hasChild = n.hasChildNodes();
18084             if(hasChild){
18085                 if(n.expanded){
18086                     cls += "-minus";
18087                     c1 = "x-tree-node-collapsed";
18088                     c2 = "x-tree-node-expanded";
18089                 }else{
18090                     cls += "-plus";
18091                     c1 = "x-tree-node-expanded";
18092                     c2 = "x-tree-node-collapsed";
18093                 }
18094                 if(this.wasLeaf){
18095                     this.removeClass("x-tree-node-leaf");
18096                     this.wasLeaf = false;
18097                 }
18098                 if(this.c1 != c1 || this.c2 != c2){
18099                     Roo.fly(this.elNode).replaceClass(c1, c2);
18100                     this.c1 = c1; this.c2 = c2;
18101                 }
18102             }else{
18103                 if(!this.wasLeaf){
18104                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18105                     delete this.c1;
18106                     delete this.c2;
18107                     this.wasLeaf = true;
18108                 }
18109             }
18110             var ecc = "x-tree-ec-icon "+cls;
18111             if(this.ecc != ecc){
18112                 this.ecNode.className = ecc;
18113                 this.ecc = ecc;
18114             }
18115         }
18116     },
18117
18118     getChildIndent : function(){
18119         if(!this.childIndent){
18120             var buf = [];
18121             var p = this.node;
18122             while(p){
18123                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18124                     if(!p.isLast()) {
18125                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18126                     } else {
18127                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18128                     }
18129                 }
18130                 p = p.parentNode;
18131             }
18132             this.childIndent = buf.join("");
18133         }
18134         return this.childIndent;
18135     },
18136
18137     renderIndent : function(){
18138         if(this.rendered){
18139             var indent = "";
18140             var p = this.node.parentNode;
18141             if(p){
18142                 indent = p.ui.getChildIndent();
18143             }
18144             if(this.indentMarkup != indent){ // don't rerender if not required
18145                 this.indentNode.innerHTML = indent;
18146                 this.indentMarkup = indent;
18147             }
18148             this.updateExpandIcon();
18149         }
18150     }
18151 };
18152
18153 Roo.tree.RootTreeNodeUI = function(){
18154     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18155 };
18156 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18157     render : function(){
18158         if(!this.rendered){
18159             var targetNode = this.node.ownerTree.innerCt.dom;
18160             this.node.expanded = true;
18161             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18162             this.wrap = this.ctNode = targetNode.firstChild;
18163         }
18164     },
18165     collapse : function(){
18166     },
18167     expand : function(){
18168     }
18169 });/*
18170  * Based on:
18171  * Ext JS Library 1.1.1
18172  * Copyright(c) 2006-2007, Ext JS, LLC.
18173  *
18174  * Originally Released Under LGPL - original licence link has changed is not relivant.
18175  *
18176  * Fork - LGPL
18177  * <script type="text/javascript">
18178  */
18179 /**
18180  * @class Roo.tree.TreeLoader
18181  * @extends Roo.util.Observable
18182  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18183  * nodes from a specified URL. The response must be a javascript Array definition
18184  * who's elements are node definition objects. eg:
18185  * <pre><code>
18186    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
18187     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
18188 </code></pre>
18189  * <br><br>
18190  * A server request is sent, and child nodes are loaded only when a node is expanded.
18191  * The loading node's id is passed to the server under the parameter name "node" to
18192  * enable the server to produce the correct child nodes.
18193  * <br><br>
18194  * To pass extra parameters, an event handler may be attached to the "beforeload"
18195  * event, and the parameters specified in the TreeLoader's baseParams property:
18196  * <pre><code>
18197     myTreeLoader.on("beforeload", function(treeLoader, node) {
18198         this.baseParams.category = node.attributes.category;
18199     }, this);
18200 </code></pre><
18201  * This would pass an HTTP parameter called "category" to the server containing
18202  * the value of the Node's "category" attribute.
18203  * @constructor
18204  * Creates a new Treeloader.
18205  * @param {Object} config A config object containing config properties.
18206  */
18207 Roo.tree.TreeLoader = function(config){
18208     this.baseParams = {};
18209     this.requestMethod = "POST";
18210     Roo.apply(this, config);
18211
18212     this.addEvents({
18213     
18214         /**
18215          * @event beforeload
18216          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18217          * @param {Object} This TreeLoader object.
18218          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18219          * @param {Object} callback The callback function specified in the {@link #load} call.
18220          */
18221         beforeload : true,
18222         /**
18223          * @event load
18224          * Fires when the node has been successfuly loaded.
18225          * @param {Object} This TreeLoader object.
18226          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18227          * @param {Object} response The response object containing the data from the server.
18228          */
18229         load : true,
18230         /**
18231          * @event loadexception
18232          * Fires if the network request failed.
18233          * @param {Object} This TreeLoader object.
18234          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18235          * @param {Object} response The response object containing the data from the server.
18236          */
18237         loadexception : true,
18238         /**
18239          * @event create
18240          * Fires before a node is created, enabling you to return custom Node types 
18241          * @param {Object} This TreeLoader object.
18242          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18243          */
18244         create : true
18245     });
18246
18247     Roo.tree.TreeLoader.superclass.constructor.call(this);
18248 };
18249
18250 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18251     /**
18252     * @cfg {String} dataUrl The URL from which to request a Json string which
18253     * specifies an array of node definition object representing the child nodes
18254     * to be loaded.
18255     */
18256     /**
18257     * @cfg {Object} baseParams (optional) An object containing properties which
18258     * specify HTTP parameters to be passed to each request for child nodes.
18259     */
18260     /**
18261     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18262     * created by this loader. If the attributes sent by the server have an attribute in this object,
18263     * they take priority.
18264     */
18265     /**
18266     * @cfg {Object} uiProviders (optional) An object containing properties which
18267     * 
18268     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
18269     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18270     * <i>uiProvider</i> attribute of a returned child node is a string rather
18271     * than a reference to a TreeNodeUI implementation, this that string value
18272     * is used as a property name in the uiProviders object. You can define the provider named
18273     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18274     */
18275     uiProviders : {},
18276
18277     /**
18278     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18279     * child nodes before loading.
18280     */
18281     clearOnLoad : true,
18282
18283     /**
18284     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18285     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18286     * Grid query { data : [ .....] }
18287     */
18288     
18289     root : false,
18290      /**
18291     * @cfg {String} queryParam (optional) 
18292     * Name of the query as it will be passed on the querystring (defaults to 'node')
18293     * eg. the request will be ?node=[id]
18294     */
18295     
18296     
18297     queryParam: false,
18298     
18299     /**
18300      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18301      * This is called automatically when a node is expanded, but may be used to reload
18302      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18303      * @param {Roo.tree.TreeNode} node
18304      * @param {Function} callback
18305      */
18306     load : function(node, callback){
18307         if(this.clearOnLoad){
18308             while(node.firstChild){
18309                 node.removeChild(node.firstChild);
18310             }
18311         }
18312         if(node.attributes.children){ // preloaded json children
18313             var cs = node.attributes.children;
18314             for(var i = 0, len = cs.length; i < len; i++){
18315                 node.appendChild(this.createNode(cs[i]));
18316             }
18317             if(typeof callback == "function"){
18318                 callback();
18319             }
18320         }else if(this.dataUrl){
18321             this.requestData(node, callback);
18322         }
18323     },
18324
18325     getParams: function(node){
18326         var buf = [], bp = this.baseParams;
18327         for(var key in bp){
18328             if(typeof bp[key] != "function"){
18329                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18330             }
18331         }
18332         var n = this.queryParam === false ? 'node' : this.queryParam;
18333         buf.push(n + "=", encodeURIComponent(node.id));
18334         return buf.join("");
18335     },
18336
18337     requestData : function(node, callback){
18338         if(this.fireEvent("beforeload", this, node, callback) !== false){
18339             this.transId = Roo.Ajax.request({
18340                 method:this.requestMethod,
18341                 url: this.dataUrl||this.url,
18342                 success: this.handleResponse,
18343                 failure: this.handleFailure,
18344                 scope: this,
18345                 argument: {callback: callback, node: node},
18346                 params: this.getParams(node)
18347             });
18348         }else{
18349             // if the load is cancelled, make sure we notify
18350             // the node that we are done
18351             if(typeof callback == "function"){
18352                 callback();
18353             }
18354         }
18355     },
18356
18357     isLoading : function(){
18358         return this.transId ? true : false;
18359     },
18360
18361     abort : function(){
18362         if(this.isLoading()){
18363             Roo.Ajax.abort(this.transId);
18364         }
18365     },
18366
18367     // private
18368     createNode : function(attr){
18369         // apply baseAttrs, nice idea Corey!
18370         if(this.baseAttrs){
18371             Roo.applyIf(attr, this.baseAttrs);
18372         }
18373         if(this.applyLoader !== false){
18374             attr.loader = this;
18375         }
18376         // uiProvider = depreciated..
18377         
18378         if(typeof(attr.uiProvider) == 'string'){
18379            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18380                 /**  eval:var:attr */ eval(attr.uiProvider);
18381         }
18382         if(typeof(this.uiProviders['default']) != 'undefined') {
18383             attr.uiProvider = this.uiProviders['default'];
18384         }
18385         
18386         this.fireEvent('create', this, attr);
18387         
18388         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18389         return(attr.leaf ?
18390                         new Roo.tree.TreeNode(attr) :
18391                         new Roo.tree.AsyncTreeNode(attr));
18392     },
18393
18394     processResponse : function(response, node, callback){
18395         var json = response.responseText;
18396         try {
18397             
18398             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
18399             if (this.root !== false) {
18400                 o = o[this.root];
18401             }
18402             
18403             for(var i = 0, len = o.length; i < len; i++){
18404                 var n = this.createNode(o[i]);
18405                 if(n){
18406                     node.appendChild(n);
18407                 }
18408             }
18409             if(typeof callback == "function"){
18410                 callback(this, node);
18411             }
18412         }catch(e){
18413             this.handleFailure(response);
18414         }
18415     },
18416
18417     handleResponse : function(response){
18418         this.transId = false;
18419         var a = response.argument;
18420         this.processResponse(response, a.node, a.callback);
18421         this.fireEvent("load", this, a.node, response);
18422     },
18423
18424     handleFailure : function(response){
18425         this.transId = false;
18426         var a = response.argument;
18427         this.fireEvent("loadexception", this, a.node, response);
18428         if(typeof a.callback == "function"){
18429             a.callback(this, a.node);
18430         }
18431     }
18432 });/*
18433  * Based on:
18434  * Ext JS Library 1.1.1
18435  * Copyright(c) 2006-2007, Ext JS, LLC.
18436  *
18437  * Originally Released Under LGPL - original licence link has changed is not relivant.
18438  *
18439  * Fork - LGPL
18440  * <script type="text/javascript">
18441  */
18442
18443 /**
18444 * @class Roo.tree.TreeFilter
18445 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18446 * @param {TreePanel} tree
18447 * @param {Object} config (optional)
18448  */
18449 Roo.tree.TreeFilter = function(tree, config){
18450     this.tree = tree;
18451     this.filtered = {};
18452     Roo.apply(this, config);
18453 };
18454
18455 Roo.tree.TreeFilter.prototype = {
18456     clearBlank:false,
18457     reverse:false,
18458     autoClear:false,
18459     remove:false,
18460
18461      /**
18462      * Filter the data by a specific attribute.
18463      * @param {String/RegExp} value Either string that the attribute value
18464      * should start with or a RegExp to test against the attribute
18465      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18466      * @param {TreeNode} startNode (optional) The node to start the filter at.
18467      */
18468     filter : function(value, attr, startNode){
18469         attr = attr || "text";
18470         var f;
18471         if(typeof value == "string"){
18472             var vlen = value.length;
18473             // auto clear empty filter
18474             if(vlen == 0 && this.clearBlank){
18475                 this.clear();
18476                 return;
18477             }
18478             value = value.toLowerCase();
18479             f = function(n){
18480                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18481             };
18482         }else if(value.exec){ // regex?
18483             f = function(n){
18484                 return value.test(n.attributes[attr]);
18485             };
18486         }else{
18487             throw 'Illegal filter type, must be string or regex';
18488         }
18489         this.filterBy(f, null, startNode);
18490         },
18491
18492     /**
18493      * Filter by a function. The passed function will be called with each
18494      * node in the tree (or from the startNode). If the function returns true, the node is kept
18495      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18496      * @param {Function} fn The filter function
18497      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18498      */
18499     filterBy : function(fn, scope, startNode){
18500         startNode = startNode || this.tree.root;
18501         if(this.autoClear){
18502             this.clear();
18503         }
18504         var af = this.filtered, rv = this.reverse;
18505         var f = function(n){
18506             if(n == startNode){
18507                 return true;
18508             }
18509             if(af[n.id]){
18510                 return false;
18511             }
18512             var m = fn.call(scope || n, n);
18513             if(!m || rv){
18514                 af[n.id] = n;
18515                 n.ui.hide();
18516                 return false;
18517             }
18518             return true;
18519         };
18520         startNode.cascade(f);
18521         if(this.remove){
18522            for(var id in af){
18523                if(typeof id != "function"){
18524                    var n = af[id];
18525                    if(n && n.parentNode){
18526                        n.parentNode.removeChild(n);
18527                    }
18528                }
18529            }
18530         }
18531     },
18532
18533     /**
18534      * Clears the current filter. Note: with the "remove" option
18535      * set a filter cannot be cleared.
18536      */
18537     clear : function(){
18538         var t = this.tree;
18539         var af = this.filtered;
18540         for(var id in af){
18541             if(typeof id != "function"){
18542                 var n = af[id];
18543                 if(n){
18544                     n.ui.show();
18545                 }
18546             }
18547         }
18548         this.filtered = {};
18549     }
18550 };
18551 /*
18552  * Based on:
18553  * Ext JS Library 1.1.1
18554  * Copyright(c) 2006-2007, Ext JS, LLC.
18555  *
18556  * Originally Released Under LGPL - original licence link has changed is not relivant.
18557  *
18558  * Fork - LGPL
18559  * <script type="text/javascript">
18560  */
18561  
18562
18563 /**
18564  * @class Roo.tree.TreeSorter
18565  * Provides sorting of nodes in a TreePanel
18566  * 
18567  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18568  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18569  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18570  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18571  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18572  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18573  * @constructor
18574  * @param {TreePanel} tree
18575  * @param {Object} config
18576  */
18577 Roo.tree.TreeSorter = function(tree, config){
18578     Roo.apply(this, config);
18579     tree.on("beforechildrenrendered", this.doSort, this);
18580     tree.on("append", this.updateSort, this);
18581     tree.on("insert", this.updateSort, this);
18582     
18583     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18584     var p = this.property || "text";
18585     var sortType = this.sortType;
18586     var fs = this.folderSort;
18587     var cs = this.caseSensitive === true;
18588     var leafAttr = this.leafAttr || 'leaf';
18589
18590     this.sortFn = function(n1, n2){
18591         if(fs){
18592             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18593                 return 1;
18594             }
18595             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18596                 return -1;
18597             }
18598         }
18599         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18600         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18601         if(v1 < v2){
18602                         return dsc ? +1 : -1;
18603                 }else if(v1 > v2){
18604                         return dsc ? -1 : +1;
18605         }else{
18606                 return 0;
18607         }
18608     };
18609 };
18610
18611 Roo.tree.TreeSorter.prototype = {
18612     doSort : function(node){
18613         node.sort(this.sortFn);
18614     },
18615     
18616     compareNodes : function(n1, n2){
18617         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18618     },
18619     
18620     updateSort : function(tree, node){
18621         if(node.childrenRendered){
18622             this.doSort.defer(1, this, [node]);
18623         }
18624     }
18625 };/*
18626  * Based on:
18627  * Ext JS Library 1.1.1
18628  * Copyright(c) 2006-2007, Ext JS, LLC.
18629  *
18630  * Originally Released Under LGPL - original licence link has changed is not relivant.
18631  *
18632  * Fork - LGPL
18633  * <script type="text/javascript">
18634  */
18635
18636 if(Roo.dd.DropZone){
18637     
18638 Roo.tree.TreeDropZone = function(tree, config){
18639     this.allowParentInsert = false;
18640     this.allowContainerDrop = false;
18641     this.appendOnly = false;
18642     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18643     this.tree = tree;
18644     this.lastInsertClass = "x-tree-no-status";
18645     this.dragOverData = {};
18646 };
18647
18648 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18649     ddGroup : "TreeDD",
18650     
18651     expandDelay : 1000,
18652     
18653     expandNode : function(node){
18654         if(node.hasChildNodes() && !node.isExpanded()){
18655             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18656         }
18657     },
18658     
18659     queueExpand : function(node){
18660         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18661     },
18662     
18663     cancelExpand : function(){
18664         if(this.expandProcId){
18665             clearTimeout(this.expandProcId);
18666             this.expandProcId = false;
18667         }
18668     },
18669     
18670     isValidDropPoint : function(n, pt, dd, e, data){
18671         if(!n || !data){ return false; }
18672         var targetNode = n.node;
18673         var dropNode = data.node;
18674         // default drop rules
18675         if(!(targetNode && targetNode.isTarget && pt)){
18676             return false;
18677         }
18678         if(pt == "append" && targetNode.allowChildren === false){
18679             return false;
18680         }
18681         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18682             return false;
18683         }
18684         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18685             return false;
18686         }
18687         // reuse the object
18688         var overEvent = this.dragOverData;
18689         overEvent.tree = this.tree;
18690         overEvent.target = targetNode;
18691         overEvent.data = data;
18692         overEvent.point = pt;
18693         overEvent.source = dd;
18694         overEvent.rawEvent = e;
18695         overEvent.dropNode = dropNode;
18696         overEvent.cancel = false;  
18697         var result = this.tree.fireEvent("nodedragover", overEvent);
18698         return overEvent.cancel === false && result !== false;
18699     },
18700     
18701     getDropPoint : function(e, n, dd){
18702         var tn = n.node;
18703         if(tn.isRoot){
18704             return tn.allowChildren !== false ? "append" : false; // always append for root
18705         }
18706         var dragEl = n.ddel;
18707         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18708         var y = Roo.lib.Event.getPageY(e);
18709         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18710         
18711         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18712         var noAppend = tn.allowChildren === false;
18713         if(this.appendOnly || tn.parentNode.allowChildren === false){
18714             return noAppend ? false : "append";
18715         }
18716         var noBelow = false;
18717         if(!this.allowParentInsert){
18718             noBelow = tn.hasChildNodes() && tn.isExpanded();
18719         }
18720         var q = (b - t) / (noAppend ? 2 : 3);
18721         if(y >= t && y < (t + q)){
18722             return "above";
18723         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
18724             return "below";
18725         }else{
18726             return "append";
18727         }
18728     },
18729     
18730     onNodeEnter : function(n, dd, e, data){
18731         this.cancelExpand();
18732     },
18733     
18734     onNodeOver : function(n, dd, e, data){
18735         var pt = this.getDropPoint(e, n, dd);
18736         var node = n.node;
18737         
18738         // auto node expand check
18739         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
18740             this.queueExpand(node);
18741         }else if(pt != "append"){
18742             this.cancelExpand();
18743         }
18744         
18745         // set the insert point style on the target node
18746         var returnCls = this.dropNotAllowed;
18747         if(this.isValidDropPoint(n, pt, dd, e, data)){
18748            if(pt){
18749                var el = n.ddel;
18750                var cls;
18751                if(pt == "above"){
18752                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
18753                    cls = "x-tree-drag-insert-above";
18754                }else if(pt == "below"){
18755                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
18756                    cls = "x-tree-drag-insert-below";
18757                }else{
18758                    returnCls = "x-tree-drop-ok-append";
18759                    cls = "x-tree-drag-append";
18760                }
18761                if(this.lastInsertClass != cls){
18762                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
18763                    this.lastInsertClass = cls;
18764                }
18765            }
18766        }
18767        return returnCls;
18768     },
18769     
18770     onNodeOut : function(n, dd, e, data){
18771         this.cancelExpand();
18772         this.removeDropIndicators(n);
18773     },
18774     
18775     onNodeDrop : function(n, dd, e, data){
18776         var point = this.getDropPoint(e, n, dd);
18777         var targetNode = n.node;
18778         targetNode.ui.startDrop();
18779         if(!this.isValidDropPoint(n, point, dd, e, data)){
18780             targetNode.ui.endDrop();
18781             return false;
18782         }
18783         // first try to find the drop node
18784         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
18785         var dropEvent = {
18786             tree : this.tree,
18787             target: targetNode,
18788             data: data,
18789             point: point,
18790             source: dd,
18791             rawEvent: e,
18792             dropNode: dropNode,
18793             cancel: !dropNode   
18794         };
18795         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
18796         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
18797             targetNode.ui.endDrop();
18798             return false;
18799         }
18800         // allow target changing
18801         targetNode = dropEvent.target;
18802         if(point == "append" && !targetNode.isExpanded()){
18803             targetNode.expand(false, null, function(){
18804                 this.completeDrop(dropEvent);
18805             }.createDelegate(this));
18806         }else{
18807             this.completeDrop(dropEvent);
18808         }
18809         return true;
18810     },
18811     
18812     completeDrop : function(de){
18813         var ns = de.dropNode, p = de.point, t = de.target;
18814         if(!(ns instanceof Array)){
18815             ns = [ns];
18816         }
18817         var n;
18818         for(var i = 0, len = ns.length; i < len; i++){
18819             n = ns[i];
18820             if(p == "above"){
18821                 t.parentNode.insertBefore(n, t);
18822             }else if(p == "below"){
18823                 t.parentNode.insertBefore(n, t.nextSibling);
18824             }else{
18825                 t.appendChild(n);
18826             }
18827         }
18828         n.ui.focus();
18829         if(this.tree.hlDrop){
18830             n.ui.highlight();
18831         }
18832         t.ui.endDrop();
18833         this.tree.fireEvent("nodedrop", de);
18834     },
18835     
18836     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
18837         if(this.tree.hlDrop){
18838             dropNode.ui.focus();
18839             dropNode.ui.highlight();
18840         }
18841         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
18842     },
18843     
18844     getTree : function(){
18845         return this.tree;
18846     },
18847     
18848     removeDropIndicators : function(n){
18849         if(n && n.ddel){
18850             var el = n.ddel;
18851             Roo.fly(el).removeClass([
18852                     "x-tree-drag-insert-above",
18853                     "x-tree-drag-insert-below",
18854                     "x-tree-drag-append"]);
18855             this.lastInsertClass = "_noclass";
18856         }
18857     },
18858     
18859     beforeDragDrop : function(target, e, id){
18860         this.cancelExpand();
18861         return true;
18862     },
18863     
18864     afterRepair : function(data){
18865         if(data && Roo.enableFx){
18866             data.node.ui.highlight();
18867         }
18868         this.hideProxy();
18869     }    
18870 });
18871
18872 }
18873 /*
18874  * Based on:
18875  * Ext JS Library 1.1.1
18876  * Copyright(c) 2006-2007, Ext JS, LLC.
18877  *
18878  * Originally Released Under LGPL - original licence link has changed is not relivant.
18879  *
18880  * Fork - LGPL
18881  * <script type="text/javascript">
18882  */
18883  
18884
18885 if(Roo.dd.DragZone){
18886 Roo.tree.TreeDragZone = function(tree, config){
18887     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
18888     this.tree = tree;
18889 };
18890
18891 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
18892     ddGroup : "TreeDD",
18893     
18894     onBeforeDrag : function(data, e){
18895         var n = data.node;
18896         return n && n.draggable && !n.disabled;
18897     },
18898     
18899     onInitDrag : function(e){
18900         var data = this.dragData;
18901         this.tree.getSelectionModel().select(data.node);
18902         this.proxy.update("");
18903         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
18904         this.tree.fireEvent("startdrag", this.tree, data.node, e);
18905     },
18906     
18907     getRepairXY : function(e, data){
18908         return data.node.ui.getDDRepairXY();
18909     },
18910     
18911     onEndDrag : function(data, e){
18912         this.tree.fireEvent("enddrag", this.tree, data.node, e);
18913     },
18914     
18915     onValidDrop : function(dd, e, id){
18916         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
18917         this.hideProxy();
18918     },
18919     
18920     beforeInvalidDrop : function(e, id){
18921         // this scrolls the original position back into view
18922         var sm = this.tree.getSelectionModel();
18923         sm.clearSelections();
18924         sm.select(this.dragData.node);
18925     }
18926 });
18927 }/*
18928  * Based on:
18929  * Ext JS Library 1.1.1
18930  * Copyright(c) 2006-2007, Ext JS, LLC.
18931  *
18932  * Originally Released Under LGPL - original licence link has changed is not relivant.
18933  *
18934  * Fork - LGPL
18935  * <script type="text/javascript">
18936  */
18937 /**
18938  * @class Roo.tree.TreeEditor
18939  * @extends Roo.Editor
18940  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
18941  * as the editor field.
18942  * @constructor
18943  * @param {TreePanel} tree
18944  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
18945  */
18946 Roo.tree.TreeEditor = function(tree, config){
18947     config = config || {};
18948     var field = config.events ? config : new Roo.form.TextField(config);
18949     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
18950
18951     this.tree = tree;
18952
18953     tree.on('beforeclick', this.beforeNodeClick, this);
18954     tree.getTreeEl().on('mousedown', this.hide, this);
18955     this.on('complete', this.updateNode, this);
18956     this.on('beforestartedit', this.fitToTree, this);
18957     this.on('startedit', this.bindScroll, this, {delay:10});
18958     this.on('specialkey', this.onSpecialKey, this);
18959 };
18960
18961 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
18962     /**
18963      * @cfg {String} alignment
18964      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
18965      */
18966     alignment: "l-l",
18967     // inherit
18968     autoSize: false,
18969     /**
18970      * @cfg {Boolean} hideEl
18971      * True to hide the bound element while the editor is displayed (defaults to false)
18972      */
18973     hideEl : false,
18974     /**
18975      * @cfg {String} cls
18976      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
18977      */
18978     cls: "x-small-editor x-tree-editor",
18979     /**
18980      * @cfg {Boolean} shim
18981      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
18982      */
18983     shim:false,
18984     // inherit
18985     shadow:"frame",
18986     /**
18987      * @cfg {Number} maxWidth
18988      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
18989      * the containing tree element's size, it will be automatically limited for you to the container width, taking
18990      * scroll and client offsets into account prior to each edit.
18991      */
18992     maxWidth: 250,
18993
18994     editDelay : 350,
18995
18996     // private
18997     fitToTree : function(ed, el){
18998         var td = this.tree.getTreeEl().dom, nd = el.dom;
18999         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19000             td.scrollLeft = nd.offsetLeft;
19001         }
19002         var w = Math.min(
19003                 this.maxWidth,
19004                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19005         this.setSize(w, '');
19006     },
19007
19008     // private
19009     triggerEdit : function(node){
19010         this.completeEdit();
19011         this.editNode = node;
19012         this.startEdit(node.ui.textNode, node.text);
19013     },
19014
19015     // private
19016     bindScroll : function(){
19017         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19018     },
19019
19020     // private
19021     beforeNodeClick : function(node, e){
19022         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19023         this.lastClick = new Date();
19024         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19025             e.stopEvent();
19026             this.triggerEdit(node);
19027             return false;
19028         }
19029     },
19030
19031     // private
19032     updateNode : function(ed, value){
19033         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19034         this.editNode.setText(value);
19035     },
19036
19037     // private
19038     onHide : function(){
19039         Roo.tree.TreeEditor.superclass.onHide.call(this);
19040         if(this.editNode){
19041             this.editNode.ui.focus();
19042         }
19043     },
19044
19045     // private
19046     onSpecialKey : function(field, e){
19047         var k = e.getKey();
19048         if(k == e.ESC){
19049             e.stopEvent();
19050             this.cancelEdit();
19051         }else if(k == e.ENTER && !e.hasModifier()){
19052             e.stopEvent();
19053             this.completeEdit();
19054         }
19055     }
19056 });//<Script type="text/javascript">
19057 /*
19058  * Based on:
19059  * Ext JS Library 1.1.1
19060  * Copyright(c) 2006-2007, Ext JS, LLC.
19061  *
19062  * Originally Released Under LGPL - original licence link has changed is not relivant.
19063  *
19064  * Fork - LGPL
19065  * <script type="text/javascript">
19066  */
19067  
19068 /**
19069  * Not documented??? - probably should be...
19070  */
19071
19072 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19073     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19074     
19075     renderElements : function(n, a, targetNode, bulkRender){
19076         //consel.log("renderElements?");
19077         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19078
19079         var t = n.getOwnerTree();
19080         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19081         
19082         var cols = t.columns;
19083         var bw = t.borderWidth;
19084         var c = cols[0];
19085         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19086          var cb = typeof a.checked == "boolean";
19087         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19088         var colcls = 'x-t-' + tid + '-c0';
19089         var buf = [
19090             '<li class="x-tree-node">',
19091             
19092                 
19093                 '<div class="x-tree-node-el ', a.cls,'">',
19094                     // extran...
19095                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19096                 
19097                 
19098                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19099                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19100                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19101                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19102                            (a.iconCls ? ' '+a.iconCls : ''),
19103                            '" unselectable="on" />',
19104                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19105                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19106                              
19107                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19108                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19109                             '<span unselectable="on" qtip="' + tx + '">',
19110                              tx,
19111                              '</span></a>' ,
19112                     '</div>',
19113                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19114                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19115                  ];
19116         for(var i = 1, len = cols.length; i < len; i++){
19117             c = cols[i];
19118             colcls = 'x-t-' + tid + '-c' +i;
19119             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19120             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19121                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19122                       "</div>");
19123          }
19124          
19125          buf.push(
19126             '</a>',
19127             '<div class="x-clear"></div></div>',
19128             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19129             "</li>");
19130         
19131         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19132             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19133                                 n.nextSibling.ui.getEl(), buf.join(""));
19134         }else{
19135             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19136         }
19137         var el = this.wrap.firstChild;
19138         this.elRow = el;
19139         this.elNode = el.firstChild;
19140         this.ranchor = el.childNodes[1];
19141         this.ctNode = this.wrap.childNodes[1];
19142         var cs = el.firstChild.childNodes;
19143         this.indentNode = cs[0];
19144         this.ecNode = cs[1];
19145         this.iconNode = cs[2];
19146         var index = 3;
19147         if(cb){
19148             this.checkbox = cs[3];
19149             index++;
19150         }
19151         this.anchor = cs[index];
19152         
19153         this.textNode = cs[index].firstChild;
19154         
19155         //el.on("click", this.onClick, this);
19156         //el.on("dblclick", this.onDblClick, this);
19157         
19158         
19159        // console.log(this);
19160     },
19161     initEvents : function(){
19162         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19163         
19164             
19165         var a = this.ranchor;
19166
19167         var el = Roo.get(a);
19168
19169         if(Roo.isOpera){ // opera render bug ignores the CSS
19170             el.setStyle("text-decoration", "none");
19171         }
19172
19173         el.on("click", this.onClick, this);
19174         el.on("dblclick", this.onDblClick, this);
19175         el.on("contextmenu", this.onContextMenu, this);
19176         
19177     },
19178     
19179     /*onSelectedChange : function(state){
19180         if(state){
19181             this.focus();
19182             this.addClass("x-tree-selected");
19183         }else{
19184             //this.blur();
19185             this.removeClass("x-tree-selected");
19186         }
19187     },*/
19188     addClass : function(cls){
19189         if(this.elRow){
19190             Roo.fly(this.elRow).addClass(cls);
19191         }
19192         
19193     },
19194     
19195     
19196     removeClass : function(cls){
19197         if(this.elRow){
19198             Roo.fly(this.elRow).removeClass(cls);
19199         }
19200     }
19201
19202     
19203     
19204 });//<Script type="text/javascript">
19205
19206 /*
19207  * Based on:
19208  * Ext JS Library 1.1.1
19209  * Copyright(c) 2006-2007, Ext JS, LLC.
19210  *
19211  * Originally Released Under LGPL - original licence link has changed is not relivant.
19212  *
19213  * Fork - LGPL
19214  * <script type="text/javascript">
19215  */
19216  
19217
19218 /**
19219  * @class Roo.tree.ColumnTree
19220  * @extends Roo.data.TreePanel
19221  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19222  * @cfg {int} borderWidth  compined right/left border allowance
19223  * @constructor
19224  * @param {String/HTMLElement/Element} el The container element
19225  * @param {Object} config
19226  */
19227 Roo.tree.ColumnTree =  function(el, config)
19228 {
19229    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19230    this.addEvents({
19231         /**
19232         * @event resize
19233         * Fire this event on a container when it resizes
19234         * @param {int} w Width
19235         * @param {int} h Height
19236         */
19237        "resize" : true
19238     });
19239     this.on('resize', this.onResize, this);
19240 };
19241
19242 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19243     //lines:false,
19244     
19245     
19246     borderWidth: Roo.isBorderBox ? 0 : 2, 
19247     headEls : false,
19248     
19249     render : function(){
19250         // add the header.....
19251        
19252         Roo.tree.ColumnTree.superclass.render.apply(this);
19253         
19254         this.el.addClass('x-column-tree');
19255         
19256         this.headers = this.el.createChild(
19257             {cls:'x-tree-headers'},this.innerCt.dom);
19258    
19259         var cols = this.columns, c;
19260         var totalWidth = 0;
19261         this.headEls = [];
19262         var  len = cols.length;
19263         for(var i = 0; i < len; i++){
19264              c = cols[i];
19265              totalWidth += c.width;
19266             this.headEls.push(this.headers.createChild({
19267                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19268                  cn: {
19269                      cls:'x-tree-hd-text',
19270                      html: c.header
19271                  },
19272                  style:'width:'+(c.width-this.borderWidth)+'px;'
19273              }));
19274         }
19275         this.headers.createChild({cls:'x-clear'});
19276         // prevent floats from wrapping when clipped
19277         this.headers.setWidth(totalWidth);
19278         //this.innerCt.setWidth(totalWidth);
19279         this.innerCt.setStyle({ overflow: 'auto' });
19280         this.onResize(this.width, this.height);
19281              
19282         
19283     },
19284     onResize : function(w,h)
19285     {
19286         this.height = h;
19287         this.width = w;
19288         // resize cols..
19289         this.innerCt.setWidth(this.width);
19290         this.innerCt.setHeight(this.height-20);
19291         
19292         // headers...
19293         var cols = this.columns, c;
19294         var totalWidth = 0;
19295         var expEl = false;
19296         var len = cols.length;
19297         for(var i = 0; i < len; i++){
19298             c = cols[i];
19299             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19300                 // it's the expander..
19301                 expEl  = this.headEls[i];
19302                 continue;
19303             }
19304             totalWidth += c.width;
19305             
19306         }
19307         if (expEl) {
19308             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19309         }
19310         this.headers.setWidth(w-20);
19311
19312         
19313         
19314         
19315     }
19316 });
19317 /*
19318  * Based on:
19319  * Ext JS Library 1.1.1
19320  * Copyright(c) 2006-2007, Ext JS, LLC.
19321  *
19322  * Originally Released Under LGPL - original licence link has changed is not relivant.
19323  *
19324  * Fork - LGPL
19325  * <script type="text/javascript">
19326  */
19327  
19328 /**
19329  * @class Roo.menu.Menu
19330  * @extends Roo.util.Observable
19331  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19332  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19333  * @constructor
19334  * Creates a new Menu
19335  * @param {Object} config Configuration options
19336  */
19337 Roo.menu.Menu = function(config){
19338     Roo.apply(this, config);
19339     this.id = this.id || Roo.id();
19340     this.addEvents({
19341         /**
19342          * @event beforeshow
19343          * Fires before this menu is displayed
19344          * @param {Roo.menu.Menu} this
19345          */
19346         beforeshow : true,
19347         /**
19348          * @event beforehide
19349          * Fires before this menu is hidden
19350          * @param {Roo.menu.Menu} this
19351          */
19352         beforehide : true,
19353         /**
19354          * @event show
19355          * Fires after this menu is displayed
19356          * @param {Roo.menu.Menu} this
19357          */
19358         show : true,
19359         /**
19360          * @event hide
19361          * Fires after this menu is hidden
19362          * @param {Roo.menu.Menu} this
19363          */
19364         hide : true,
19365         /**
19366          * @event click
19367          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19368          * @param {Roo.menu.Menu} this
19369          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19370          * @param {Roo.EventObject} e
19371          */
19372         click : true,
19373         /**
19374          * @event mouseover
19375          * Fires when the mouse is hovering over this menu
19376          * @param {Roo.menu.Menu} this
19377          * @param {Roo.EventObject} e
19378          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19379          */
19380         mouseover : true,
19381         /**
19382          * @event mouseout
19383          * Fires when the mouse exits this menu
19384          * @param {Roo.menu.Menu} this
19385          * @param {Roo.EventObject} e
19386          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19387          */
19388         mouseout : true,
19389         /**
19390          * @event itemclick
19391          * Fires when a menu item contained in this menu is clicked
19392          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19393          * @param {Roo.EventObject} e
19394          */
19395         itemclick: true
19396     });
19397     if (this.registerMenu) {
19398         Roo.menu.MenuMgr.register(this);
19399     }
19400     
19401     var mis = this.items;
19402     this.items = new Roo.util.MixedCollection();
19403     if(mis){
19404         this.add.apply(this, mis);
19405     }
19406 };
19407
19408 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19409     /**
19410      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19411      */
19412     minWidth : 120,
19413     /**
19414      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19415      * for bottom-right shadow (defaults to "sides")
19416      */
19417     shadow : "sides",
19418     /**
19419      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19420      * this menu (defaults to "tl-tr?")
19421      */
19422     subMenuAlign : "tl-tr?",
19423     /**
19424      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19425      * relative to its element of origin (defaults to "tl-bl?")
19426      */
19427     defaultAlign : "tl-bl?",
19428     /**
19429      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19430      */
19431     allowOtherMenus : false,
19432     /**
19433      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19434      */
19435     registerMenu : true,
19436
19437     hidden:true,
19438
19439     // private
19440     render : function(){
19441         if(this.el){
19442             return;
19443         }
19444         var el = this.el = new Roo.Layer({
19445             cls: "x-menu",
19446             shadow:this.shadow,
19447             constrain: false,
19448             parentEl: this.parentEl || document.body,
19449             zindex:15000
19450         });
19451
19452         this.keyNav = new Roo.menu.MenuNav(this);
19453
19454         if(this.plain){
19455             el.addClass("x-menu-plain");
19456         }
19457         if(this.cls){
19458             el.addClass(this.cls);
19459         }
19460         // generic focus element
19461         this.focusEl = el.createChild({
19462             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19463         });
19464         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19465         ul.on("click", this.onClick, this);
19466         ul.on("mouseover", this.onMouseOver, this);
19467         ul.on("mouseout", this.onMouseOut, this);
19468         this.items.each(function(item){
19469             var li = document.createElement("li");
19470             li.className = "x-menu-list-item";
19471             ul.dom.appendChild(li);
19472             item.render(li, this);
19473         }, this);
19474         this.ul = ul;
19475         this.autoWidth();
19476     },
19477
19478     // private
19479     autoWidth : function(){
19480         var el = this.el, ul = this.ul;
19481         if(!el){
19482             return;
19483         }
19484         var w = this.width;
19485         if(w){
19486             el.setWidth(w);
19487         }else if(Roo.isIE){
19488             el.setWidth(this.minWidth);
19489             var t = el.dom.offsetWidth; // force recalc
19490             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19491         }
19492     },
19493
19494     // private
19495     delayAutoWidth : function(){
19496         if(this.rendered){
19497             if(!this.awTask){
19498                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19499             }
19500             this.awTask.delay(20);
19501         }
19502     },
19503
19504     // private
19505     findTargetItem : function(e){
19506         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19507         if(t && t.menuItemId){
19508             return this.items.get(t.menuItemId);
19509         }
19510     },
19511
19512     // private
19513     onClick : function(e){
19514         var t;
19515         if(t = this.findTargetItem(e)){
19516             t.onClick(e);
19517             this.fireEvent("click", this, t, e);
19518         }
19519     },
19520
19521     // private
19522     setActiveItem : function(item, autoExpand){
19523         if(item != this.activeItem){
19524             if(this.activeItem){
19525                 this.activeItem.deactivate();
19526             }
19527             this.activeItem = item;
19528             item.activate(autoExpand);
19529         }else if(autoExpand){
19530             item.expandMenu();
19531         }
19532     },
19533
19534     // private
19535     tryActivate : function(start, step){
19536         var items = this.items;
19537         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19538             var item = items.get(i);
19539             if(!item.disabled && item.canActivate){
19540                 this.setActiveItem(item, false);
19541                 return item;
19542             }
19543         }
19544         return false;
19545     },
19546
19547     // private
19548     onMouseOver : function(e){
19549         var t;
19550         if(t = this.findTargetItem(e)){
19551             if(t.canActivate && !t.disabled){
19552                 this.setActiveItem(t, true);
19553             }
19554         }
19555         this.fireEvent("mouseover", this, e, t);
19556     },
19557
19558     // private
19559     onMouseOut : function(e){
19560         var t;
19561         if(t = this.findTargetItem(e)){
19562             if(t == this.activeItem && t.shouldDeactivate(e)){
19563                 this.activeItem.deactivate();
19564                 delete this.activeItem;
19565             }
19566         }
19567         this.fireEvent("mouseout", this, e, t);
19568     },
19569
19570     /**
19571      * Read-only.  Returns true if the menu is currently displayed, else false.
19572      * @type Boolean
19573      */
19574     isVisible : function(){
19575         return this.el && !this.hidden;
19576     },
19577
19578     /**
19579      * Displays this menu relative to another element
19580      * @param {String/HTMLElement/Roo.Element} element The element to align to
19581      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19582      * the element (defaults to this.defaultAlign)
19583      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19584      */
19585     show : function(el, pos, parentMenu){
19586         this.parentMenu = parentMenu;
19587         if(!this.el){
19588             this.render();
19589         }
19590         this.fireEvent("beforeshow", this);
19591         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19592     },
19593
19594     /**
19595      * Displays this menu at a specific xy position
19596      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19597      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19598      */
19599     showAt : function(xy, parentMenu, /* private: */_e){
19600         this.parentMenu = parentMenu;
19601         if(!this.el){
19602             this.render();
19603         }
19604         if(_e !== false){
19605             this.fireEvent("beforeshow", this);
19606             xy = this.el.adjustForConstraints(xy);
19607         }
19608         this.el.setXY(xy);
19609         this.el.show();
19610         this.hidden = false;
19611         this.focus();
19612         this.fireEvent("show", this);
19613     },
19614
19615     focus : function(){
19616         if(!this.hidden){
19617             this.doFocus.defer(50, this);
19618         }
19619     },
19620
19621     doFocus : function(){
19622         if(!this.hidden){
19623             this.focusEl.focus();
19624         }
19625     },
19626
19627     /**
19628      * Hides this menu and optionally all parent menus
19629      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19630      */
19631     hide : function(deep){
19632         if(this.el && this.isVisible()){
19633             this.fireEvent("beforehide", this);
19634             if(this.activeItem){
19635                 this.activeItem.deactivate();
19636                 this.activeItem = null;
19637             }
19638             this.el.hide();
19639             this.hidden = true;
19640             this.fireEvent("hide", this);
19641         }
19642         if(deep === true && this.parentMenu){
19643             this.parentMenu.hide(true);
19644         }
19645     },
19646
19647     /**
19648      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19649      * Any of the following are valid:
19650      * <ul>
19651      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19652      * <li>An HTMLElement object which will be converted to a menu item</li>
19653      * <li>A menu item config object that will be created as a new menu item</li>
19654      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19655      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19656      * </ul>
19657      * Usage:
19658      * <pre><code>
19659 // Create the menu
19660 var menu = new Roo.menu.Menu();
19661
19662 // Create a menu item to add by reference
19663 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19664
19665 // Add a bunch of items at once using different methods.
19666 // Only the last item added will be returned.
19667 var item = menu.add(
19668     menuItem,                // add existing item by ref
19669     'Dynamic Item',          // new TextItem
19670     '-',                     // new separator
19671     { text: 'Config Item' }  // new item by config
19672 );
19673 </code></pre>
19674      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19675      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19676      */
19677     add : function(){
19678         var a = arguments, l = a.length, item;
19679         for(var i = 0; i < l; i++){
19680             var el = a[i];
19681             if ((typeof(el) == "object") && el.xtype && el.xns) {
19682                 el = Roo.factory(el, Roo.menu);
19683             }
19684             
19685             if(el.render){ // some kind of Item
19686                 item = this.addItem(el);
19687             }else if(typeof el == "string"){ // string
19688                 if(el == "separator" || el == "-"){
19689                     item = this.addSeparator();
19690                 }else{
19691                     item = this.addText(el);
19692                 }
19693             }else if(el.tagName || el.el){ // element
19694                 item = this.addElement(el);
19695             }else if(typeof el == "object"){ // must be menu item config?
19696                 item = this.addMenuItem(el);
19697             }
19698         }
19699         return item;
19700     },
19701
19702     /**
19703      * Returns this menu's underlying {@link Roo.Element} object
19704      * @return {Roo.Element} The element
19705      */
19706     getEl : function(){
19707         if(!this.el){
19708             this.render();
19709         }
19710         return this.el;
19711     },
19712
19713     /**
19714      * Adds a separator bar to the menu
19715      * @return {Roo.menu.Item} The menu item that was added
19716      */
19717     addSeparator : function(){
19718         return this.addItem(new Roo.menu.Separator());
19719     },
19720
19721     /**
19722      * Adds an {@link Roo.Element} object to the menu
19723      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
19724      * @return {Roo.menu.Item} The menu item that was added
19725      */
19726     addElement : function(el){
19727         return this.addItem(new Roo.menu.BaseItem(el));
19728     },
19729
19730     /**
19731      * Adds an existing object based on {@link Roo.menu.Item} to the menu
19732      * @param {Roo.menu.Item} item The menu item to add
19733      * @return {Roo.menu.Item} The menu item that was added
19734      */
19735     addItem : function(item){
19736         this.items.add(item);
19737         if(this.ul){
19738             var li = document.createElement("li");
19739             li.className = "x-menu-list-item";
19740             this.ul.dom.appendChild(li);
19741             item.render(li, this);
19742             this.delayAutoWidth();
19743         }
19744         return item;
19745     },
19746
19747     /**
19748      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
19749      * @param {Object} config A MenuItem config object
19750      * @return {Roo.menu.Item} The menu item that was added
19751      */
19752     addMenuItem : function(config){
19753         if(!(config instanceof Roo.menu.Item)){
19754             if(typeof config.checked == "boolean"){ // must be check menu item config?
19755                 config = new Roo.menu.CheckItem(config);
19756             }else{
19757                 config = new Roo.menu.Item(config);
19758             }
19759         }
19760         return this.addItem(config);
19761     },
19762
19763     /**
19764      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
19765      * @param {String} text The text to display in the menu item
19766      * @return {Roo.menu.Item} The menu item that was added
19767      */
19768     addText : function(text){
19769         return this.addItem(new Roo.menu.TextItem({ text : text }));
19770     },
19771
19772     /**
19773      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
19774      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
19775      * @param {Roo.menu.Item} item The menu item to add
19776      * @return {Roo.menu.Item} The menu item that was added
19777      */
19778     insert : function(index, item){
19779         this.items.insert(index, item);
19780         if(this.ul){
19781             var li = document.createElement("li");
19782             li.className = "x-menu-list-item";
19783             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
19784             item.render(li, this);
19785             this.delayAutoWidth();
19786         }
19787         return item;
19788     },
19789
19790     /**
19791      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
19792      * @param {Roo.menu.Item} item The menu item to remove
19793      */
19794     remove : function(item){
19795         this.items.removeKey(item.id);
19796         item.destroy();
19797     },
19798
19799     /**
19800      * Removes and destroys all items in the menu
19801      */
19802     removeAll : function(){
19803         var f;
19804         while(f = this.items.first()){
19805             this.remove(f);
19806         }
19807     }
19808 });
19809
19810 // MenuNav is a private utility class used internally by the Menu
19811 Roo.menu.MenuNav = function(menu){
19812     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
19813     this.scope = this.menu = menu;
19814 };
19815
19816 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
19817     doRelay : function(e, h){
19818         var k = e.getKey();
19819         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
19820             this.menu.tryActivate(0, 1);
19821             return false;
19822         }
19823         return h.call(this.scope || this, e, this.menu);
19824     },
19825
19826     up : function(e, m){
19827         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
19828             m.tryActivate(m.items.length-1, -1);
19829         }
19830     },
19831
19832     down : function(e, m){
19833         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
19834             m.tryActivate(0, 1);
19835         }
19836     },
19837
19838     right : function(e, m){
19839         if(m.activeItem){
19840             m.activeItem.expandMenu(true);
19841         }
19842     },
19843
19844     left : function(e, m){
19845         m.hide();
19846         if(m.parentMenu && m.parentMenu.activeItem){
19847             m.parentMenu.activeItem.activate();
19848         }
19849     },
19850
19851     enter : function(e, m){
19852         if(m.activeItem){
19853             e.stopPropagation();
19854             m.activeItem.onClick(e);
19855             m.fireEvent("click", this, m.activeItem);
19856             return true;
19857         }
19858     }
19859 });/*
19860  * Based on:
19861  * Ext JS Library 1.1.1
19862  * Copyright(c) 2006-2007, Ext JS, LLC.
19863  *
19864  * Originally Released Under LGPL - original licence link has changed is not relivant.
19865  *
19866  * Fork - LGPL
19867  * <script type="text/javascript">
19868  */
19869  
19870 /**
19871  * @class Roo.menu.MenuMgr
19872  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
19873  * @singleton
19874  */
19875 Roo.menu.MenuMgr = function(){
19876    var menus, active, groups = {}, attached = false, lastShow = new Date();
19877
19878    // private - called when first menu is created
19879    function init(){
19880        menus = {};
19881        active = new Roo.util.MixedCollection();
19882        Roo.get(document).addKeyListener(27, function(){
19883            if(active.length > 0){
19884                hideAll();
19885            }
19886        });
19887    }
19888
19889    // private
19890    function hideAll(){
19891        if(active && active.length > 0){
19892            var c = active.clone();
19893            c.each(function(m){
19894                m.hide();
19895            });
19896        }
19897    }
19898
19899    // private
19900    function onHide(m){
19901        active.remove(m);
19902        if(active.length < 1){
19903            Roo.get(document).un("mousedown", onMouseDown);
19904            attached = false;
19905        }
19906    }
19907
19908    // private
19909    function onShow(m){
19910        var last = active.last();
19911        lastShow = new Date();
19912        active.add(m);
19913        if(!attached){
19914            Roo.get(document).on("mousedown", onMouseDown);
19915            attached = true;
19916        }
19917        if(m.parentMenu){
19918           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
19919           m.parentMenu.activeChild = m;
19920        }else if(last && last.isVisible()){
19921           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
19922        }
19923    }
19924
19925    // private
19926    function onBeforeHide(m){
19927        if(m.activeChild){
19928            m.activeChild.hide();
19929        }
19930        if(m.autoHideTimer){
19931            clearTimeout(m.autoHideTimer);
19932            delete m.autoHideTimer;
19933        }
19934    }
19935
19936    // private
19937    function onBeforeShow(m){
19938        var pm = m.parentMenu;
19939        if(!pm && !m.allowOtherMenus){
19940            hideAll();
19941        }else if(pm && pm.activeChild && active != m){
19942            pm.activeChild.hide();
19943        }
19944    }
19945
19946    // private
19947    function onMouseDown(e){
19948        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
19949            hideAll();
19950        }
19951    }
19952
19953    // private
19954    function onBeforeCheck(mi, state){
19955        if(state){
19956            var g = groups[mi.group];
19957            for(var i = 0, l = g.length; i < l; i++){
19958                if(g[i] != mi){
19959                    g[i].setChecked(false);
19960                }
19961            }
19962        }
19963    }
19964
19965    return {
19966
19967        /**
19968         * Hides all menus that are currently visible
19969         */
19970        hideAll : function(){
19971             hideAll();  
19972        },
19973
19974        // private
19975        register : function(menu){
19976            if(!menus){
19977                init();
19978            }
19979            menus[menu.id] = menu;
19980            menu.on("beforehide", onBeforeHide);
19981            menu.on("hide", onHide);
19982            menu.on("beforeshow", onBeforeShow);
19983            menu.on("show", onShow);
19984            var g = menu.group;
19985            if(g && menu.events["checkchange"]){
19986                if(!groups[g]){
19987                    groups[g] = [];
19988                }
19989                groups[g].push(menu);
19990                menu.on("checkchange", onCheck);
19991            }
19992        },
19993
19994         /**
19995          * Returns a {@link Roo.menu.Menu} object
19996          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
19997          * be used to generate and return a new Menu instance.
19998          */
19999        get : function(menu){
20000            if(typeof menu == "string"){ // menu id
20001                return menus[menu];
20002            }else if(menu.events){  // menu instance
20003                return menu;
20004            }else if(typeof menu.length == 'number'){ // array of menu items?
20005                return new Roo.menu.Menu({items:menu});
20006            }else{ // otherwise, must be a config
20007                return new Roo.menu.Menu(menu);
20008            }
20009        },
20010
20011        // private
20012        unregister : function(menu){
20013            delete menus[menu.id];
20014            menu.un("beforehide", onBeforeHide);
20015            menu.un("hide", onHide);
20016            menu.un("beforeshow", onBeforeShow);
20017            menu.un("show", onShow);
20018            var g = menu.group;
20019            if(g && menu.events["checkchange"]){
20020                groups[g].remove(menu);
20021                menu.un("checkchange", onCheck);
20022            }
20023        },
20024
20025        // private
20026        registerCheckable : function(menuItem){
20027            var g = menuItem.group;
20028            if(g){
20029                if(!groups[g]){
20030                    groups[g] = [];
20031                }
20032                groups[g].push(menuItem);
20033                menuItem.on("beforecheckchange", onBeforeCheck);
20034            }
20035        },
20036
20037        // private
20038        unregisterCheckable : function(menuItem){
20039            var g = menuItem.group;
20040            if(g){
20041                groups[g].remove(menuItem);
20042                menuItem.un("beforecheckchange", onBeforeCheck);
20043            }
20044        }
20045    };
20046 }();/*
20047  * Based on:
20048  * Ext JS Library 1.1.1
20049  * Copyright(c) 2006-2007, Ext JS, LLC.
20050  *
20051  * Originally Released Under LGPL - original licence link has changed is not relivant.
20052  *
20053  * Fork - LGPL
20054  * <script type="text/javascript">
20055  */
20056  
20057
20058 /**
20059  * @class Roo.menu.BaseItem
20060  * @extends Roo.Component
20061  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20062  * management and base configuration options shared by all menu components.
20063  * @constructor
20064  * Creates a new BaseItem
20065  * @param {Object} config Configuration options
20066  */
20067 Roo.menu.BaseItem = function(config){
20068     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20069
20070     this.addEvents({
20071         /**
20072          * @event click
20073          * Fires when this item is clicked
20074          * @param {Roo.menu.BaseItem} this
20075          * @param {Roo.EventObject} e
20076          */
20077         click: true,
20078         /**
20079          * @event activate
20080          * Fires when this item is activated
20081          * @param {Roo.menu.BaseItem} this
20082          */
20083         activate : true,
20084         /**
20085          * @event deactivate
20086          * Fires when this item is deactivated
20087          * @param {Roo.menu.BaseItem} this
20088          */
20089         deactivate : true
20090     });
20091
20092     if(this.handler){
20093         this.on("click", this.handler, this.scope, true);
20094     }
20095 };
20096
20097 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20098     /**
20099      * @cfg {Function} handler
20100      * A function that will handle the click event of this menu item (defaults to undefined)
20101      */
20102     /**
20103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20104      */
20105     canActivate : false,
20106     /**
20107      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20108      */
20109     activeClass : "x-menu-item-active",
20110     /**
20111      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20112      */
20113     hideOnClick : true,
20114     /**
20115      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20116      */
20117     hideDelay : 100,
20118
20119     // private
20120     ctype: "Roo.menu.BaseItem",
20121
20122     // private
20123     actionMode : "container",
20124
20125     // private
20126     render : function(container, parentMenu){
20127         this.parentMenu = parentMenu;
20128         Roo.menu.BaseItem.superclass.render.call(this, container);
20129         this.container.menuItemId = this.id;
20130     },
20131
20132     // private
20133     onRender : function(container, position){
20134         this.el = Roo.get(this.el);
20135         container.dom.appendChild(this.el.dom);
20136     },
20137
20138     // private
20139     onClick : function(e){
20140         if(!this.disabled && this.fireEvent("click", this, e) !== false
20141                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20142             this.handleClick(e);
20143         }else{
20144             e.stopEvent();
20145         }
20146     },
20147
20148     // private
20149     activate : function(){
20150         if(this.disabled){
20151             return false;
20152         }
20153         var li = this.container;
20154         li.addClass(this.activeClass);
20155         this.region = li.getRegion().adjust(2, 2, -2, -2);
20156         this.fireEvent("activate", this);
20157         return true;
20158     },
20159
20160     // private
20161     deactivate : function(){
20162         this.container.removeClass(this.activeClass);
20163         this.fireEvent("deactivate", this);
20164     },
20165
20166     // private
20167     shouldDeactivate : function(e){
20168         return !this.region || !this.region.contains(e.getPoint());
20169     },
20170
20171     // private
20172     handleClick : function(e){
20173         if(this.hideOnClick){
20174             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20175         }
20176     },
20177
20178     // private
20179     expandMenu : function(autoActivate){
20180         // do nothing
20181     },
20182
20183     // private
20184     hideMenu : function(){
20185         // do nothing
20186     }
20187 });/*
20188  * Based on:
20189  * Ext JS Library 1.1.1
20190  * Copyright(c) 2006-2007, Ext JS, LLC.
20191  *
20192  * Originally Released Under LGPL - original licence link has changed is not relivant.
20193  *
20194  * Fork - LGPL
20195  * <script type="text/javascript">
20196  */
20197  
20198 /**
20199  * @class Roo.menu.Adapter
20200  * @extends Roo.menu.BaseItem
20201  * 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.
20202  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20203  * @constructor
20204  * Creates a new Adapter
20205  * @param {Object} config Configuration options
20206  */
20207 Roo.menu.Adapter = function(component, config){
20208     Roo.menu.Adapter.superclass.constructor.call(this, config);
20209     this.component = component;
20210 };
20211 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20212     // private
20213     canActivate : true,
20214
20215     // private
20216     onRender : function(container, position){
20217         this.component.render(container);
20218         this.el = this.component.getEl();
20219     },
20220
20221     // private
20222     activate : function(){
20223         if(this.disabled){
20224             return false;
20225         }
20226         this.component.focus();
20227         this.fireEvent("activate", this);
20228         return true;
20229     },
20230
20231     // private
20232     deactivate : function(){
20233         this.fireEvent("deactivate", this);
20234     },
20235
20236     // private
20237     disable : function(){
20238         this.component.disable();
20239         Roo.menu.Adapter.superclass.disable.call(this);
20240     },
20241
20242     // private
20243     enable : function(){
20244         this.component.enable();
20245         Roo.menu.Adapter.superclass.enable.call(this);
20246     }
20247 });/*
20248  * Based on:
20249  * Ext JS Library 1.1.1
20250  * Copyright(c) 2006-2007, Ext JS, LLC.
20251  *
20252  * Originally Released Under LGPL - original licence link has changed is not relivant.
20253  *
20254  * Fork - LGPL
20255  * <script type="text/javascript">
20256  */
20257
20258 /**
20259  * @class Roo.menu.TextItem
20260  * @extends Roo.menu.BaseItem
20261  * Adds a static text string to a menu, usually used as either a heading or group separator.
20262  * Note: old style constructor with text is still supported.
20263  * 
20264  * @constructor
20265  * Creates a new TextItem
20266  * @param {Object} cfg Configuration
20267  */
20268 Roo.menu.TextItem = function(cfg){
20269     if (typeof(cfg) == 'string') {
20270         this.text = cfg;
20271     } else {
20272         Roo.apply(this,cfg);
20273     }
20274     
20275     Roo.menu.TextItem.superclass.constructor.call(this);
20276 };
20277
20278 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20279     /**
20280      * @cfg {Boolean} text Text to show on item.
20281      */
20282     text : '',
20283     
20284     /**
20285      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20286      */
20287     hideOnClick : false,
20288     /**
20289      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20290      */
20291     itemCls : "x-menu-text",
20292
20293     // private
20294     onRender : function(){
20295         var s = document.createElement("span");
20296         s.className = this.itemCls;
20297         s.innerHTML = this.text;
20298         this.el = s;
20299         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20300     }
20301 });/*
20302  * Based on:
20303  * Ext JS Library 1.1.1
20304  * Copyright(c) 2006-2007, Ext JS, LLC.
20305  *
20306  * Originally Released Under LGPL - original licence link has changed is not relivant.
20307  *
20308  * Fork - LGPL
20309  * <script type="text/javascript">
20310  */
20311
20312 /**
20313  * @class Roo.menu.Separator
20314  * @extends Roo.menu.BaseItem
20315  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20316  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20317  * @constructor
20318  * @param {Object} config Configuration options
20319  */
20320 Roo.menu.Separator = function(config){
20321     Roo.menu.Separator.superclass.constructor.call(this, config);
20322 };
20323
20324 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20325     /**
20326      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20327      */
20328     itemCls : "x-menu-sep",
20329     /**
20330      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20331      */
20332     hideOnClick : false,
20333
20334     // private
20335     onRender : function(li){
20336         var s = document.createElement("span");
20337         s.className = this.itemCls;
20338         s.innerHTML = "&#160;";
20339         this.el = s;
20340         li.addClass("x-menu-sep-li");
20341         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20342     }
20343 });/*
20344  * Based on:
20345  * Ext JS Library 1.1.1
20346  * Copyright(c) 2006-2007, Ext JS, LLC.
20347  *
20348  * Originally Released Under LGPL - original licence link has changed is not relivant.
20349  *
20350  * Fork - LGPL
20351  * <script type="text/javascript">
20352  */
20353 /**
20354  * @class Roo.menu.Item
20355  * @extends Roo.menu.BaseItem
20356  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20357  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20358  * activation and click handling.
20359  * @constructor
20360  * Creates a new Item
20361  * @param {Object} config Configuration options
20362  */
20363 Roo.menu.Item = function(config){
20364     Roo.menu.Item.superclass.constructor.call(this, config);
20365     if(this.menu){
20366         this.menu = Roo.menu.MenuMgr.get(this.menu);
20367     }
20368 };
20369 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20370     
20371     /**
20372      * @cfg {String} text
20373      * The text to show on the menu item.
20374      */
20375     text: '',
20376      /**
20377      * @cfg {String} HTML to render in menu
20378      * The text to show on the menu item (HTML version).
20379      */
20380     html: '',
20381     /**
20382      * @cfg {String} icon
20383      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20384      */
20385     icon: undefined,
20386     /**
20387      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20388      */
20389     itemCls : "x-menu-item",
20390     /**
20391      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20392      */
20393     canActivate : true,
20394     /**
20395      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20396      */
20397     showDelay: 200,
20398     // doc'd in BaseItem
20399     hideDelay: 200,
20400
20401     // private
20402     ctype: "Roo.menu.Item",
20403     
20404     // private
20405     onRender : function(container, position){
20406         var el = document.createElement("a");
20407         el.hideFocus = true;
20408         el.unselectable = "on";
20409         el.href = this.href || "#";
20410         if(this.hrefTarget){
20411             el.target = this.hrefTarget;
20412         }
20413         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20414         
20415         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20416         
20417         el.innerHTML = String.format(
20418                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20419                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20420         this.el = el;
20421         Roo.menu.Item.superclass.onRender.call(this, container, position);
20422     },
20423
20424     /**
20425      * Sets the text to display in this menu item
20426      * @param {String} text The text to display
20427      * @param {Boolean} isHTML true to indicate text is pure html.
20428      */
20429     setText : function(text, isHTML){
20430         if (isHTML) {
20431             this.html = text;
20432         } else {
20433             this.text = text;
20434             this.html = '';
20435         }
20436         if(this.rendered){
20437             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20438      
20439             this.el.update(String.format(
20440                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20441                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20442             this.parentMenu.autoWidth();
20443         }
20444     },
20445
20446     // private
20447     handleClick : function(e){
20448         if(!this.href){ // if no link defined, stop the event automatically
20449             e.stopEvent();
20450         }
20451         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20452     },
20453
20454     // private
20455     activate : function(autoExpand){
20456         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20457             this.focus();
20458             if(autoExpand){
20459                 this.expandMenu();
20460             }
20461         }
20462         return true;
20463     },
20464
20465     // private
20466     shouldDeactivate : function(e){
20467         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20468             if(this.menu && this.menu.isVisible()){
20469                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20470             }
20471             return true;
20472         }
20473         return false;
20474     },
20475
20476     // private
20477     deactivate : function(){
20478         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20479         this.hideMenu();
20480     },
20481
20482     // private
20483     expandMenu : function(autoActivate){
20484         if(!this.disabled && this.menu){
20485             clearTimeout(this.hideTimer);
20486             delete this.hideTimer;
20487             if(!this.menu.isVisible() && !this.showTimer){
20488                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20489             }else if (this.menu.isVisible() && autoActivate){
20490                 this.menu.tryActivate(0, 1);
20491             }
20492         }
20493     },
20494
20495     // private
20496     deferExpand : function(autoActivate){
20497         delete this.showTimer;
20498         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20499         if(autoActivate){
20500             this.menu.tryActivate(0, 1);
20501         }
20502     },
20503
20504     // private
20505     hideMenu : function(){
20506         clearTimeout(this.showTimer);
20507         delete this.showTimer;
20508         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20509             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20510         }
20511     },
20512
20513     // private
20514     deferHide : function(){
20515         delete this.hideTimer;
20516         this.menu.hide();
20517     }
20518 });/*
20519  * Based on:
20520  * Ext JS Library 1.1.1
20521  * Copyright(c) 2006-2007, Ext JS, LLC.
20522  *
20523  * Originally Released Under LGPL - original licence link has changed is not relivant.
20524  *
20525  * Fork - LGPL
20526  * <script type="text/javascript">
20527  */
20528  
20529 /**
20530  * @class Roo.menu.CheckItem
20531  * @extends Roo.menu.Item
20532  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20533  * @constructor
20534  * Creates a new CheckItem
20535  * @param {Object} config Configuration options
20536  */
20537 Roo.menu.CheckItem = function(config){
20538     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20539     this.addEvents({
20540         /**
20541          * @event beforecheckchange
20542          * Fires before the checked value is set, providing an opportunity to cancel if needed
20543          * @param {Roo.menu.CheckItem} this
20544          * @param {Boolean} checked The new checked value that will be set
20545          */
20546         "beforecheckchange" : true,
20547         /**
20548          * @event checkchange
20549          * Fires after the checked value has been set
20550          * @param {Roo.menu.CheckItem} this
20551          * @param {Boolean} checked The checked value that was set
20552          */
20553         "checkchange" : true
20554     });
20555     if(this.checkHandler){
20556         this.on('checkchange', this.checkHandler, this.scope);
20557     }
20558 };
20559 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20560     /**
20561      * @cfg {String} group
20562      * All check items with the same group name will automatically be grouped into a single-select
20563      * radio button group (defaults to '')
20564      */
20565     /**
20566      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20567      */
20568     itemCls : "x-menu-item x-menu-check-item",
20569     /**
20570      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20571      */
20572     groupClass : "x-menu-group-item",
20573
20574     /**
20575      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20576      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20577      * initialized with checked = true will be rendered as checked.
20578      */
20579     checked: false,
20580
20581     // private
20582     ctype: "Roo.menu.CheckItem",
20583
20584     // private
20585     onRender : function(c){
20586         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20587         if(this.group){
20588             this.el.addClass(this.groupClass);
20589         }
20590         Roo.menu.MenuMgr.registerCheckable(this);
20591         if(this.checked){
20592             this.checked = false;
20593             this.setChecked(true, true);
20594         }
20595     },
20596
20597     // private
20598     destroy : function(){
20599         if(this.rendered){
20600             Roo.menu.MenuMgr.unregisterCheckable(this);
20601         }
20602         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20603     },
20604
20605     /**
20606      * Set the checked state of this item
20607      * @param {Boolean} checked The new checked value
20608      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20609      */
20610     setChecked : function(state, suppressEvent){
20611         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20612             if(this.container){
20613                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20614             }
20615             this.checked = state;
20616             if(suppressEvent !== true){
20617                 this.fireEvent("checkchange", this, state);
20618             }
20619         }
20620     },
20621
20622     // private
20623     handleClick : function(e){
20624        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20625            this.setChecked(!this.checked);
20626        }
20627        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20628     }
20629 });/*
20630  * Based on:
20631  * Ext JS Library 1.1.1
20632  * Copyright(c) 2006-2007, Ext JS, LLC.
20633  *
20634  * Originally Released Under LGPL - original licence link has changed is not relivant.
20635  *
20636  * Fork - LGPL
20637  * <script type="text/javascript">
20638  */
20639  
20640 /**
20641  * @class Roo.menu.DateItem
20642  * @extends Roo.menu.Adapter
20643  * A menu item that wraps the {@link Roo.DatPicker} component.
20644  * @constructor
20645  * Creates a new DateItem
20646  * @param {Object} config Configuration options
20647  */
20648 Roo.menu.DateItem = function(config){
20649     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20650     /** The Roo.DatePicker object @type Roo.DatePicker */
20651     this.picker = this.component;
20652     this.addEvents({select: true});
20653     
20654     this.picker.on("render", function(picker){
20655         picker.getEl().swallowEvent("click");
20656         picker.container.addClass("x-menu-date-item");
20657     });
20658
20659     this.picker.on("select", this.onSelect, this);
20660 };
20661
20662 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20663     // private
20664     onSelect : function(picker, date){
20665         this.fireEvent("select", this, date, picker);
20666         Roo.menu.DateItem.superclass.handleClick.call(this);
20667     }
20668 });/*
20669  * Based on:
20670  * Ext JS Library 1.1.1
20671  * Copyright(c) 2006-2007, Ext JS, LLC.
20672  *
20673  * Originally Released Under LGPL - original licence link has changed is not relivant.
20674  *
20675  * Fork - LGPL
20676  * <script type="text/javascript">
20677  */
20678  
20679 /**
20680  * @class Roo.menu.ColorItem
20681  * @extends Roo.menu.Adapter
20682  * A menu item that wraps the {@link Roo.ColorPalette} component.
20683  * @constructor
20684  * Creates a new ColorItem
20685  * @param {Object} config Configuration options
20686  */
20687 Roo.menu.ColorItem = function(config){
20688     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
20689     /** The Roo.ColorPalette object @type Roo.ColorPalette */
20690     this.palette = this.component;
20691     this.relayEvents(this.palette, ["select"]);
20692     if(this.selectHandler){
20693         this.on('select', this.selectHandler, this.scope);
20694     }
20695 };
20696 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
20697  * Based on:
20698  * Ext JS Library 1.1.1
20699  * Copyright(c) 2006-2007, Ext JS, LLC.
20700  *
20701  * Originally Released Under LGPL - original licence link has changed is not relivant.
20702  *
20703  * Fork - LGPL
20704  * <script type="text/javascript">
20705  */
20706  
20707
20708 /**
20709  * @class Roo.menu.DateMenu
20710  * @extends Roo.menu.Menu
20711  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
20712  * @constructor
20713  * Creates a new DateMenu
20714  * @param {Object} config Configuration options
20715  */
20716 Roo.menu.DateMenu = function(config){
20717     Roo.menu.DateMenu.superclass.constructor.call(this, config);
20718     this.plain = true;
20719     var di = new Roo.menu.DateItem(config);
20720     this.add(di);
20721     /**
20722      * The {@link Roo.DatePicker} instance for this DateMenu
20723      * @type DatePicker
20724      */
20725     this.picker = di.picker;
20726     /**
20727      * @event select
20728      * @param {DatePicker} picker
20729      * @param {Date} date
20730      */
20731     this.relayEvents(di, ["select"]);
20732
20733     this.on('beforeshow', function(){
20734         if(this.picker){
20735             this.picker.hideMonthPicker(true);
20736         }
20737     }, this);
20738 };
20739 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
20740     cls:'x-date-menu'
20741 });/*
20742  * Based on:
20743  * Ext JS Library 1.1.1
20744  * Copyright(c) 2006-2007, Ext JS, LLC.
20745  *
20746  * Originally Released Under LGPL - original licence link has changed is not relivant.
20747  *
20748  * Fork - LGPL
20749  * <script type="text/javascript">
20750  */
20751  
20752
20753 /**
20754  * @class Roo.menu.ColorMenu
20755  * @extends Roo.menu.Menu
20756  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
20757  * @constructor
20758  * Creates a new ColorMenu
20759  * @param {Object} config Configuration options
20760  */
20761 Roo.menu.ColorMenu = function(config){
20762     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
20763     this.plain = true;
20764     var ci = new Roo.menu.ColorItem(config);
20765     this.add(ci);
20766     /**
20767      * The {@link Roo.ColorPalette} instance for this ColorMenu
20768      * @type ColorPalette
20769      */
20770     this.palette = ci.palette;
20771     /**
20772      * @event select
20773      * @param {ColorPalette} palette
20774      * @param {String} color
20775      */
20776     this.relayEvents(ci, ["select"]);
20777 };
20778 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
20779  * Based on:
20780  * Ext JS Library 1.1.1
20781  * Copyright(c) 2006-2007, Ext JS, LLC.
20782  *
20783  * Originally Released Under LGPL - original licence link has changed is not relivant.
20784  *
20785  * Fork - LGPL
20786  * <script type="text/javascript">
20787  */
20788  
20789 /**
20790  * @class Roo.form.Field
20791  * @extends Roo.BoxComponent
20792  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
20793  * @constructor
20794  * Creates a new Field
20795  * @param {Object} config Configuration options
20796  */
20797 Roo.form.Field = function(config){
20798     Roo.form.Field.superclass.constructor.call(this, config);
20799 };
20800
20801 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
20802     /**
20803      * @cfg {String} fieldLabel Label to use when rendering a form.
20804      */
20805        /**
20806      * @cfg {String} qtip Mouse over tip
20807      */
20808      
20809     /**
20810      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
20811      */
20812     invalidClass : "x-form-invalid",
20813     /**
20814      * @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")
20815      */
20816     invalidText : "The value in this field is invalid",
20817     /**
20818      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
20819      */
20820     focusClass : "x-form-focus",
20821     /**
20822      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
20823       automatic validation (defaults to "keyup").
20824      */
20825     validationEvent : "keyup",
20826     /**
20827      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
20828      */
20829     validateOnBlur : true,
20830     /**
20831      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
20832      */
20833     validationDelay : 250,
20834     /**
20835      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20836      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
20837      */
20838     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
20839     /**
20840      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
20841      */
20842     fieldClass : "x-form-field",
20843     /**
20844      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
20845      *<pre>
20846 Value         Description
20847 -----------   ----------------------------------------------------------------------
20848 qtip          Display a quick tip when the user hovers over the field
20849 title         Display a default browser title attribute popup
20850 under         Add a block div beneath the field containing the error text
20851 side          Add an error icon to the right of the field with a popup on hover
20852 [element id]  Add the error text directly to the innerHTML of the specified element
20853 </pre>
20854      */
20855     msgTarget : 'qtip',
20856     /**
20857      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
20858      */
20859     msgFx : 'normal',
20860
20861     /**
20862      * @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.
20863      */
20864     readOnly : false,
20865
20866     /**
20867      * @cfg {Boolean} disabled True to disable the field (defaults to false).
20868      */
20869     disabled : false,
20870
20871     /**
20872      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
20873      */
20874     inputType : undefined,
20875     
20876     /**
20877      * @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).
20878          */
20879         tabIndex : undefined,
20880         
20881     // private
20882     isFormField : true,
20883
20884     // private
20885     hasFocus : false,
20886     /**
20887      * @property {Roo.Element} fieldEl
20888      * Element Containing the rendered Field (with label etc.)
20889      */
20890     /**
20891      * @cfg {Mixed} value A value to initialize this field with.
20892      */
20893     value : undefined,
20894
20895     /**
20896      * @cfg {String} name The field's HTML name attribute.
20897      */
20898     /**
20899      * @cfg {String} cls A CSS class to apply to the field's underlying element.
20900      */
20901
20902         // private ??
20903         initComponent : function(){
20904         Roo.form.Field.superclass.initComponent.call(this);
20905         this.addEvents({
20906             /**
20907              * @event focus
20908              * Fires when this field receives input focus.
20909              * @param {Roo.form.Field} this
20910              */
20911             focus : true,
20912             /**
20913              * @event blur
20914              * Fires when this field loses input focus.
20915              * @param {Roo.form.Field} this
20916              */
20917             blur : true,
20918             /**
20919              * @event specialkey
20920              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
20921              * {@link Roo.EventObject#getKey} to determine which key was pressed.
20922              * @param {Roo.form.Field} this
20923              * @param {Roo.EventObject} e The event object
20924              */
20925             specialkey : true,
20926             /**
20927              * @event change
20928              * Fires just before the field blurs if the field value has changed.
20929              * @param {Roo.form.Field} this
20930              * @param {Mixed} newValue The new value
20931              * @param {Mixed} oldValue The original value
20932              */
20933             change : true,
20934             /**
20935              * @event invalid
20936              * Fires after the field has been marked as invalid.
20937              * @param {Roo.form.Field} this
20938              * @param {String} msg The validation message
20939              */
20940             invalid : true,
20941             /**
20942              * @event valid
20943              * Fires after the field has been validated with no errors.
20944              * @param {Roo.form.Field} this
20945              */
20946             valid : true,
20947              /**
20948              * @event keyup
20949              * Fires after the key up
20950              * @param {Roo.form.Field} this
20951              * @param {Roo.EventObject}  e The event Object
20952              */
20953             keyup : true
20954         });
20955     },
20956
20957     /**
20958      * Returns the name attribute of the field if available
20959      * @return {String} name The field name
20960      */
20961     getName: function(){
20962          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
20963     },
20964
20965     // private
20966     onRender : function(ct, position){
20967         Roo.form.Field.superclass.onRender.call(this, ct, position);
20968         if(!this.el){
20969             var cfg = this.getAutoCreate();
20970             if(!cfg.name){
20971                 cfg.name = this.name || this.id;
20972             }
20973             if(this.inputType){
20974                 cfg.type = this.inputType;
20975             }
20976             this.el = ct.createChild(cfg, position);
20977         }
20978         var type = this.el.dom.type;
20979         if(type){
20980             if(type == 'password'){
20981                 type = 'text';
20982             }
20983             this.el.addClass('x-form-'+type);
20984         }
20985         if(this.readOnly){
20986             this.el.dom.readOnly = true;
20987         }
20988         if(this.tabIndex !== undefined){
20989             this.el.dom.setAttribute('tabIndex', this.tabIndex);
20990         }
20991
20992         this.el.addClass([this.fieldClass, this.cls]);
20993         this.initValue();
20994     },
20995
20996     /**
20997      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
20998      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
20999      * @return {Roo.form.Field} this
21000      */
21001     applyTo : function(target){
21002         this.allowDomMove = false;
21003         this.el = Roo.get(target);
21004         this.render(this.el.dom.parentNode);
21005         return this;
21006     },
21007
21008     // private
21009     initValue : function(){
21010         if(this.value !== undefined){
21011             this.setValue(this.value);
21012         }else if(this.el.dom.value.length > 0){
21013             this.setValue(this.el.dom.value);
21014         }
21015     },
21016
21017     /**
21018      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21019      */
21020     isDirty : function() {
21021         if(this.disabled) {
21022             return false;
21023         }
21024         return String(this.getValue()) !== String(this.originalValue);
21025     },
21026
21027     // private
21028     afterRender : function(){
21029         Roo.form.Field.superclass.afterRender.call(this);
21030         this.initEvents();
21031     },
21032
21033     // private
21034     fireKey : function(e){
21035         //Roo.log('field ' + e.getKey());
21036         if(e.isNavKeyPress()){
21037             this.fireEvent("specialkey", this, e);
21038         }
21039     },
21040
21041     /**
21042      * Resets the current field value to the originally loaded value and clears any validation messages
21043      */
21044     reset : function(){
21045         this.setValue(this.originalValue);
21046         this.clearInvalid();
21047     },
21048
21049     // private
21050     initEvents : function(){
21051         // safari killled keypress - so keydown is now used..
21052         this.el.on("keydown" , this.fireKey,  this);
21053         this.el.on("focus", this.onFocus,  this);
21054         this.el.on("blur", this.onBlur,  this);
21055         this.el.relayEvent('keyup', this);
21056
21057         // reference to original value for reset
21058         this.originalValue = this.getValue();
21059     },
21060
21061     // private
21062     onFocus : function(){
21063         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21064             this.el.addClass(this.focusClass);
21065         }
21066         if(!this.hasFocus){
21067             this.hasFocus = true;
21068             this.startValue = this.getValue();
21069             this.fireEvent("focus", this);
21070         }
21071     },
21072
21073     beforeBlur : Roo.emptyFn,
21074
21075     // private
21076     onBlur : function(){
21077         this.beforeBlur();
21078         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21079             this.el.removeClass(this.focusClass);
21080         }
21081         this.hasFocus = false;
21082         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21083             this.validate();
21084         }
21085         var v = this.getValue();
21086         if(String(v) !== String(this.startValue)){
21087             this.fireEvent('change', this, v, this.startValue);
21088         }
21089         this.fireEvent("blur", this);
21090     },
21091
21092     /**
21093      * Returns whether or not the field value is currently valid
21094      * @param {Boolean} preventMark True to disable marking the field invalid
21095      * @return {Boolean} True if the value is valid, else false
21096      */
21097     isValid : function(preventMark){
21098         if(this.disabled){
21099             return true;
21100         }
21101         var restore = this.preventMark;
21102         this.preventMark = preventMark === true;
21103         var v = this.validateValue(this.processValue(this.getRawValue()));
21104         this.preventMark = restore;
21105         return v;
21106     },
21107
21108     /**
21109      * Validates the field value
21110      * @return {Boolean} True if the value is valid, else false
21111      */
21112     validate : function(){
21113         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21114             this.clearInvalid();
21115             return true;
21116         }
21117         return false;
21118     },
21119
21120     processValue : function(value){
21121         return value;
21122     },
21123
21124     // private
21125     // Subclasses should provide the validation implementation by overriding this
21126     validateValue : function(value){
21127         return true;
21128     },
21129
21130     /**
21131      * Mark this field as invalid
21132      * @param {String} msg The validation message
21133      */
21134     markInvalid : function(msg){
21135         if(!this.rendered || this.preventMark){ // not rendered
21136             return;
21137         }
21138         this.el.addClass(this.invalidClass);
21139         msg = msg || this.invalidText;
21140         switch(this.msgTarget){
21141             case 'qtip':
21142                 this.el.dom.qtip = msg;
21143                 this.el.dom.qclass = 'x-form-invalid-tip';
21144                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21145                     Roo.QuickTips.enable();
21146                 }
21147                 break;
21148             case 'title':
21149                 this.el.dom.title = msg;
21150                 break;
21151             case 'under':
21152                 if(!this.errorEl){
21153                     var elp = this.el.findParent('.x-form-element', 5, true);
21154                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21155                     this.errorEl.setWidth(elp.getWidth(true)-20);
21156                 }
21157                 this.errorEl.update(msg);
21158                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21159                 break;
21160             case 'side':
21161                 if(!this.errorIcon){
21162                     var elp = this.el.findParent('.x-form-element', 5, true);
21163                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21164                 }
21165                 this.alignErrorIcon();
21166                 this.errorIcon.dom.qtip = msg;
21167                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21168                 this.errorIcon.show();
21169                 this.on('resize', this.alignErrorIcon, this);
21170                 break;
21171             default:
21172                 var t = Roo.getDom(this.msgTarget);
21173                 t.innerHTML = msg;
21174                 t.style.display = this.msgDisplay;
21175                 break;
21176         }
21177         this.fireEvent('invalid', this, msg);
21178     },
21179
21180     // private
21181     alignErrorIcon : function(){
21182         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21183     },
21184
21185     /**
21186      * Clear any invalid styles/messages for this field
21187      */
21188     clearInvalid : function(){
21189         if(!this.rendered || this.preventMark){ // not rendered
21190             return;
21191         }
21192         this.el.removeClass(this.invalidClass);
21193         switch(this.msgTarget){
21194             case 'qtip':
21195                 this.el.dom.qtip = '';
21196                 break;
21197             case 'title':
21198                 this.el.dom.title = '';
21199                 break;
21200             case 'under':
21201                 if(this.errorEl){
21202                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21203                 }
21204                 break;
21205             case 'side':
21206                 if(this.errorIcon){
21207                     this.errorIcon.dom.qtip = '';
21208                     this.errorIcon.hide();
21209                     this.un('resize', this.alignErrorIcon, this);
21210                 }
21211                 break;
21212             default:
21213                 var t = Roo.getDom(this.msgTarget);
21214                 t.innerHTML = '';
21215                 t.style.display = 'none';
21216                 break;
21217         }
21218         this.fireEvent('valid', this);
21219     },
21220
21221     /**
21222      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21223      * @return {Mixed} value The field value
21224      */
21225     getRawValue : function(){
21226         var v = this.el.getValue();
21227         if(v === this.emptyText){
21228             v = '';
21229         }
21230         return v;
21231     },
21232
21233     /**
21234      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21235      * @return {Mixed} value The field value
21236      */
21237     getValue : function(){
21238         var v = this.el.getValue();
21239         if(v === this.emptyText || v === undefined){
21240             v = '';
21241         }
21242         return v;
21243     },
21244
21245     /**
21246      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21247      * @param {Mixed} value The value to set
21248      */
21249     setRawValue : function(v){
21250         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21251     },
21252
21253     /**
21254      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21255      * @param {Mixed} value The value to set
21256      */
21257     setValue : function(v){
21258         this.value = v;
21259         if(this.rendered){
21260             this.el.dom.value = (v === null || v === undefined ? '' : v);
21261             this.validate();
21262         }
21263     },
21264
21265     adjustSize : function(w, h){
21266         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21267         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21268         return s;
21269     },
21270
21271     adjustWidth : function(tag, w){
21272         tag = tag.toLowerCase();
21273         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21274             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21275                 if(tag == 'input'){
21276                     return w + 2;
21277                 }
21278                 if(tag = 'textarea'){
21279                     return w-2;
21280                 }
21281             }else if(Roo.isOpera){
21282                 if(tag == 'input'){
21283                     return w + 2;
21284                 }
21285                 if(tag = 'textarea'){
21286                     return w-2;
21287                 }
21288             }
21289         }
21290         return w;
21291     }
21292 });
21293
21294
21295 // anything other than normal should be considered experimental
21296 Roo.form.Field.msgFx = {
21297     normal : {
21298         show: function(msgEl, f){
21299             msgEl.setDisplayed('block');
21300         },
21301
21302         hide : function(msgEl, f){
21303             msgEl.setDisplayed(false).update('');
21304         }
21305     },
21306
21307     slide : {
21308         show: function(msgEl, f){
21309             msgEl.slideIn('t', {stopFx:true});
21310         },
21311
21312         hide : function(msgEl, f){
21313             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21314         }
21315     },
21316
21317     slideRight : {
21318         show: function(msgEl, f){
21319             msgEl.fixDisplay();
21320             msgEl.alignTo(f.el, 'tl-tr');
21321             msgEl.slideIn('l', {stopFx:true});
21322         },
21323
21324         hide : function(msgEl, f){
21325             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21326         }
21327     }
21328 };/*
21329  * Based on:
21330  * Ext JS Library 1.1.1
21331  * Copyright(c) 2006-2007, Ext JS, LLC.
21332  *
21333  * Originally Released Under LGPL - original licence link has changed is not relivant.
21334  *
21335  * Fork - LGPL
21336  * <script type="text/javascript">
21337  */
21338  
21339
21340 /**
21341  * @class Roo.form.TextField
21342  * @extends Roo.form.Field
21343  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21344  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21345  * @constructor
21346  * Creates a new TextField
21347  * @param {Object} config Configuration options
21348  */
21349 Roo.form.TextField = function(config){
21350     Roo.form.TextField.superclass.constructor.call(this, config);
21351     this.addEvents({
21352         /**
21353          * @event autosize
21354          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21355          * according to the default logic, but this event provides a hook for the developer to apply additional
21356          * logic at runtime to resize the field if needed.
21357              * @param {Roo.form.Field} this This text field
21358              * @param {Number} width The new field width
21359              */
21360         autosize : true
21361     });
21362 };
21363
21364 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21365     /**
21366      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21367      */
21368     grow : false,
21369     /**
21370      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21371      */
21372     growMin : 30,
21373     /**
21374      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21375      */
21376     growMax : 800,
21377     /**
21378      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21379      */
21380     vtype : null,
21381     /**
21382      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21383      */
21384     maskRe : null,
21385     /**
21386      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21387      */
21388     disableKeyFilter : false,
21389     /**
21390      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21391      */
21392     allowBlank : true,
21393     /**
21394      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21395      */
21396     minLength : 0,
21397     /**
21398      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21399      */
21400     maxLength : Number.MAX_VALUE,
21401     /**
21402      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21403      */
21404     minLengthText : "The minimum length for this field is {0}",
21405     /**
21406      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21407      */
21408     maxLengthText : "The maximum length for this field is {0}",
21409     /**
21410      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21411      */
21412     selectOnFocus : false,
21413     /**
21414      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21415      */
21416     blankText : "This field is required",
21417     /**
21418      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21419      * If available, this function will be called only after the basic validators all return true, and will be passed the
21420      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21421      */
21422     validator : null,
21423     /**
21424      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21425      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21426      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21427      */
21428     regex : null,
21429     /**
21430      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21431      */
21432     regexText : "",
21433     /**
21434      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21435      */
21436     emptyText : null,
21437     /**
21438      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21439      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21440      */
21441     emptyClass : 'x-form-empty-field',
21442
21443     // private
21444     initEvents : function(){
21445         Roo.form.TextField.superclass.initEvents.call(this);
21446         if(this.validationEvent == 'keyup'){
21447             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21448             this.el.on('keyup', this.filterValidation, this);
21449         }
21450         else if(this.validationEvent !== false){
21451             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21452         }
21453         if(this.selectOnFocus || this.emptyText){
21454             this.on("focus", this.preFocus, this);
21455             if(this.emptyText){
21456                 this.on('blur', this.postBlur, this);
21457                 this.applyEmptyText();
21458             }
21459         }
21460         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21461             this.el.on("keypress", this.filterKeys, this);
21462         }
21463         if(this.grow){
21464             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21465             this.el.on("click", this.autoSize,  this);
21466         }
21467     },
21468
21469     processValue : function(value){
21470         if(this.stripCharsRe){
21471             var newValue = value.replace(this.stripCharsRe, '');
21472             if(newValue !== value){
21473                 this.setRawValue(newValue);
21474                 return newValue;
21475             }
21476         }
21477         return value;
21478     },
21479
21480     filterValidation : function(e){
21481         if(!e.isNavKeyPress()){
21482             this.validationTask.delay(this.validationDelay);
21483         }
21484     },
21485
21486     // private
21487     onKeyUp : function(e){
21488         if(!e.isNavKeyPress()){
21489             this.autoSize();
21490         }
21491     },
21492
21493     /**
21494      * Resets the current field value to the originally-loaded value and clears any validation messages.
21495      * Also adds emptyText and emptyClass if the original value was blank.
21496      */
21497     reset : function(){
21498         Roo.form.TextField.superclass.reset.call(this);
21499         this.applyEmptyText();
21500     },
21501
21502     applyEmptyText : function(){
21503         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21504             this.setRawValue(this.emptyText);
21505             this.el.addClass(this.emptyClass);
21506         }
21507     },
21508
21509     // private
21510     preFocus : function(){
21511         if(this.emptyText){
21512             if(this.el.dom.value == this.emptyText){
21513                 this.setRawValue('');
21514             }
21515             this.el.removeClass(this.emptyClass);
21516         }
21517         if(this.selectOnFocus){
21518             this.el.dom.select();
21519         }
21520     },
21521
21522     // private
21523     postBlur : function(){
21524         this.applyEmptyText();
21525     },
21526
21527     // private
21528     filterKeys : function(e){
21529         var k = e.getKey();
21530         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21531             return;
21532         }
21533         var c = e.getCharCode(), cc = String.fromCharCode(c);
21534         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21535             return;
21536         }
21537         if(!this.maskRe.test(cc)){
21538             e.stopEvent();
21539         }
21540     },
21541
21542     setValue : function(v){
21543         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21544             this.el.removeClass(this.emptyClass);
21545         }
21546         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21547         this.applyEmptyText();
21548         this.autoSize();
21549     },
21550
21551     /**
21552      * Validates a value according to the field's validation rules and marks the field as invalid
21553      * if the validation fails
21554      * @param {Mixed} value The value to validate
21555      * @return {Boolean} True if the value is valid, else false
21556      */
21557     validateValue : function(value){
21558         if(value.length < 1 || value === this.emptyText){ // if it's blank
21559              if(this.allowBlank){
21560                 this.clearInvalid();
21561                 return true;
21562              }else{
21563                 this.markInvalid(this.blankText);
21564                 return false;
21565              }
21566         }
21567         if(value.length < this.minLength){
21568             this.markInvalid(String.format(this.minLengthText, this.minLength));
21569             return false;
21570         }
21571         if(value.length > this.maxLength){
21572             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21573             return false;
21574         }
21575         if(this.vtype){
21576             var vt = Roo.form.VTypes;
21577             if(!vt[this.vtype](value, this)){
21578                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21579                 return false;
21580             }
21581         }
21582         if(typeof this.validator == "function"){
21583             var msg = this.validator(value);
21584             if(msg !== true){
21585                 this.markInvalid(msg);
21586                 return false;
21587             }
21588         }
21589         if(this.regex && !this.regex.test(value)){
21590             this.markInvalid(this.regexText);
21591             return false;
21592         }
21593         return true;
21594     },
21595
21596     /**
21597      * Selects text in this field
21598      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21599      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21600      */
21601     selectText : function(start, end){
21602         var v = this.getRawValue();
21603         if(v.length > 0){
21604             start = start === undefined ? 0 : start;
21605             end = end === undefined ? v.length : end;
21606             var d = this.el.dom;
21607             if(d.setSelectionRange){
21608                 d.setSelectionRange(start, end);
21609             }else if(d.createTextRange){
21610                 var range = d.createTextRange();
21611                 range.moveStart("character", start);
21612                 range.moveEnd("character", v.length-end);
21613                 range.select();
21614             }
21615         }
21616     },
21617
21618     /**
21619      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21620      * This only takes effect if grow = true, and fires the autosize event.
21621      */
21622     autoSize : function(){
21623         if(!this.grow || !this.rendered){
21624             return;
21625         }
21626         if(!this.metrics){
21627             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21628         }
21629         var el = this.el;
21630         var v = el.dom.value;
21631         var d = document.createElement('div');
21632         d.appendChild(document.createTextNode(v));
21633         v = d.innerHTML;
21634         d = null;
21635         v += "&#160;";
21636         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21637         this.el.setWidth(w);
21638         this.fireEvent("autosize", this, w);
21639     }
21640 });/*
21641  * Based on:
21642  * Ext JS Library 1.1.1
21643  * Copyright(c) 2006-2007, Ext JS, LLC.
21644  *
21645  * Originally Released Under LGPL - original licence link has changed is not relivant.
21646  *
21647  * Fork - LGPL
21648  * <script type="text/javascript">
21649  */
21650  
21651 /**
21652  * @class Roo.form.Hidden
21653  * @extends Roo.form.TextField
21654  * Simple Hidden element used on forms 
21655  * 
21656  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21657  * 
21658  * @constructor
21659  * Creates a new Hidden form element.
21660  * @param {Object} config Configuration options
21661  */
21662
21663
21664
21665 // easy hidden field...
21666 Roo.form.Hidden = function(config){
21667     Roo.form.Hidden.superclass.constructor.call(this, config);
21668 };
21669   
21670 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21671     fieldLabel:      '',
21672     inputType:      'hidden',
21673     width:          50,
21674     allowBlank:     true,
21675     labelSeparator: '',
21676     hidden:         true,
21677     itemCls :       'x-form-item-display-none'
21678
21679
21680 });
21681
21682
21683 /*
21684  * Based on:
21685  * Ext JS Library 1.1.1
21686  * Copyright(c) 2006-2007, Ext JS, LLC.
21687  *
21688  * Originally Released Under LGPL - original licence link has changed is not relivant.
21689  *
21690  * Fork - LGPL
21691  * <script type="text/javascript">
21692  */
21693  
21694 /**
21695  * @class Roo.form.TriggerField
21696  * @extends Roo.form.TextField
21697  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
21698  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
21699  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
21700  * for which you can provide a custom implementation.  For example:
21701  * <pre><code>
21702 var trigger = new Roo.form.TriggerField();
21703 trigger.onTriggerClick = myTriggerFn;
21704 trigger.applyTo('my-field');
21705 </code></pre>
21706  *
21707  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
21708  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
21709  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
21710  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
21711  * @constructor
21712  * Create a new TriggerField.
21713  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
21714  * to the base TextField)
21715  */
21716 Roo.form.TriggerField = function(config){
21717     this.mimicing = false;
21718     Roo.form.TriggerField.superclass.constructor.call(this, config);
21719 };
21720
21721 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
21722     /**
21723      * @cfg {String} triggerClass A CSS class to apply to the trigger
21724      */
21725     /**
21726      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21727      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
21728      */
21729     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
21730     /**
21731      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
21732      */
21733     hideTrigger:false,
21734
21735     /** @cfg {Boolean} grow @hide */
21736     /** @cfg {Number} growMin @hide */
21737     /** @cfg {Number} growMax @hide */
21738
21739     /**
21740      * @hide 
21741      * @method
21742      */
21743     autoSize: Roo.emptyFn,
21744     // private
21745     monitorTab : true,
21746     // private
21747     deferHeight : true,
21748
21749     
21750     actionMode : 'wrap',
21751     // private
21752     onResize : function(w, h){
21753         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
21754         if(typeof w == 'number'){
21755             var x = w - this.trigger.getWidth();
21756             this.el.setWidth(this.adjustWidth('input', x));
21757             this.trigger.setStyle('left', x+'px');
21758         }
21759     },
21760
21761     // private
21762     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21763
21764     // private
21765     getResizeEl : function(){
21766         return this.wrap;
21767     },
21768
21769     // private
21770     getPositionEl : function(){
21771         return this.wrap;
21772     },
21773
21774     // private
21775     alignErrorIcon : function(){
21776         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
21777     },
21778
21779     // private
21780     onRender : function(ct, position){
21781         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
21782         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
21783         this.trigger = this.wrap.createChild(this.triggerConfig ||
21784                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
21785         if(this.hideTrigger){
21786             this.trigger.setDisplayed(false);
21787         }
21788         this.initTrigger();
21789         if(!this.width){
21790             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
21791         }
21792     },
21793
21794     // private
21795     initTrigger : function(){
21796         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
21797         this.trigger.addClassOnOver('x-form-trigger-over');
21798         this.trigger.addClassOnClick('x-form-trigger-click');
21799     },
21800
21801     // private
21802     onDestroy : function(){
21803         if(this.trigger){
21804             this.trigger.removeAllListeners();
21805             this.trigger.remove();
21806         }
21807         if(this.wrap){
21808             this.wrap.remove();
21809         }
21810         Roo.form.TriggerField.superclass.onDestroy.call(this);
21811     },
21812
21813     // private
21814     onFocus : function(){
21815         Roo.form.TriggerField.superclass.onFocus.call(this);
21816         if(!this.mimicing){
21817             this.wrap.addClass('x-trigger-wrap-focus');
21818             this.mimicing = true;
21819             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
21820             if(this.monitorTab){
21821                 this.el.on("keydown", this.checkTab, this);
21822             }
21823         }
21824     },
21825
21826     // private
21827     checkTab : function(e){
21828         if(e.getKey() == e.TAB){
21829             this.triggerBlur();
21830         }
21831     },
21832
21833     // private
21834     onBlur : function(){
21835         // do nothing
21836     },
21837
21838     // private
21839     mimicBlur : function(e, t){
21840         if(!this.wrap.contains(t) && this.validateBlur()){
21841             this.triggerBlur();
21842         }
21843     },
21844
21845     // private
21846     triggerBlur : function(){
21847         this.mimicing = false;
21848         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
21849         if(this.monitorTab){
21850             this.el.un("keydown", this.checkTab, this);
21851         }
21852         this.wrap.removeClass('x-trigger-wrap-focus');
21853         Roo.form.TriggerField.superclass.onBlur.call(this);
21854     },
21855
21856     // private
21857     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
21858     validateBlur : function(e, t){
21859         return true;
21860     },
21861
21862     // private
21863     onDisable : function(){
21864         Roo.form.TriggerField.superclass.onDisable.call(this);
21865         if(this.wrap){
21866             this.wrap.addClass('x-item-disabled');
21867         }
21868     },
21869
21870     // private
21871     onEnable : function(){
21872         Roo.form.TriggerField.superclass.onEnable.call(this);
21873         if(this.wrap){
21874             this.wrap.removeClass('x-item-disabled');
21875         }
21876     },
21877
21878     // private
21879     onShow : function(){
21880         var ae = this.getActionEl();
21881         
21882         if(ae){
21883             ae.dom.style.display = '';
21884             ae.dom.style.visibility = 'visible';
21885         }
21886     },
21887
21888     // private
21889     
21890     onHide : function(){
21891         var ae = this.getActionEl();
21892         ae.dom.style.display = 'none';
21893     },
21894
21895     /**
21896      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
21897      * by an implementing function.
21898      * @method
21899      * @param {EventObject} e
21900      */
21901     onTriggerClick : Roo.emptyFn
21902 });
21903
21904 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
21905 // to be extended by an implementing class.  For an example of implementing this class, see the custom
21906 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
21907 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
21908     initComponent : function(){
21909         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
21910
21911         this.triggerConfig = {
21912             tag:'span', cls:'x-form-twin-triggers', cn:[
21913             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
21914             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
21915         ]};
21916     },
21917
21918     getTrigger : function(index){
21919         return this.triggers[index];
21920     },
21921
21922     initTrigger : function(){
21923         var ts = this.trigger.select('.x-form-trigger', true);
21924         this.wrap.setStyle('overflow', 'hidden');
21925         var triggerField = this;
21926         ts.each(function(t, all, index){
21927             t.hide = function(){
21928                 var w = triggerField.wrap.getWidth();
21929                 this.dom.style.display = 'none';
21930                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21931             };
21932             t.show = function(){
21933                 var w = triggerField.wrap.getWidth();
21934                 this.dom.style.display = '';
21935                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
21936             };
21937             var triggerIndex = 'Trigger'+(index+1);
21938
21939             if(this['hide'+triggerIndex]){
21940                 t.dom.style.display = 'none';
21941             }
21942             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
21943             t.addClassOnOver('x-form-trigger-over');
21944             t.addClassOnClick('x-form-trigger-click');
21945         }, this);
21946         this.triggers = ts.elements;
21947     },
21948
21949     onTrigger1Click : Roo.emptyFn,
21950     onTrigger2Click : Roo.emptyFn
21951 });/*
21952  * Based on:
21953  * Ext JS Library 1.1.1
21954  * Copyright(c) 2006-2007, Ext JS, LLC.
21955  *
21956  * Originally Released Under LGPL - original licence link has changed is not relivant.
21957  *
21958  * Fork - LGPL
21959  * <script type="text/javascript">
21960  */
21961  
21962 /**
21963  * @class Roo.form.TextArea
21964  * @extends Roo.form.TextField
21965  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
21966  * support for auto-sizing.
21967  * @constructor
21968  * Creates a new TextArea
21969  * @param {Object} config Configuration options
21970  */
21971 Roo.form.TextArea = function(config){
21972     Roo.form.TextArea.superclass.constructor.call(this, config);
21973     // these are provided exchanges for backwards compat
21974     // minHeight/maxHeight were replaced by growMin/growMax to be
21975     // compatible with TextField growing config values
21976     if(this.minHeight !== undefined){
21977         this.growMin = this.minHeight;
21978     }
21979     if(this.maxHeight !== undefined){
21980         this.growMax = this.maxHeight;
21981     }
21982 };
21983
21984 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
21985     /**
21986      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
21987      */
21988     growMin : 60,
21989     /**
21990      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
21991      */
21992     growMax: 1000,
21993     /**
21994      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
21995      * in the field (equivalent to setting overflow: hidden, defaults to false)
21996      */
21997     preventScrollbars: false,
21998     /**
21999      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22000      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22001      */
22002
22003     // private
22004     onRender : function(ct, position){
22005         if(!this.el){
22006             this.defaultAutoCreate = {
22007                 tag: "textarea",
22008                 style:"width:300px;height:60px;",
22009                 autocomplete: "off"
22010             };
22011         }
22012         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22013         if(this.grow){
22014             this.textSizeEl = Roo.DomHelper.append(document.body, {
22015                 tag: "pre", cls: "x-form-grow-sizer"
22016             });
22017             if(this.preventScrollbars){
22018                 this.el.setStyle("overflow", "hidden");
22019             }
22020             this.el.setHeight(this.growMin);
22021         }
22022     },
22023
22024     onDestroy : function(){
22025         if(this.textSizeEl){
22026             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22027         }
22028         Roo.form.TextArea.superclass.onDestroy.call(this);
22029     },
22030
22031     // private
22032     onKeyUp : function(e){
22033         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22034             this.autoSize();
22035         }
22036     },
22037
22038     /**
22039      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22040      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22041      */
22042     autoSize : function(){
22043         if(!this.grow || !this.textSizeEl){
22044             return;
22045         }
22046         var el = this.el;
22047         var v = el.dom.value;
22048         var ts = this.textSizeEl;
22049
22050         ts.innerHTML = '';
22051         ts.appendChild(document.createTextNode(v));
22052         v = ts.innerHTML;
22053
22054         Roo.fly(ts).setWidth(this.el.getWidth());
22055         if(v.length < 1){
22056             v = "&#160;&#160;";
22057         }else{
22058             if(Roo.isIE){
22059                 v = v.replace(/\n/g, '<p>&#160;</p>');
22060             }
22061             v += "&#160;\n&#160;";
22062         }
22063         ts.innerHTML = v;
22064         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22065         if(h != this.lastHeight){
22066             this.lastHeight = h;
22067             this.el.setHeight(h);
22068             this.fireEvent("autosize", this, h);
22069         }
22070     }
22071 });/*
22072  * Based on:
22073  * Ext JS Library 1.1.1
22074  * Copyright(c) 2006-2007, Ext JS, LLC.
22075  *
22076  * Originally Released Under LGPL - original licence link has changed is not relivant.
22077  *
22078  * Fork - LGPL
22079  * <script type="text/javascript">
22080  */
22081  
22082
22083 /**
22084  * @class Roo.form.NumberField
22085  * @extends Roo.form.TextField
22086  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22087  * @constructor
22088  * Creates a new NumberField
22089  * @param {Object} config Configuration options
22090  */
22091 Roo.form.NumberField = function(config){
22092     Roo.form.NumberField.superclass.constructor.call(this, config);
22093 };
22094
22095 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22096     /**
22097      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22098      */
22099     fieldClass: "x-form-field x-form-num-field",
22100     /**
22101      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22102      */
22103     allowDecimals : true,
22104     /**
22105      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22106      */
22107     decimalSeparator : ".",
22108     /**
22109      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22110      */
22111     decimalPrecision : 2,
22112     /**
22113      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22114      */
22115     allowNegative : true,
22116     /**
22117      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22118      */
22119     minValue : Number.NEGATIVE_INFINITY,
22120     /**
22121      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22122      */
22123     maxValue : Number.MAX_VALUE,
22124     /**
22125      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22126      */
22127     minText : "The minimum value for this field is {0}",
22128     /**
22129      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22130      */
22131     maxText : "The maximum value for this field is {0}",
22132     /**
22133      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22134      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22135      */
22136     nanText : "{0} is not a valid number",
22137
22138     // private
22139     initEvents : function(){
22140         Roo.form.NumberField.superclass.initEvents.call(this);
22141         var allowed = "0123456789";
22142         if(this.allowDecimals){
22143             allowed += this.decimalSeparator;
22144         }
22145         if(this.allowNegative){
22146             allowed += "-";
22147         }
22148         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22149         var keyPress = function(e){
22150             var k = e.getKey();
22151             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22152                 return;
22153             }
22154             var c = e.getCharCode();
22155             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22156                 e.stopEvent();
22157             }
22158         };
22159         this.el.on("keypress", keyPress, this);
22160     },
22161
22162     // private
22163     validateValue : function(value){
22164         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22165             return false;
22166         }
22167         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22168              return true;
22169         }
22170         var num = this.parseValue(value);
22171         if(isNaN(num)){
22172             this.markInvalid(String.format(this.nanText, value));
22173             return false;
22174         }
22175         if(num < this.minValue){
22176             this.markInvalid(String.format(this.minText, this.minValue));
22177             return false;
22178         }
22179         if(num > this.maxValue){
22180             this.markInvalid(String.format(this.maxText, this.maxValue));
22181             return false;
22182         }
22183         return true;
22184     },
22185
22186     getValue : function(){
22187         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22188     },
22189
22190     // private
22191     parseValue : function(value){
22192         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22193         return isNaN(value) ? '' : value;
22194     },
22195
22196     // private
22197     fixPrecision : function(value){
22198         var nan = isNaN(value);
22199         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22200             return nan ? '' : value;
22201         }
22202         return parseFloat(value).toFixed(this.decimalPrecision);
22203     },
22204
22205     setValue : function(v){
22206         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22207     },
22208
22209     // private
22210     decimalPrecisionFcn : function(v){
22211         return Math.floor(v);
22212     },
22213
22214     beforeBlur : function(){
22215         var v = this.parseValue(this.getRawValue());
22216         if(v){
22217             this.setValue(this.fixPrecision(v));
22218         }
22219     }
22220 });/*
22221  * Based on:
22222  * Ext JS Library 1.1.1
22223  * Copyright(c) 2006-2007, Ext JS, LLC.
22224  *
22225  * Originally Released Under LGPL - original licence link has changed is not relivant.
22226  *
22227  * Fork - LGPL
22228  * <script type="text/javascript">
22229  */
22230  
22231 /**
22232  * @class Roo.form.DateField
22233  * @extends Roo.form.TriggerField
22234  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22235 * @constructor
22236 * Create a new DateField
22237 * @param {Object} config
22238  */
22239 Roo.form.DateField = function(config){
22240     Roo.form.DateField.superclass.constructor.call(this, config);
22241     
22242       this.addEvents({
22243          
22244         /**
22245          * @event select
22246          * Fires when a date is selected
22247              * @param {Roo.form.DateField} combo This combo box
22248              * @param {Date} date The date selected
22249              */
22250         'select' : true
22251          
22252     });
22253     
22254     
22255     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22256     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22257     this.ddMatch = null;
22258     if(this.disabledDates){
22259         var dd = this.disabledDates;
22260         var re = "(?:";
22261         for(var i = 0; i < dd.length; i++){
22262             re += dd[i];
22263             if(i != dd.length-1) re += "|";
22264         }
22265         this.ddMatch = new RegExp(re + ")");
22266     }
22267 };
22268
22269 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22270     /**
22271      * @cfg {String} format
22272      * The default date format string which can be overriden for localization support.  The format must be
22273      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22274      */
22275     format : "m/d/y",
22276     /**
22277      * @cfg {String} altFormats
22278      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22279      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22280      */
22281     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22282     /**
22283      * @cfg {Array} disabledDays
22284      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22285      */
22286     disabledDays : null,
22287     /**
22288      * @cfg {String} disabledDaysText
22289      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22290      */
22291     disabledDaysText : "Disabled",
22292     /**
22293      * @cfg {Array} disabledDates
22294      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22295      * expression so they are very powerful. Some examples:
22296      * <ul>
22297      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22298      * <li>["03/08", "09/16"] would disable those days for every year</li>
22299      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22300      * <li>["03/../2006"] would disable every day in March 2006</li>
22301      * <li>["^03"] would disable every day in every March</li>
22302      * </ul>
22303      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22304      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22305      */
22306     disabledDates : null,
22307     /**
22308      * @cfg {String} disabledDatesText
22309      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22310      */
22311     disabledDatesText : "Disabled",
22312     /**
22313      * @cfg {Date/String} minValue
22314      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22315      * valid format (defaults to null).
22316      */
22317     minValue : null,
22318     /**
22319      * @cfg {Date/String} maxValue
22320      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22321      * valid format (defaults to null).
22322      */
22323     maxValue : null,
22324     /**
22325      * @cfg {String} minText
22326      * The error text to display when the date in the cell is before minValue (defaults to
22327      * 'The date in this field must be after {minValue}').
22328      */
22329     minText : "The date in this field must be equal to or after {0}",
22330     /**
22331      * @cfg {String} maxText
22332      * The error text to display when the date in the cell is after maxValue (defaults to
22333      * 'The date in this field must be before {maxValue}').
22334      */
22335     maxText : "The date in this field must be equal to or before {0}",
22336     /**
22337      * @cfg {String} invalidText
22338      * The error text to display when the date in the field is invalid (defaults to
22339      * '{value} is not a valid date - it must be in the format {format}').
22340      */
22341     invalidText : "{0} is not a valid date - it must be in the format {1}",
22342     /**
22343      * @cfg {String} triggerClass
22344      * An additional CSS class used to style the trigger button.  The trigger will always get the
22345      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22346      * which displays a calendar icon).
22347      */
22348     triggerClass : 'x-form-date-trigger',
22349     
22350
22351     /**
22352      * @cfg {bool} useIso
22353      * if enabled, then the date field will use a hidden field to store the 
22354      * real value as iso formated date. default (false)
22355      */ 
22356     useIso : false,
22357     /**
22358      * @cfg {String/Object} autoCreate
22359      * A DomHelper element spec, or true for a default element spec (defaults to
22360      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22361      */ 
22362     // private
22363     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22364     
22365     // private
22366     hiddenField: false,
22367     
22368     onRender : function(ct, position)
22369     {
22370         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22371         if (this.useIso) {
22372             this.el.dom.removeAttribute('name'); 
22373             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22374                     'before', true);
22375             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22376             // prevent input submission
22377             this.hiddenName = this.name;
22378         }
22379             
22380             
22381     },
22382     
22383     // private
22384     validateValue : function(value)
22385     {
22386         value = this.formatDate(value);
22387         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22388             return false;
22389         }
22390         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22391              return true;
22392         }
22393         var svalue = value;
22394         value = this.parseDate(value);
22395         if(!value){
22396             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22397             return false;
22398         }
22399         var time = value.getTime();
22400         if(this.minValue && time < this.minValue.getTime()){
22401             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22402             return false;
22403         }
22404         if(this.maxValue && time > this.maxValue.getTime()){
22405             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22406             return false;
22407         }
22408         if(this.disabledDays){
22409             var day = value.getDay();
22410             for(var i = 0; i < this.disabledDays.length; i++) {
22411                 if(day === this.disabledDays[i]){
22412                     this.markInvalid(this.disabledDaysText);
22413                     return false;
22414                 }
22415             }
22416         }
22417         var fvalue = this.formatDate(value);
22418         if(this.ddMatch && this.ddMatch.test(fvalue)){
22419             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22420             return false;
22421         }
22422         return true;
22423     },
22424
22425     // private
22426     // Provides logic to override the default TriggerField.validateBlur which just returns true
22427     validateBlur : function(){
22428         return !this.menu || !this.menu.isVisible();
22429     },
22430
22431     /**
22432      * Returns the current date value of the date field.
22433      * @return {Date} The date value
22434      */
22435     getValue : function(){
22436         
22437         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22438     },
22439
22440     /**
22441      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22442      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22443      * (the default format used is "m/d/y").
22444      * <br />Usage:
22445      * <pre><code>
22446 //All of these calls set the same date value (May 4, 2006)
22447
22448 //Pass a date object:
22449 var dt = new Date('5/4/06');
22450 dateField.setValue(dt);
22451
22452 //Pass a date string (default format):
22453 dateField.setValue('5/4/06');
22454
22455 //Pass a date string (custom format):
22456 dateField.format = 'Y-m-d';
22457 dateField.setValue('2006-5-4');
22458 </code></pre>
22459      * @param {String/Date} date The date or valid date string
22460      */
22461     setValue : function(date){
22462         if (this.hiddenField) {
22463             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22464         }
22465         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22466     },
22467
22468     // private
22469     parseDate : function(value){
22470         if(!value || value instanceof Date){
22471             return value;
22472         }
22473         var v = Date.parseDate(value, this.format);
22474         if(!v && this.altFormats){
22475             if(!this.altFormatsArray){
22476                 this.altFormatsArray = this.altFormats.split("|");
22477             }
22478             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22479                 v = Date.parseDate(value, this.altFormatsArray[i]);
22480             }
22481         }
22482         return v;
22483     },
22484
22485     // private
22486     formatDate : function(date, fmt){
22487         return (!date || !(date instanceof Date)) ?
22488                date : date.dateFormat(fmt || this.format);
22489     },
22490
22491     // private
22492     menuListeners : {
22493         select: function(m, d){
22494             this.setValue(d);
22495             this.fireEvent('select', this, d);
22496         },
22497         show : function(){ // retain focus styling
22498             this.onFocus();
22499         },
22500         hide : function(){
22501             this.focus.defer(10, this);
22502             var ml = this.menuListeners;
22503             this.menu.un("select", ml.select,  this);
22504             this.menu.un("show", ml.show,  this);
22505             this.menu.un("hide", ml.hide,  this);
22506         }
22507     },
22508
22509     // private
22510     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22511     onTriggerClick : function(){
22512         if(this.disabled){
22513             return;
22514         }
22515         if(this.menu == null){
22516             this.menu = new Roo.menu.DateMenu();
22517         }
22518         Roo.apply(this.menu.picker,  {
22519             showClear: this.allowBlank,
22520             minDate : this.minValue,
22521             maxDate : this.maxValue,
22522             disabledDatesRE : this.ddMatch,
22523             disabledDatesText : this.disabledDatesText,
22524             disabledDays : this.disabledDays,
22525             disabledDaysText : this.disabledDaysText,
22526             format : this.format,
22527             minText : String.format(this.minText, this.formatDate(this.minValue)),
22528             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22529         });
22530         this.menu.on(Roo.apply({}, this.menuListeners, {
22531             scope:this
22532         }));
22533         this.menu.picker.setValue(this.getValue() || new Date());
22534         this.menu.show(this.el, "tl-bl?");
22535     },
22536
22537     beforeBlur : function(){
22538         var v = this.parseDate(this.getRawValue());
22539         if(v){
22540             this.setValue(v);
22541         }
22542     }
22543
22544     /** @cfg {Boolean} grow @hide */
22545     /** @cfg {Number} growMin @hide */
22546     /** @cfg {Number} growMax @hide */
22547     /**
22548      * @hide
22549      * @method autoSize
22550      */
22551 });/*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561  
22562
22563 /**
22564  * @class Roo.form.ComboBox
22565  * @extends Roo.form.TriggerField
22566  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22567  * @constructor
22568  * Create a new ComboBox.
22569  * @param {Object} config Configuration options
22570  */
22571 Roo.form.ComboBox = function(config){
22572     Roo.form.ComboBox.superclass.constructor.call(this, config);
22573     this.addEvents({
22574         /**
22575          * @event expand
22576          * Fires when the dropdown list is expanded
22577              * @param {Roo.form.ComboBox} combo This combo box
22578              */
22579         'expand' : true,
22580         /**
22581          * @event collapse
22582          * Fires when the dropdown list is collapsed
22583              * @param {Roo.form.ComboBox} combo This combo box
22584              */
22585         'collapse' : true,
22586         /**
22587          * @event beforeselect
22588          * Fires before a list item is selected. Return false to cancel the selection.
22589              * @param {Roo.form.ComboBox} combo This combo box
22590              * @param {Roo.data.Record} record The data record returned from the underlying store
22591              * @param {Number} index The index of the selected item in the dropdown list
22592              */
22593         'beforeselect' : true,
22594         /**
22595          * @event select
22596          * Fires when a list item is selected
22597              * @param {Roo.form.ComboBox} combo This combo box
22598              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22599              * @param {Number} index The index of the selected item in the dropdown list
22600              */
22601         'select' : true,
22602         /**
22603          * @event beforequery
22604          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22605          * The event object passed has these properties:
22606              * @param {Roo.form.ComboBox} combo This combo box
22607              * @param {String} query The query
22608              * @param {Boolean} forceAll true to force "all" query
22609              * @param {Boolean} cancel true to cancel the query
22610              * @param {Object} e The query event object
22611              */
22612         'beforequery': true,
22613          /**
22614          * @event add
22615          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22616              * @param {Roo.form.ComboBox} combo This combo box
22617              */
22618         'add' : true,
22619         /**
22620          * @event edit
22621          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22622              * @param {Roo.form.ComboBox} combo This combo box
22623              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22624              */
22625         'edit' : true
22626         
22627         
22628     });
22629     if(this.transform){
22630         this.allowDomMove = false;
22631         var s = Roo.getDom(this.transform);
22632         if(!this.hiddenName){
22633             this.hiddenName = s.name;
22634         }
22635         if(!this.store){
22636             this.mode = 'local';
22637             var d = [], opts = s.options;
22638             for(var i = 0, len = opts.length;i < len; i++){
22639                 var o = opts[i];
22640                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22641                 if(o.selected) {
22642                     this.value = value;
22643                 }
22644                 d.push([value, o.text]);
22645             }
22646             this.store = new Roo.data.SimpleStore({
22647                 'id': 0,
22648                 fields: ['value', 'text'],
22649                 data : d
22650             });
22651             this.valueField = 'value';
22652             this.displayField = 'text';
22653         }
22654         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22655         if(!this.lazyRender){
22656             this.target = true;
22657             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22658             s.parentNode.removeChild(s); // remove it
22659             this.render(this.el.parentNode);
22660         }else{
22661             s.parentNode.removeChild(s); // remove it
22662         }
22663
22664     }
22665     if (this.store) {
22666         this.store = Roo.factory(this.store, Roo.data);
22667     }
22668     
22669     this.selectedIndex = -1;
22670     if(this.mode == 'local'){
22671         if(config.queryDelay === undefined){
22672             this.queryDelay = 10;
22673         }
22674         if(config.minChars === undefined){
22675             this.minChars = 0;
22676         }
22677     }
22678 };
22679
22680 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22681     /**
22682      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
22683      */
22684     /**
22685      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
22686      * rendering into an Roo.Editor, defaults to false)
22687      */
22688     /**
22689      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
22690      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
22691      */
22692     /**
22693      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
22694      */
22695     /**
22696      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
22697      * the dropdown list (defaults to undefined, with no header element)
22698      */
22699
22700      /**
22701      * @cfg {String/Roo.Template} tpl The template to use to render the output
22702      */
22703      
22704     // private
22705     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
22706     /**
22707      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
22708      */
22709     listWidth: undefined,
22710     /**
22711      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
22712      * mode = 'remote' or 'text' if mode = 'local')
22713      */
22714     displayField: undefined,
22715     /**
22716      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
22717      * mode = 'remote' or 'value' if mode = 'local'). 
22718      * Note: use of a valueField requires the user make a selection
22719      * in order for a value to be mapped.
22720      */
22721     valueField: undefined,
22722     /**
22723      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
22724      * field's data value (defaults to the underlying DOM element's name)
22725      */
22726     hiddenName: undefined,
22727     /**
22728      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
22729      */
22730     listClass: '',
22731     /**
22732      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
22733      */
22734     selectedClass: 'x-combo-selected',
22735     /**
22736      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22737      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
22738      * which displays a downward arrow icon).
22739      */
22740     triggerClass : 'x-form-arrow-trigger',
22741     /**
22742      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
22743      */
22744     shadow:'sides',
22745     /**
22746      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
22747      * anchor positions (defaults to 'tl-bl')
22748      */
22749     listAlign: 'tl-bl?',
22750     /**
22751      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
22752      */
22753     maxHeight: 300,
22754     /**
22755      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
22756      * query specified by the allQuery config option (defaults to 'query')
22757      */
22758     triggerAction: 'query',
22759     /**
22760      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
22761      * (defaults to 4, does not apply if editable = false)
22762      */
22763     minChars : 4,
22764     /**
22765      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
22766      * delay (typeAheadDelay) if it matches a known value (defaults to false)
22767      */
22768     typeAhead: false,
22769     /**
22770      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
22771      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
22772      */
22773     queryDelay: 500,
22774     /**
22775      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
22776      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
22777      */
22778     pageSize: 0,
22779     /**
22780      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
22781      * when editable = true (defaults to false)
22782      */
22783     selectOnFocus:false,
22784     /**
22785      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
22786      */
22787     queryParam: 'query',
22788     /**
22789      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
22790      * when mode = 'remote' (defaults to 'Loading...')
22791      */
22792     loadingText: 'Loading...',
22793     /**
22794      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
22795      */
22796     resizable: false,
22797     /**
22798      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
22799      */
22800     handleHeight : 8,
22801     /**
22802      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
22803      * traditional select (defaults to true)
22804      */
22805     editable: true,
22806     /**
22807      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
22808      */
22809     allQuery: '',
22810     /**
22811      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
22812      */
22813     mode: 'remote',
22814     /**
22815      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
22816      * listWidth has a higher value)
22817      */
22818     minListWidth : 70,
22819     /**
22820      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
22821      * allow the user to set arbitrary text into the field (defaults to false)
22822      */
22823     forceSelection:false,
22824     /**
22825      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
22826      * if typeAhead = true (defaults to 250)
22827      */
22828     typeAheadDelay : 250,
22829     /**
22830      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
22831      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
22832      */
22833     valueNotFoundText : undefined,
22834     /**
22835      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
22836      */
22837     blockFocus : false,
22838     
22839     /**
22840      * @cfg {Boolean} disableClear Disable showing of clear button.
22841      */
22842     disableClear : false,
22843     /**
22844      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
22845      */
22846     alwaysQuery : false,
22847     
22848     //private
22849     addicon : false,
22850     editicon: false,
22851     
22852     
22853     // private
22854     onRender : function(ct, position){
22855         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
22856         if(this.hiddenName){
22857             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
22858                     'before', true);
22859             this.hiddenField.value =
22860                 this.hiddenValue !== undefined ? this.hiddenValue :
22861                 this.value !== undefined ? this.value : '';
22862
22863             // prevent input submission
22864             this.el.dom.removeAttribute('name');
22865         }
22866         if(Roo.isGecko){
22867             this.el.dom.setAttribute('autocomplete', 'off');
22868         }
22869
22870         var cls = 'x-combo-list';
22871
22872         this.list = new Roo.Layer({
22873             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
22874         });
22875
22876         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
22877         this.list.setWidth(lw);
22878         this.list.swallowEvent('mousewheel');
22879         this.assetHeight = 0;
22880
22881         if(this.title){
22882             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
22883             this.assetHeight += this.header.getHeight();
22884         }
22885
22886         this.innerList = this.list.createChild({cls:cls+'-inner'});
22887         this.innerList.on('mouseover', this.onViewOver, this);
22888         this.innerList.on('mousemove', this.onViewMove, this);
22889         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
22890         
22891         if(this.allowBlank && !this.pageSize && !this.disableClear){
22892             this.footer = this.list.createChild({cls:cls+'-ft'});
22893             this.pageTb = new Roo.Toolbar(this.footer);
22894            
22895         }
22896         if(this.pageSize){
22897             this.footer = this.list.createChild({cls:cls+'-ft'});
22898             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
22899                     {pageSize: this.pageSize});
22900             
22901         }
22902         
22903         if (this.pageTb && this.allowBlank && !this.disableClear) {
22904             var _this = this;
22905             this.pageTb.add(new Roo.Toolbar.Fill(), {
22906                 cls: 'x-btn-icon x-btn-clear',
22907                 text: '&#160;',
22908                 handler: function()
22909                 {
22910                     _this.collapse();
22911                     _this.clearValue();
22912                     _this.onSelect(false, -1);
22913                 }
22914             });
22915         }
22916         if (this.footer) {
22917             this.assetHeight += this.footer.getHeight();
22918         }
22919         
22920
22921         if(!this.tpl){
22922             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
22923         }
22924
22925         this.view = new Roo.View(this.innerList, this.tpl, {
22926             singleSelect:true, store: this.store, selectedClass: this.selectedClass
22927         });
22928
22929         this.view.on('click', this.onViewClick, this);
22930
22931         this.store.on('beforeload', this.onBeforeLoad, this);
22932         this.store.on('load', this.onLoad, this);
22933         this.store.on('loadexception', this.collapse, this);
22934
22935         if(this.resizable){
22936             this.resizer = new Roo.Resizable(this.list,  {
22937                pinned:true, handles:'se'
22938             });
22939             this.resizer.on('resize', function(r, w, h){
22940                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
22941                 this.listWidth = w;
22942                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
22943                 this.restrictHeight();
22944             }, this);
22945             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
22946         }
22947         if(!this.editable){
22948             this.editable = true;
22949             this.setEditable(false);
22950         }  
22951         
22952         
22953         if (typeof(this.events.add.listeners) != 'undefined') {
22954             
22955             this.addicon = this.wrap.createChild(
22956                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
22957        
22958             this.addicon.on('click', function(e) {
22959                 this.fireEvent('add', this);
22960             }, this);
22961         }
22962         if (typeof(this.events.edit.listeners) != 'undefined') {
22963             
22964             this.editicon = this.wrap.createChild(
22965                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
22966             if (this.addicon) {
22967                 this.editicon.setStyle('margin-left', '40px');
22968             }
22969             this.editicon.on('click', function(e) {
22970                 
22971                 // we fire even  if inothing is selected..
22972                 this.fireEvent('edit', this, this.lastData );
22973                 
22974             }, this);
22975         }
22976         
22977         
22978         
22979     },
22980
22981     // private
22982     initEvents : function(){
22983         Roo.form.ComboBox.superclass.initEvents.call(this);
22984
22985         this.keyNav = new Roo.KeyNav(this.el, {
22986             "up" : function(e){
22987                 this.inKeyMode = true;
22988                 this.selectPrev();
22989             },
22990
22991             "down" : function(e){
22992                 if(!this.isExpanded()){
22993                     this.onTriggerClick();
22994                 }else{
22995                     this.inKeyMode = true;
22996                     this.selectNext();
22997                 }
22998             },
22999
23000             "enter" : function(e){
23001                 this.onViewClick();
23002                 //return true;
23003             },
23004
23005             "esc" : function(e){
23006                 this.collapse();
23007             },
23008
23009             "tab" : function(e){
23010                 this.onViewClick(false);
23011                 return true;
23012             },
23013
23014             scope : this,
23015
23016             doRelay : function(foo, bar, hname){
23017                 if(hname == 'down' || this.scope.isExpanded()){
23018                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23019                 }
23020                 return true;
23021             },
23022
23023             forceKeyDown: true
23024         });
23025         this.queryDelay = Math.max(this.queryDelay || 10,
23026                 this.mode == 'local' ? 10 : 250);
23027         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23028         if(this.typeAhead){
23029             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23030         }
23031         if(this.editable !== false){
23032             this.el.on("keyup", this.onKeyUp, this);
23033         }
23034         if(this.forceSelection){
23035             this.on('blur', this.doForce, this);
23036         }
23037     },
23038
23039     onDestroy : function(){
23040         if(this.view){
23041             this.view.setStore(null);
23042             this.view.el.removeAllListeners();
23043             this.view.el.remove();
23044             this.view.purgeListeners();
23045         }
23046         if(this.list){
23047             this.list.destroy();
23048         }
23049         if(this.store){
23050             this.store.un('beforeload', this.onBeforeLoad, this);
23051             this.store.un('load', this.onLoad, this);
23052             this.store.un('loadexception', this.collapse, this);
23053         }
23054         Roo.form.ComboBox.superclass.onDestroy.call(this);
23055     },
23056
23057     // private
23058     fireKey : function(e){
23059         if(e.isNavKeyPress() && !this.list.isVisible()){
23060             this.fireEvent("specialkey", this, e);
23061         }
23062     },
23063
23064     // private
23065     onResize: function(w, h){
23066         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23067         
23068         if(typeof w != 'number'){
23069             // we do not handle it!?!?
23070             return;
23071         }
23072         var tw = this.trigger.getWidth();
23073         tw += this.addicon ? this.addicon.getWidth() : 0;
23074         tw += this.editicon ? this.editicon.getWidth() : 0;
23075         var x = w - tw;
23076         this.el.setWidth( this.adjustWidth('input', x));
23077             
23078         this.trigger.setStyle('left', x+'px');
23079         
23080         if(this.list && this.listWidth === undefined){
23081             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23082             this.list.setWidth(lw);
23083             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23084         }
23085         
23086     
23087         
23088     },
23089
23090     /**
23091      * Allow or prevent the user from directly editing the field text.  If false is passed,
23092      * the user will only be able to select from the items defined in the dropdown list.  This method
23093      * is the runtime equivalent of setting the 'editable' config option at config time.
23094      * @param {Boolean} value True to allow the user to directly edit the field text
23095      */
23096     setEditable : function(value){
23097         if(value == this.editable){
23098             return;
23099         }
23100         this.editable = value;
23101         if(!value){
23102             this.el.dom.setAttribute('readOnly', true);
23103             this.el.on('mousedown', this.onTriggerClick,  this);
23104             this.el.addClass('x-combo-noedit');
23105         }else{
23106             this.el.dom.setAttribute('readOnly', false);
23107             this.el.un('mousedown', this.onTriggerClick,  this);
23108             this.el.removeClass('x-combo-noedit');
23109         }
23110     },
23111
23112     // private
23113     onBeforeLoad : function(){
23114         if(!this.hasFocus){
23115             return;
23116         }
23117         this.innerList.update(this.loadingText ?
23118                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23119         this.restrictHeight();
23120         this.selectedIndex = -1;
23121     },
23122
23123     // private
23124     onLoad : function(){
23125         if(!this.hasFocus){
23126             return;
23127         }
23128         if(this.store.getCount() > 0){
23129             this.expand();
23130             this.restrictHeight();
23131             if(this.lastQuery == this.allQuery){
23132                 if(this.editable){
23133                     this.el.dom.select();
23134                 }
23135                 if(!this.selectByValue(this.value, true)){
23136                     this.select(0, true);
23137                 }
23138             }else{
23139                 this.selectNext();
23140                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23141                     this.taTask.delay(this.typeAheadDelay);
23142                 }
23143             }
23144         }else{
23145             this.onEmptyResults();
23146         }
23147         //this.el.focus();
23148     },
23149
23150     // private
23151     onTypeAhead : function(){
23152         if(this.store.getCount() > 0){
23153             var r = this.store.getAt(0);
23154             var newValue = r.data[this.displayField];
23155             var len = newValue.length;
23156             var selStart = this.getRawValue().length;
23157             if(selStart != len){
23158                 this.setRawValue(newValue);
23159                 this.selectText(selStart, newValue.length);
23160             }
23161         }
23162     },
23163
23164     // private
23165     onSelect : function(record, index){
23166         if(this.fireEvent('beforeselect', this, record, index) !== false){
23167             this.setFromData(index > -1 ? record.data : false);
23168             this.collapse();
23169             this.fireEvent('select', this, record, index);
23170         }
23171     },
23172
23173     /**
23174      * Returns the currently selected field value or empty string if no value is set.
23175      * @return {String} value The selected value
23176      */
23177     getValue : function(){
23178         if(this.valueField){
23179             return typeof this.value != 'undefined' ? this.value : '';
23180         }else{
23181             return Roo.form.ComboBox.superclass.getValue.call(this);
23182         }
23183     },
23184
23185     /**
23186      * Clears any text/value currently set in the field
23187      */
23188     clearValue : function(){
23189         if(this.hiddenField){
23190             this.hiddenField.value = '';
23191         }
23192         this.value = '';
23193         this.setRawValue('');
23194         this.lastSelectionText = '';
23195         this.applyEmptyText();
23196     },
23197
23198     /**
23199      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23200      * will be displayed in the field.  If the value does not match the data value of an existing item,
23201      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23202      * Otherwise the field will be blank (although the value will still be set).
23203      * @param {String} value The value to match
23204      */
23205     setValue : function(v){
23206         var text = v;
23207         if(this.valueField){
23208             var r = this.findRecord(this.valueField, v);
23209             if(r){
23210                 text = r.data[this.displayField];
23211             }else if(this.valueNotFoundText !== undefined){
23212                 text = this.valueNotFoundText;
23213             }
23214         }
23215         this.lastSelectionText = text;
23216         if(this.hiddenField){
23217             this.hiddenField.value = v;
23218         }
23219         Roo.form.ComboBox.superclass.setValue.call(this, text);
23220         this.value = v;
23221     },
23222     /**
23223      * @property {Object} the last set data for the element
23224      */
23225     
23226     lastData : false,
23227     /**
23228      * Sets the value of the field based on a object which is related to the record format for the store.
23229      * @param {Object} value the value to set as. or false on reset?
23230      */
23231     setFromData : function(o){
23232         var dv = ''; // display value
23233         var vv = ''; // value value..
23234         this.lastData = o;
23235         if (this.displayField) {
23236             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23237         } else {
23238             // this is an error condition!!!
23239             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23240         }
23241         
23242         if(this.valueField){
23243             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23244         }
23245         if(this.hiddenField){
23246             this.hiddenField.value = vv;
23247             
23248             this.lastSelectionText = dv;
23249             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23250             this.value = vv;
23251             return;
23252         }
23253         // no hidden field.. - we store the value in 'value', but still display
23254         // display field!!!!
23255         this.lastSelectionText = dv;
23256         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23257         this.value = vv;
23258         
23259         
23260     },
23261     // private
23262     reset : function(){
23263         // overridden so that last data is reset..
23264         this.setValue(this.originalValue);
23265         this.clearInvalid();
23266         this.lastData = false;
23267     },
23268     // private
23269     findRecord : function(prop, value){
23270         var record;
23271         if(this.store.getCount() > 0){
23272             this.store.each(function(r){
23273                 if(r.data[prop] == value){
23274                     record = r;
23275                     return false;
23276                 }
23277             });
23278         }
23279         return record;
23280     },
23281
23282     // private
23283     onViewMove : function(e, t){
23284         this.inKeyMode = false;
23285     },
23286
23287     // private
23288     onViewOver : function(e, t){
23289         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23290             return;
23291         }
23292         var item = this.view.findItemFromChild(t);
23293         if(item){
23294             var index = this.view.indexOf(item);
23295             this.select(index, false);
23296         }
23297     },
23298
23299     // private
23300     onViewClick : function(doFocus){
23301         var index = this.view.getSelectedIndexes()[0];
23302         var r = this.store.getAt(index);
23303         if(r){
23304             this.onSelect(r, index);
23305         }
23306         if(doFocus !== false && !this.blockFocus){
23307             this.el.focus();
23308         }
23309     },
23310
23311     // private
23312     restrictHeight : function(){
23313         this.innerList.dom.style.height = '';
23314         var inner = this.innerList.dom;
23315         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23316         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23317         this.list.beginUpdate();
23318         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23319         this.list.alignTo(this.el, this.listAlign);
23320         this.list.endUpdate();
23321     },
23322
23323     // private
23324     onEmptyResults : function(){
23325         this.collapse();
23326     },
23327
23328     /**
23329      * Returns true if the dropdown list is expanded, else false.
23330      */
23331     isExpanded : function(){
23332         return this.list.isVisible();
23333     },
23334
23335     /**
23336      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23337      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23338      * @param {String} value The data value of the item to select
23339      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23340      * selected item if it is not currently in view (defaults to true)
23341      * @return {Boolean} True if the value matched an item in the list, else false
23342      */
23343     selectByValue : function(v, scrollIntoView){
23344         if(v !== undefined && v !== null){
23345             var r = this.findRecord(this.valueField || this.displayField, v);
23346             if(r){
23347                 this.select(this.store.indexOf(r), scrollIntoView);
23348                 return true;
23349             }
23350         }
23351         return false;
23352     },
23353
23354     /**
23355      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23356      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23357      * @param {Number} index The zero-based index of the list item to select
23358      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23359      * selected item if it is not currently in view (defaults to true)
23360      */
23361     select : function(index, scrollIntoView){
23362         this.selectedIndex = index;
23363         this.view.select(index);
23364         if(scrollIntoView !== false){
23365             var el = this.view.getNode(index);
23366             if(el){
23367                 this.innerList.scrollChildIntoView(el, false);
23368             }
23369         }
23370     },
23371
23372     // private
23373     selectNext : function(){
23374         var ct = this.store.getCount();
23375         if(ct > 0){
23376             if(this.selectedIndex == -1){
23377                 this.select(0);
23378             }else if(this.selectedIndex < ct-1){
23379                 this.select(this.selectedIndex+1);
23380             }
23381         }
23382     },
23383
23384     // private
23385     selectPrev : function(){
23386         var ct = this.store.getCount();
23387         if(ct > 0){
23388             if(this.selectedIndex == -1){
23389                 this.select(0);
23390             }else if(this.selectedIndex != 0){
23391                 this.select(this.selectedIndex-1);
23392             }
23393         }
23394     },
23395
23396     // private
23397     onKeyUp : function(e){
23398         if(this.editable !== false && !e.isSpecialKey()){
23399             this.lastKey = e.getKey();
23400             this.dqTask.delay(this.queryDelay);
23401         }
23402     },
23403
23404     // private
23405     validateBlur : function(){
23406         return !this.list || !this.list.isVisible();   
23407     },
23408
23409     // private
23410     initQuery : function(){
23411         this.doQuery(this.getRawValue());
23412     },
23413
23414     // private
23415     doForce : function(){
23416         if(this.el.dom.value.length > 0){
23417             this.el.dom.value =
23418                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23419             this.applyEmptyText();
23420         }
23421     },
23422
23423     /**
23424      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23425      * query allowing the query action to be canceled if needed.
23426      * @param {String} query The SQL query to execute
23427      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23428      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23429      * saved in the current store (defaults to false)
23430      */
23431     doQuery : function(q, forceAll){
23432         if(q === undefined || q === null){
23433             q = '';
23434         }
23435         var qe = {
23436             query: q,
23437             forceAll: forceAll,
23438             combo: this,
23439             cancel:false
23440         };
23441         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23442             return false;
23443         }
23444         q = qe.query;
23445         forceAll = qe.forceAll;
23446         if(forceAll === true || (q.length >= this.minChars)){
23447             if(this.lastQuery != q || this.alwaysQuery){
23448                 this.lastQuery = q;
23449                 if(this.mode == 'local'){
23450                     this.selectedIndex = -1;
23451                     if(forceAll){
23452                         this.store.clearFilter();
23453                     }else{
23454                         this.store.filter(this.displayField, q);
23455                     }
23456                     this.onLoad();
23457                 }else{
23458                     this.store.baseParams[this.queryParam] = q;
23459                     this.store.load({
23460                         params: this.getParams(q)
23461                     });
23462                     this.expand();
23463                 }
23464             }else{
23465                 this.selectedIndex = -1;
23466                 this.onLoad();   
23467             }
23468         }
23469     },
23470
23471     // private
23472     getParams : function(q){
23473         var p = {};
23474         //p[this.queryParam] = q;
23475         if(this.pageSize){
23476             p.start = 0;
23477             p.limit = this.pageSize;
23478         }
23479         return p;
23480     },
23481
23482     /**
23483      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23484      */
23485     collapse : function(){
23486         if(!this.isExpanded()){
23487             return;
23488         }
23489         this.list.hide();
23490         Roo.get(document).un('mousedown', this.collapseIf, this);
23491         Roo.get(document).un('mousewheel', this.collapseIf, this);
23492         if (!this.editable) {
23493             Roo.get(document).un('keydown', this.listKeyPress, this);
23494         }
23495         this.fireEvent('collapse', this);
23496     },
23497
23498     // private
23499     collapseIf : function(e){
23500         if(!e.within(this.wrap) && !e.within(this.list)){
23501             this.collapse();
23502         }
23503     },
23504
23505     /**
23506      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23507      */
23508     expand : function(){
23509         if(this.isExpanded() || !this.hasFocus){
23510             return;
23511         }
23512         this.list.alignTo(this.el, this.listAlign);
23513         this.list.show();
23514         Roo.get(document).on('mousedown', this.collapseIf, this);
23515         Roo.get(document).on('mousewheel', this.collapseIf, this);
23516         if (!this.editable) {
23517             Roo.get(document).on('keydown', this.listKeyPress, this);
23518         }
23519         
23520         this.fireEvent('expand', this);
23521     },
23522
23523     // private
23524     // Implements the default empty TriggerField.onTriggerClick function
23525     onTriggerClick : function(){
23526         if(this.disabled){
23527             return;
23528         }
23529         if(this.isExpanded()){
23530             this.collapse();
23531             if (!this.blockFocus) {
23532                 this.el.focus();
23533             }
23534             
23535         }else {
23536             this.hasFocus = true;
23537             if(this.triggerAction == 'all') {
23538                 this.doQuery(this.allQuery, true);
23539             } else {
23540                 this.doQuery(this.getRawValue());
23541             }
23542             if (!this.blockFocus) {
23543                 this.el.focus();
23544             }
23545         }
23546     },
23547     listKeyPress : function(e)
23548     {
23549         //Roo.log('listkeypress');
23550         // scroll to first matching element based on key pres..
23551         if (e.isSpecialKey()) {
23552             return false;
23553         }
23554         var k = String.fromCharCode(e.getKey()).toUpperCase();
23555         //Roo.log(k);
23556         var match  = false;
23557         var csel = this.view.getSelectedNodes();
23558         var cselitem = false;
23559         if (csel.length) {
23560             var ix = this.view.indexOf(csel[0]);
23561             cselitem  = this.store.getAt(ix);
23562             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23563                 cselitem = false;
23564             }
23565             
23566         }
23567         
23568         this.store.each(function(v) { 
23569             if (cselitem) {
23570                 // start at existing selection.
23571                 if (cselitem.id == v.id) {
23572                     cselitem = false;
23573                 }
23574                 return;
23575             }
23576                 
23577             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23578                 match = this.store.indexOf(v);
23579                 return false;
23580             }
23581         }, this);
23582         
23583         if (match === false) {
23584             return true; // no more action?
23585         }
23586         // scroll to?
23587         this.view.select(match);
23588         var sn = Roo.get(this.view.getSelectedNodes()[0])
23589         sn.scrollIntoView(sn.dom.parentNode, false);
23590     }
23591
23592     /** 
23593     * @cfg {Boolean} grow 
23594     * @hide 
23595     */
23596     /** 
23597     * @cfg {Number} growMin 
23598     * @hide 
23599     */
23600     /** 
23601     * @cfg {Number} growMax 
23602     * @hide 
23603     */
23604     /**
23605      * @hide
23606      * @method autoSize
23607      */
23608 });/*
23609  * Based on:
23610  * Ext JS Library 1.1.1
23611  * Copyright(c) 2006-2007, Ext JS, LLC.
23612  *
23613  * Originally Released Under LGPL - original licence link has changed is not relivant.
23614  *
23615  * Fork - LGPL
23616  * <script type="text/javascript">
23617  */
23618 /**
23619  * @class Roo.form.Checkbox
23620  * @extends Roo.form.Field
23621  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
23622  * @constructor
23623  * Creates a new Checkbox
23624  * @param {Object} config Configuration options
23625  */
23626 Roo.form.Checkbox = function(config){
23627     Roo.form.Checkbox.superclass.constructor.call(this, config);
23628     this.addEvents({
23629         /**
23630          * @event check
23631          * Fires when the checkbox is checked or unchecked.
23632              * @param {Roo.form.Checkbox} this This checkbox
23633              * @param {Boolean} checked The new checked value
23634              */
23635         check : true
23636     });
23637 };
23638
23639 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
23640     /**
23641      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
23642      */
23643     focusClass : undefined,
23644     /**
23645      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
23646      */
23647     fieldClass: "x-form-field",
23648     /**
23649      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
23650      */
23651     checked: false,
23652     /**
23653      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
23654      * {tag: "input", type: "checkbox", autocomplete: "off"})
23655      */
23656     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
23657     /**
23658      * @cfg {String} boxLabel The text that appears beside the checkbox
23659      */
23660     boxLabel : "",
23661     /**
23662      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
23663      */  
23664     inputValue : '1',
23665     /**
23666      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23667      */
23668      valueOff: '0', // value when not checked..
23669
23670     actionMode : 'viewEl', 
23671     //
23672     // private
23673     itemCls : 'x-menu-check-item x-form-item',
23674     groupClass : 'x-menu-group-item',
23675     inputType : 'hidden',
23676     
23677     
23678     inSetChecked: false, // check that we are not calling self...
23679     
23680     inputElement: false, // real input element?
23681     basedOn: false, // ????
23682     
23683     isFormField: true, // not sure where this is needed!!!!
23684
23685     onResize : function(){
23686         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
23687         if(!this.boxLabel){
23688             this.el.alignTo(this.wrap, 'c-c');
23689         }
23690     },
23691
23692     initEvents : function(){
23693         Roo.form.Checkbox.superclass.initEvents.call(this);
23694         this.el.on("click", this.onClick,  this);
23695         this.el.on("change", this.onClick,  this);
23696     },
23697
23698
23699     getResizeEl : function(){
23700         return this.wrap;
23701     },
23702
23703     getPositionEl : function(){
23704         return this.wrap;
23705     },
23706
23707     // private
23708     onRender : function(ct, position){
23709         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
23710         /*
23711         if(this.inputValue !== undefined){
23712             this.el.dom.value = this.inputValue;
23713         }
23714         */
23715         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
23716         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
23717         var viewEl = this.wrap.createChild({ 
23718             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
23719         this.viewEl = viewEl;   
23720         this.wrap.on('click', this.onClick,  this); 
23721         
23722         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
23723         this.el.on('propertychange', this.setFromHidden,  this);  //ie
23724         
23725         
23726         
23727         if(this.boxLabel){
23728             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
23729         //    viewEl.on('click', this.onClick,  this); 
23730         }
23731         //if(this.checked){
23732             this.setChecked(this.checked);
23733         //}else{
23734             //this.checked = this.el.dom;
23735         //}
23736
23737     },
23738
23739     // private
23740     initValue : Roo.emptyFn,
23741
23742     /**
23743      * Returns the checked state of the checkbox.
23744      * @return {Boolean} True if checked, else false
23745      */
23746     getValue : function(){
23747         if(this.el){
23748             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
23749         }
23750         return this.valueOff;
23751         
23752     },
23753
23754         // private
23755     onClick : function(){ 
23756         this.setChecked(!this.checked);
23757
23758         //if(this.el.dom.checked != this.checked){
23759         //    this.setValue(this.el.dom.checked);
23760        // }
23761     },
23762
23763     /**
23764      * Sets the checked state of the checkbox.
23765      * On is always based on a string comparison between inputValue and the param.
23766      * @param {Boolean/String} value - the value to set 
23767      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
23768      */
23769     setValue : function(v,suppressEvent){
23770         
23771         
23772         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
23773         //if(this.el && this.el.dom){
23774         //    this.el.dom.checked = this.checked;
23775         //    this.el.dom.defaultChecked = this.checked;
23776         //}
23777         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
23778         //this.fireEvent("check", this, this.checked);
23779     },
23780     // private..
23781     setChecked : function(state,suppressEvent)
23782     {
23783         if (this.inSetChecked) {
23784             this.checked = state;
23785             return;
23786         }
23787         
23788     
23789         if(this.wrap){
23790             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
23791         }
23792         this.checked = state;
23793         if(suppressEvent !== true){
23794             this.fireEvent('check', this, state);
23795         }
23796         this.inSetChecked = true;
23797         this.el.dom.value = state ? this.inputValue : this.valueOff;
23798         this.inSetChecked = false;
23799         
23800     },
23801     // handle setting of hidden value by some other method!!?!?
23802     setFromHidden: function()
23803     {
23804         if(!this.el){
23805             return;
23806         }
23807         //console.log("SET FROM HIDDEN");
23808         //alert('setFrom hidden');
23809         this.setValue(this.el.dom.value);
23810     },
23811     
23812     onDestroy : function()
23813     {
23814         if(this.viewEl){
23815             Roo.get(this.viewEl).remove();
23816         }
23817          
23818         Roo.form.Checkbox.superclass.onDestroy.call(this);
23819     }
23820
23821 });/*
23822  * Based on:
23823  * Ext JS Library 1.1.1
23824  * Copyright(c) 2006-2007, Ext JS, LLC.
23825  *
23826  * Originally Released Under LGPL - original licence link has changed is not relivant.
23827  *
23828  * Fork - LGPL
23829  * <script type="text/javascript">
23830  */
23831  
23832 /**
23833  * @class Roo.form.Radio
23834  * @extends Roo.form.Checkbox
23835  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
23836  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
23837  * @constructor
23838  * Creates a new Radio
23839  * @param {Object} config Configuration options
23840  */
23841 Roo.form.Radio = function(){
23842     Roo.form.Radio.superclass.constructor.apply(this, arguments);
23843 };
23844 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
23845     inputType: 'radio',
23846
23847     /**
23848      * If this radio is part of a group, it will return the selected value
23849      * @return {String}
23850      */
23851     getGroupValue : function(){
23852         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
23853     }
23854 });//<script type="text/javascript">
23855
23856 /*
23857  * Ext JS Library 1.1.1
23858  * Copyright(c) 2006-2007, Ext JS, LLC.
23859  * licensing@extjs.com
23860  * 
23861  * http://www.extjs.com/license
23862  */
23863  
23864  /*
23865   * 
23866   * Known bugs:
23867   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
23868   * - IE ? - no idea how much works there.
23869   * 
23870   * 
23871   * 
23872   */
23873  
23874
23875 /**
23876  * @class Ext.form.HtmlEditor
23877  * @extends Ext.form.Field
23878  * Provides a lightweight HTML Editor component.
23879  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
23880  * 
23881  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
23882  * supported by this editor.</b><br/><br/>
23883  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
23884  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23885  */
23886 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
23887       /**
23888      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23889      */
23890     toolbars : false,
23891     /**
23892      * @cfg {String} createLinkText The default text for the create link prompt
23893      */
23894     createLinkText : 'Please enter the URL for the link:',
23895     /**
23896      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23897      */
23898     defaultLinkValue : 'http:/'+'/',
23899    
23900     
23901     // id of frame..
23902     frameId: false,
23903     
23904     // private properties
23905     validationEvent : false,
23906     deferHeight: true,
23907     initialized : false,
23908     activated : false,
23909     sourceEditMode : false,
23910     onFocus : Roo.emptyFn,
23911     iframePad:3,
23912     hideMode:'offsets',
23913     defaultAutoCreate : {
23914         tag: "textarea",
23915         style:"width:500px;height:300px;",
23916         autocomplete: "off"
23917     },
23918
23919     // private
23920     initComponent : function(){
23921         this.addEvents({
23922             /**
23923              * @event initialize
23924              * Fires when the editor is fully initialized (including the iframe)
23925              * @param {HtmlEditor} this
23926              */
23927             initialize: true,
23928             /**
23929              * @event activate
23930              * Fires when the editor is first receives the focus. Any insertion must wait
23931              * until after this event.
23932              * @param {HtmlEditor} this
23933              */
23934             activate: true,
23935              /**
23936              * @event beforesync
23937              * Fires before the textarea is updated with content from the editor iframe. Return false
23938              * to cancel the sync.
23939              * @param {HtmlEditor} this
23940              * @param {String} html
23941              */
23942             beforesync: true,
23943              /**
23944              * @event beforepush
23945              * Fires before the iframe editor is updated with content from the textarea. Return false
23946              * to cancel the push.
23947              * @param {HtmlEditor} this
23948              * @param {String} html
23949              */
23950             beforepush: true,
23951              /**
23952              * @event sync
23953              * Fires when the textarea is updated with content from the editor iframe.
23954              * @param {HtmlEditor} this
23955              * @param {String} html
23956              */
23957             sync: true,
23958              /**
23959              * @event push
23960              * Fires when the iframe editor is updated with content from the textarea.
23961              * @param {HtmlEditor} this
23962              * @param {String} html
23963              */
23964             push: true,
23965              /**
23966              * @event editmodechange
23967              * Fires when the editor switches edit modes
23968              * @param {HtmlEditor} this
23969              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23970              */
23971             editmodechange: true,
23972             /**
23973              * @event editorevent
23974              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23975              * @param {HtmlEditor} this
23976              */
23977             editorevent: true
23978         })
23979     },
23980
23981     /**
23982      * Protected method that will not generally be called directly. It
23983      * is called when the editor creates its toolbar. Override this method if you need to
23984      * add custom toolbar buttons.
23985      * @param {HtmlEditor} editor
23986      */
23987     createToolbar : function(editor){
23988         if (!editor.toolbars || !editor.toolbars.length) {
23989             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
23990         }
23991         
23992         for (var i =0 ; i < editor.toolbars.length;i++) {
23993             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
23994             editor.toolbars[i].init(editor);
23995         }
23996          
23997         
23998     },
23999
24000     /**
24001      * Protected method that will not generally be called directly. It
24002      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24003      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24004      */
24005     getDocMarkup : function(){
24006         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
24007     },
24008
24009     // private
24010     onRender : function(ct, position){
24011         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24012         this.el.dom.style.border = '0 none';
24013         this.el.dom.setAttribute('tabIndex', -1);
24014         this.el.addClass('x-hidden');
24015         if(Roo.isIE){ // fix IE 1px bogus margin
24016             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24017         }
24018         this.wrap = this.el.wrap({
24019             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24020         });
24021
24022         this.frameId = Roo.id();
24023         this.createToolbar(this);
24024         
24025         
24026         
24027         
24028       
24029         
24030         var iframe = this.wrap.createChild({
24031             tag: 'iframe',
24032             id: this.frameId,
24033             name: this.frameId,
24034             frameBorder : 'no',
24035             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24036         });
24037         
24038        // console.log(iframe);
24039         //this.wrap.dom.appendChild(iframe);
24040
24041         this.iframe = iframe.dom;
24042
24043          this.assignDocWin();
24044         
24045         this.doc.designMode = 'on';
24046        
24047         this.doc.open();
24048         this.doc.write(this.getDocMarkup());
24049         this.doc.close();
24050
24051         
24052         var task = { // must defer to wait for browser to be ready
24053             run : function(){
24054                 //console.log("run task?" + this.doc.readyState);
24055                 this.assignDocWin();
24056                 if(this.doc.body || this.doc.readyState == 'complete'){
24057                     try {
24058                         this.doc.designMode="on";
24059                     } catch (e) {
24060                         return;
24061                     }
24062                     Roo.TaskMgr.stop(task);
24063                     this.initEditor.defer(10, this);
24064                 }
24065             },
24066             interval : 10,
24067             duration:10000,
24068             scope: this
24069         };
24070         Roo.TaskMgr.start(task);
24071
24072         if(!this.width){
24073             this.setSize(this.el.getSize());
24074         }
24075     },
24076
24077     // private
24078     onResize : function(w, h){
24079         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24080         if(this.el && this.iframe){
24081             if(typeof w == 'number'){
24082                 var aw = w - this.wrap.getFrameWidth('lr');
24083                 this.el.setWidth(this.adjustWidth('textarea', aw));
24084                 this.iframe.style.width = aw + 'px';
24085             }
24086             if(typeof h == 'number'){
24087                 var tbh = 0;
24088                 for (var i =0; i < this.toolbars.length;i++) {
24089                     // fixme - ask toolbars for heights?
24090                     tbh += this.toolbars[i].tb.el.getHeight();
24091                 }
24092                 
24093                 
24094                 
24095                 
24096                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24097                 this.el.setHeight(this.adjustWidth('textarea', ah));
24098                 this.iframe.style.height = ah + 'px';
24099                 if(this.doc){
24100                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24101                 }
24102             }
24103         }
24104     },
24105
24106     /**
24107      * Toggles the editor between standard and source edit mode.
24108      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24109      */
24110     toggleSourceEdit : function(sourceEditMode){
24111         
24112         this.sourceEditMode = sourceEditMode === true;
24113         
24114         if(this.sourceEditMode){
24115           
24116             this.syncValue();
24117             this.iframe.className = 'x-hidden';
24118             this.el.removeClass('x-hidden');
24119             this.el.dom.removeAttribute('tabIndex');
24120             this.el.focus();
24121         }else{
24122              
24123             this.pushValue();
24124             this.iframe.className = '';
24125             this.el.addClass('x-hidden');
24126             this.el.dom.setAttribute('tabIndex', -1);
24127             this.deferFocus();
24128         }
24129         this.setSize(this.wrap.getSize());
24130         this.fireEvent('editmodechange', this, this.sourceEditMode);
24131     },
24132
24133     // private used internally
24134     createLink : function(){
24135         var url = prompt(this.createLinkText, this.defaultLinkValue);
24136         if(url && url != 'http:/'+'/'){
24137             this.relayCmd('createlink', url);
24138         }
24139     },
24140
24141     // private (for BoxComponent)
24142     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24143
24144     // private (for BoxComponent)
24145     getResizeEl : function(){
24146         return this.wrap;
24147     },
24148
24149     // private (for BoxComponent)
24150     getPositionEl : function(){
24151         return this.wrap;
24152     },
24153
24154     // private
24155     initEvents : function(){
24156         this.originalValue = this.getValue();
24157     },
24158
24159     /**
24160      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24161      * @method
24162      */
24163     markInvalid : Roo.emptyFn,
24164     /**
24165      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
24166      * @method
24167      */
24168     clearInvalid : Roo.emptyFn,
24169
24170     setValue : function(v){
24171         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
24172         this.pushValue();
24173     },
24174
24175     /**
24176      * Protected method that will not generally be called directly. If you need/want
24177      * custom HTML cleanup, this is the method you should override.
24178      * @param {String} html The HTML to be cleaned
24179      * return {String} The cleaned HTML
24180      */
24181     cleanHtml : function(html){
24182         html = String(html);
24183         if(html.length > 5){
24184             if(Roo.isSafari){ // strip safari nonsense
24185                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24186             }
24187         }
24188         if(html == '&nbsp;'){
24189             html = '';
24190         }
24191         return html;
24192     },
24193
24194     /**
24195      * Protected method that will not generally be called directly. Syncs the contents
24196      * of the editor iframe with the textarea.
24197      */
24198     syncValue : function(){
24199         if(this.initialized){
24200             var bd = (this.doc.body || this.doc.documentElement);
24201             var html = bd.innerHTML;
24202             if(Roo.isSafari){
24203                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24204                 var m = bs.match(/text-align:(.*?);/i);
24205                 if(m && m[1]){
24206                     html = '<div style="'+m[0]+'">' + html + '</div>';
24207                 }
24208             }
24209             html = this.cleanHtml(html);
24210             if(this.fireEvent('beforesync', this, html) !== false){
24211                 this.el.dom.value = html;
24212                 this.fireEvent('sync', this, html);
24213             }
24214         }
24215     },
24216
24217     /**
24218      * Protected method that will not generally be called directly. Pushes the value of the textarea
24219      * into the iframe editor.
24220      */
24221     pushValue : function(){
24222         if(this.initialized){
24223             var v = this.el.dom.value;
24224             if(v.length < 1){
24225                 v = '&#160;';
24226             }
24227             if(this.fireEvent('beforepush', this, v) !== false){
24228                 (this.doc.body || this.doc.documentElement).innerHTML = v;
24229                 this.fireEvent('push', this, v);
24230             }
24231         }
24232     },
24233
24234     // private
24235     deferFocus : function(){
24236         this.focus.defer(10, this);
24237     },
24238
24239     // doc'ed in Field
24240     focus : function(){
24241         if(this.win && !this.sourceEditMode){
24242             this.win.focus();
24243         }else{
24244             this.el.focus();
24245         }
24246     },
24247     
24248     assignDocWin: function()
24249     {
24250         var iframe = this.iframe;
24251         
24252          if(Roo.isIE){
24253             this.doc = iframe.contentWindow.document;
24254             this.win = iframe.contentWindow;
24255         } else {
24256             if (!Roo.get(this.frameId)) {
24257                 return;
24258             }
24259             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24260             this.win = Roo.get(this.frameId).dom.contentWindow;
24261         }
24262     },
24263     
24264     // private
24265     initEditor : function(){
24266         //console.log("INIT EDITOR");
24267         this.assignDocWin();
24268         
24269         
24270         
24271         this.doc.designMode="on";
24272         this.doc.open();
24273         this.doc.write(this.getDocMarkup());
24274         this.doc.close();
24275         
24276         var dbody = (this.doc.body || this.doc.documentElement);
24277         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24278         // this copies styles from the containing element into thsi one..
24279         // not sure why we need all of this..
24280         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24281         ss['background-attachment'] = 'fixed'; // w3c
24282         dbody.bgProperties = 'fixed'; // ie
24283         Roo.DomHelper.applyStyles(dbody, ss);
24284         Roo.EventManager.on(this.doc, {
24285             'mousedown': this.onEditorEvent,
24286             'dblclick': this.onEditorEvent,
24287             'click': this.onEditorEvent,
24288             'keyup': this.onEditorEvent,
24289             buffer:100,
24290             scope: this
24291         });
24292         if(Roo.isGecko){
24293             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24294         }
24295         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24296             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24297         }
24298         this.initialized = true;
24299
24300         this.fireEvent('initialize', this);
24301         this.pushValue();
24302     },
24303
24304     // private
24305     onDestroy : function(){
24306         
24307         
24308         
24309         if(this.rendered){
24310             
24311             for (var i =0; i < this.toolbars.length;i++) {
24312                 // fixme - ask toolbars for heights?
24313                 this.toolbars[i].onDestroy();
24314             }
24315             
24316             this.wrap.dom.innerHTML = '';
24317             this.wrap.remove();
24318         }
24319     },
24320
24321     // private
24322     onFirstFocus : function(){
24323         
24324         this.assignDocWin();
24325         
24326         
24327         this.activated = true;
24328         for (var i =0; i < this.toolbars.length;i++) {
24329             this.toolbars[i].onFirstFocus();
24330         }
24331        
24332         if(Roo.isGecko){ // prevent silly gecko errors
24333             this.win.focus();
24334             var s = this.win.getSelection();
24335             if(!s.focusNode || s.focusNode.nodeType != 3){
24336                 var r = s.getRangeAt(0);
24337                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24338                 r.collapse(true);
24339                 this.deferFocus();
24340             }
24341             try{
24342                 this.execCmd('useCSS', true);
24343                 this.execCmd('styleWithCSS', false);
24344             }catch(e){}
24345         }
24346         this.fireEvent('activate', this);
24347     },
24348
24349     // private
24350     adjustFont: function(btn){
24351         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24352         //if(Roo.isSafari){ // safari
24353         //    adjust *= 2;
24354        // }
24355         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24356         if(Roo.isSafari){ // safari
24357             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24358             v =  (v < 10) ? 10 : v;
24359             v =  (v > 48) ? 48 : v;
24360             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24361             
24362         }
24363         
24364         
24365         v = Math.max(1, v+adjust);
24366         
24367         this.execCmd('FontSize', v  );
24368     },
24369
24370     onEditorEvent : function(e){
24371         this.fireEvent('editorevent', this, e);
24372       //  this.updateToolbar();
24373         this.syncValue();
24374     },
24375
24376     insertTag : function(tg)
24377     {
24378         // could be a bit smarter... -> wrap the current selected tRoo..
24379         
24380         this.execCmd("formatblock",   tg);
24381         
24382     },
24383     
24384     insertText : function(txt)
24385     {
24386         
24387         
24388         range = this.createRange();
24389         range.deleteContents();
24390                //alert(Sender.getAttribute('label'));
24391                
24392         range.insertNode(this.doc.createTextNode(txt));
24393     } ,
24394     
24395     // private
24396     relayBtnCmd : function(btn){
24397         this.relayCmd(btn.cmd);
24398     },
24399
24400     /**
24401      * Executes a Midas editor command on the editor document and performs necessary focus and
24402      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24403      * @param {String} cmd The Midas command
24404      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24405      */
24406     relayCmd : function(cmd, value){
24407         this.win.focus();
24408         this.execCmd(cmd, value);
24409         this.fireEvent('editorevent', this);
24410         //this.updateToolbar();
24411         this.deferFocus();
24412     },
24413
24414     /**
24415      * Executes a Midas editor command directly on the editor document.
24416      * For visual commands, you should use {@link #relayCmd} instead.
24417      * <b>This should only be called after the editor is initialized.</b>
24418      * @param {String} cmd The Midas command
24419      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24420      */
24421     execCmd : function(cmd, value){
24422         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24423         this.syncValue();
24424     },
24425
24426    
24427     /**
24428      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24429      * to insert tRoo.
24430      * @param {String} text
24431      */
24432     insertAtCursor : function(text){
24433         if(!this.activated){
24434             return;
24435         }
24436         if(Roo.isIE){
24437             this.win.focus();
24438             var r = this.doc.selection.createRange();
24439             if(r){
24440                 r.collapse(true);
24441                 r.pasteHTML(text);
24442                 this.syncValue();
24443                 this.deferFocus();
24444             }
24445         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24446             this.win.focus();
24447             this.execCmd('InsertHTML', text);
24448             this.deferFocus();
24449         }
24450     },
24451  // private
24452     mozKeyPress : function(e){
24453         if(e.ctrlKey){
24454             var c = e.getCharCode(), cmd;
24455           
24456             if(c > 0){
24457                 c = String.fromCharCode(c).toLowerCase();
24458                 switch(c){
24459                     case 'b':
24460                         cmd = 'bold';
24461                     break;
24462                     case 'i':
24463                         cmd = 'italic';
24464                     break;
24465                     case 'u':
24466                         cmd = 'underline';
24467                     case 'v':
24468                         this.cleanUpPaste.defer(100, this);
24469                         return;
24470                     break;
24471                 }
24472                 if(cmd){
24473                     this.win.focus();
24474                     this.execCmd(cmd);
24475                     this.deferFocus();
24476                     e.preventDefault();
24477                 }
24478                 
24479             }
24480         }
24481     },
24482
24483     // private
24484     fixKeys : function(){ // load time branching for fastest keydown performance
24485         if(Roo.isIE){
24486             return function(e){
24487                 var k = e.getKey(), r;
24488                 if(k == e.TAB){
24489                     e.stopEvent();
24490                     r = this.doc.selection.createRange();
24491                     if(r){
24492                         r.collapse(true);
24493                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24494                         this.deferFocus();
24495                     }
24496                     return;
24497                 }
24498                 
24499                 if(k == e.ENTER){
24500                     r = this.doc.selection.createRange();
24501                     if(r){
24502                         var target = r.parentElement();
24503                         if(!target || target.tagName.toLowerCase() != 'li'){
24504                             e.stopEvent();
24505                             r.pasteHTML('<br />');
24506                             r.collapse(false);
24507                             r.select();
24508                         }
24509                     }
24510                 }
24511                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24512                     this.cleanUpPaste.defer(100, this);
24513                     return;
24514                 }
24515                 
24516                 
24517             };
24518         }else if(Roo.isOpera){
24519             return function(e){
24520                 var k = e.getKey();
24521                 if(k == e.TAB){
24522                     e.stopEvent();
24523                     this.win.focus();
24524                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24525                     this.deferFocus();
24526                 }
24527                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24528                     this.cleanUpPaste.defer(100, this);
24529                     return;
24530                 }
24531                 
24532             };
24533         }else if(Roo.isSafari){
24534             return function(e){
24535                 var k = e.getKey();
24536                 
24537                 if(k == e.TAB){
24538                     e.stopEvent();
24539                     this.execCmd('InsertText','\t');
24540                     this.deferFocus();
24541                     return;
24542                 }
24543                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24544                     this.cleanUpPaste.defer(100, this);
24545                     return;
24546                 }
24547                 
24548              };
24549         }
24550     }(),
24551     
24552     getAllAncestors: function()
24553     {
24554         var p = this.getSelectedNode();
24555         var a = [];
24556         if (!p) {
24557             a.push(p); // push blank onto stack..
24558             p = this.getParentElement();
24559         }
24560         
24561         
24562         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24563             a.push(p);
24564             p = p.parentNode;
24565         }
24566         a.push(this.doc.body);
24567         return a;
24568     },
24569     lastSel : false,
24570     lastSelNode : false,
24571     
24572     
24573     getSelection : function() 
24574     {
24575         this.assignDocWin();
24576         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24577     },
24578     
24579     getSelectedNode: function() 
24580     {
24581         // this may only work on Gecko!!!
24582         
24583         // should we cache this!!!!
24584         
24585         
24586         
24587          
24588         var range = this.createRange(this.getSelection());
24589         
24590         if (Roo.isIE) {
24591             var parent = range.parentElement();
24592             while (true) {
24593                 var testRange = range.duplicate();
24594                 testRange.moveToElementText(parent);
24595                 if (testRange.inRange(range)) {
24596                     break;
24597                 }
24598                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24599                     break;
24600                 }
24601                 parent = parent.parentElement;
24602             }
24603             return parent;
24604         }
24605         
24606         
24607         var ar = range.endContainer.childNodes;
24608         if (!ar.length) {
24609             ar = range.commonAncestorContainer.childNodes;
24610             //alert(ar.length);
24611         }
24612         var nodes = [];
24613         var other_nodes = [];
24614         var has_other_nodes = false;
24615         for (var i=0;i<ar.length;i++) {
24616             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24617                 continue;
24618             }
24619             // fullly contained node.
24620             
24621             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24622                 nodes.push(ar[i]);
24623                 continue;
24624             }
24625             
24626             // probably selected..
24627             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24628                 other_nodes.push(ar[i]);
24629                 continue;
24630             }
24631             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24632                 continue;
24633             }
24634             
24635             
24636             has_other_nodes = true;
24637         }
24638         if (!nodes.length && other_nodes.length) {
24639             nodes= other_nodes;
24640         }
24641         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24642             return false;
24643         }
24644         
24645         return nodes[0];
24646     },
24647     createRange: function(sel)
24648     {
24649         // this has strange effects when using with 
24650         // top toolbar - not sure if it's a great idea.
24651         //this.editor.contentWindow.focus();
24652         if (typeof sel != "undefined") {
24653             try {
24654                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24655             } catch(e) {
24656                 return this.doc.createRange();
24657             }
24658         } else {
24659             return this.doc.createRange();
24660         }
24661     },
24662     getParentElement: function()
24663     {
24664         
24665         this.assignDocWin();
24666         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24667         
24668         var range = this.createRange(sel);
24669          
24670         try {
24671             var p = range.commonAncestorContainer;
24672             while (p.nodeType == 3) { // text node
24673                 p = p.parentNode;
24674             }
24675             return p;
24676         } catch (e) {
24677             return null;
24678         }
24679     
24680     },
24681     
24682     
24683     
24684     // BC Hacks - cause I cant work out what i was trying to do..
24685     rangeIntersectsNode : function(range, node)
24686     {
24687         var nodeRange = node.ownerDocument.createRange();
24688         try {
24689             nodeRange.selectNode(node);
24690         }
24691         catch (e) {
24692             nodeRange.selectNodeContents(node);
24693         }
24694
24695         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
24696                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
24697     },
24698     rangeCompareNode : function(range, node) {
24699         var nodeRange = node.ownerDocument.createRange();
24700         try {
24701             nodeRange.selectNode(node);
24702         } catch (e) {
24703             nodeRange.selectNodeContents(node);
24704         }
24705         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
24706         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
24707
24708         if (nodeIsBefore && !nodeIsAfter)
24709             return 0;
24710         if (!nodeIsBefore && nodeIsAfter)
24711             return 1;
24712         if (nodeIsBefore && nodeIsAfter)
24713             return 2;
24714
24715         return 3;
24716     },
24717
24718     // private? - in a new class?
24719     cleanUpPaste :  function()
24720     {
24721         // cleans up the whole document..
24722       //  console.log('cleanuppaste');
24723         this.cleanUpChildren(this.doc.body)
24724         
24725         
24726     },
24727     cleanUpChildren : function (n)
24728     {
24729         if (!n.childNodes.length) {
24730             return;
24731         }
24732         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24733            this.cleanUpChild(n.childNodes[i]);
24734         }
24735     },
24736     
24737     
24738         
24739     
24740     cleanUpChild : function (node)
24741     {
24742         //console.log(node);
24743         if (node.nodeName == "#text") {
24744             // clean up silly Windows -- stuff?
24745             return; 
24746         }
24747         if (node.nodeName == "#comment") {
24748             node.parentNode.removeChild(node);
24749             // clean up silly Windows -- stuff?
24750             return; 
24751         }
24752         
24753         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
24754             // remove node.
24755             node.parentNode.removeChild(node);
24756             return;
24757             
24758         }
24759         if (!node.attributes || !node.attributes.length) {
24760             this.cleanUpChildren(node);
24761             return;
24762         }
24763         
24764         function cleanAttr(n,v)
24765         {
24766             
24767             if (v.match(/^\./) || v.match(/^\//)) {
24768                 return;
24769             }
24770             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
24771                 return;
24772             }
24773             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
24774             node.removeAttribute(n);
24775             
24776         }
24777         
24778         function cleanStyle(n,v)
24779         {
24780             if (v.match(/expression/)) { //XSS?? should we even bother..
24781                 node.removeAttribute(n);
24782                 return;
24783             }
24784             
24785             
24786             var parts = v.split(/;/);
24787             Roo.each(parts, function(p) {
24788                 p = p.replace(/\s+/g,'');
24789                 if (!p.length) {
24790                     return;
24791                 }
24792                 var l = p.split(':').shift().replace(/\s+/g,'');
24793                 
24794                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
24795                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
24796                     node.removeAttribute(n);
24797                     return false;
24798                 }
24799             });
24800             
24801             
24802         }
24803         
24804         
24805         for (var i = node.attributes.length-1; i > -1 ; i--) {
24806             var a = node.attributes[i];
24807             //console.log(a);
24808             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
24809                 node.removeAttribute(a.name);
24810                 return;
24811             }
24812             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
24813                 cleanAttr(a.name,a.value); // fixme..
24814                 return;
24815             }
24816             if (a.name == 'style') {
24817                 cleanStyle(a.name,a.value);
24818             }
24819             /// clean up MS crap..
24820             if (a.name == 'class') {
24821                 if (a.value.match(/^Mso/)) {
24822                     node.className = '';
24823                 }
24824             }
24825             
24826             // style cleanup!?
24827             // class cleanup?
24828             
24829         }
24830         
24831         
24832         this.cleanUpChildren(node);
24833         
24834         
24835     }
24836     
24837     
24838     // hide stuff that is not compatible
24839     /**
24840      * @event blur
24841      * @hide
24842      */
24843     /**
24844      * @event change
24845      * @hide
24846      */
24847     /**
24848      * @event focus
24849      * @hide
24850      */
24851     /**
24852      * @event specialkey
24853      * @hide
24854      */
24855     /**
24856      * @cfg {String} fieldClass @hide
24857      */
24858     /**
24859      * @cfg {String} focusClass @hide
24860      */
24861     /**
24862      * @cfg {String} autoCreate @hide
24863      */
24864     /**
24865      * @cfg {String} inputType @hide
24866      */
24867     /**
24868      * @cfg {String} invalidClass @hide
24869      */
24870     /**
24871      * @cfg {String} invalidText @hide
24872      */
24873     /**
24874      * @cfg {String} msgFx @hide
24875      */
24876     /**
24877      * @cfg {String} validateOnBlur @hide
24878      */
24879 });
24880
24881 Roo.form.HtmlEditor.white = [
24882         'area', 'br', 'img', 'input', 'hr', 'wbr',
24883         
24884        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
24885        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
24886        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
24887        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
24888        'table',   'ul',         'xmp', 
24889        
24890        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
24891       'thead',   'tr', 
24892      
24893       'dir', 'menu', 'ol', 'ul', 'dl',
24894        
24895       'embed',  'object'
24896 ];
24897
24898
24899 Roo.form.HtmlEditor.black = [
24900     //    'embed',  'object', // enable - backend responsiblity to clean thiese
24901         'applet', // 
24902         'base',   'basefont', 'bgsound', 'blink',  'body', 
24903         'frame',  'frameset', 'head',    'html',   'ilayer', 
24904         'iframe', 'layer',  'link',     'meta',    'object',   
24905         'script', 'style' ,'title',  'xml' // clean later..
24906 ];
24907 Roo.form.HtmlEditor.clean = [
24908     'script', 'style', 'title', 'xml'
24909 ];
24910
24911 // attributes..
24912
24913 Roo.form.HtmlEditor.ablack = [
24914     'on'
24915 ];
24916     
24917 Roo.form.HtmlEditor.aclean = [ 
24918     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
24919 ];
24920
24921 // protocols..
24922 Roo.form.HtmlEditor.pwhite= [
24923         'http',  'https',  'mailto'
24924 ];
24925
24926 Roo.form.HtmlEditor.cwhite= [
24927         'text-align',
24928         'font-size'
24929 ];
24930
24931 // <script type="text/javascript">
24932 /*
24933  * Based on
24934  * Ext JS Library 1.1.1
24935  * Copyright(c) 2006-2007, Ext JS, LLC.
24936  *  
24937  
24938  */
24939
24940 /**
24941  * @class Roo.form.HtmlEditorToolbar1
24942  * Basic Toolbar
24943  * 
24944  * Usage:
24945  *
24946  new Roo.form.HtmlEditor({
24947     ....
24948     toolbars : [
24949         new Roo.form.HtmlEditorToolbar1({
24950             disable : { fonts: 1 , format: 1, ..., ... , ...],
24951             btns : [ .... ]
24952         })
24953     }
24954      
24955  * 
24956  * @cfg {Object} disable List of elements to disable..
24957  * @cfg {Array} btns List of additional buttons.
24958  * 
24959  * 
24960  * NEEDS Extra CSS? 
24961  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
24962  */
24963  
24964 Roo.form.HtmlEditor.ToolbarStandard = function(config)
24965 {
24966     
24967     Roo.apply(this, config);
24968     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24969     // dont call parent... till later.
24970 }
24971
24972 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
24973     
24974     tb: false,
24975     
24976     rendered: false,
24977     
24978     editor : false,
24979     /**
24980      * @cfg {Object} disable  List of toolbar elements to disable
24981          
24982      */
24983     disable : false,
24984       /**
24985      * @cfg {Array} fontFamilies An array of available font families
24986      */
24987     fontFamilies : [
24988         'Arial',
24989         'Courier New',
24990         'Tahoma',
24991         'Times New Roman',
24992         'Verdana'
24993     ],
24994     
24995     specialChars : [
24996            "&#169;",
24997           "&#174;",     
24998           "&#8482;",    
24999           "&#163;" ,    
25000          // "&#8212;",    
25001           "&#8230;",    
25002           "&#247;" ,    
25003         //  "&#225;" ,     ?? a acute?
25004            "&#8364;"    , //Euro
25005        //   "&#8220;"    ,
25006         //  "&#8221;"    ,
25007         //  "&#8226;"    ,
25008           "&#176;"  //   , // degrees
25009
25010          // "&#233;"     , // e ecute
25011          // "&#250;"     , // u ecute?
25012     ],
25013     inputElements : [ 
25014             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
25015             "input:submit", "input:button", "select", "textarea", "label" ],
25016     formats : [
25017         ["p"] ,  
25018         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
25019         ["pre"],[ "code"], 
25020         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
25021     ],
25022      /**
25023      * @cfg {String} defaultFont default font to use.
25024      */
25025     defaultFont: 'tahoma',
25026    
25027     fontSelect : false,
25028     
25029     
25030     formatCombo : false,
25031     
25032     init : function(editor)
25033     {
25034         this.editor = editor;
25035         
25036         
25037         var fid = editor.frameId;
25038         var etb = this;
25039         function btn(id, toggle, handler){
25040             var xid = fid + '-'+ id ;
25041             return {
25042                 id : xid,
25043                 cmd : id,
25044                 cls : 'x-btn-icon x-edit-'+id,
25045                 enableToggle:toggle !== false,
25046                 scope: editor, // was editor...
25047                 handler:handler||editor.relayBtnCmd,
25048                 clickEvent:'mousedown',
25049                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25050                 tabIndex:-1
25051             };
25052         }
25053         
25054         
25055         
25056         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
25057         this.tb = tb;
25058          // stop form submits
25059         tb.el.on('click', function(e){
25060             e.preventDefault(); // what does this do?
25061         });
25062
25063         if(!this.disable.font && !Roo.isSafari){
25064             /* why no safari for fonts
25065             editor.fontSelect = tb.el.createChild({
25066                 tag:'select',
25067                 tabIndex: -1,
25068                 cls:'x-font-select',
25069                 html: editor.createFontOptions()
25070             });
25071             editor.fontSelect.on('change', function(){
25072                 var font = editor.fontSelect.dom.value;
25073                 editor.relayCmd('fontname', font);
25074                 editor.deferFocus();
25075             }, editor);
25076             tb.add(
25077                 editor.fontSelect.dom,
25078                 '-'
25079             );
25080             */
25081         };
25082         if(!this.disable.formats){
25083             this.formatCombo = new Roo.form.ComboBox({
25084                 store: new Roo.data.SimpleStore({
25085                     id : 'tag',
25086                     fields: ['tag'],
25087                     data : this.formats // from states.js
25088                 }),
25089                 blockFocus : true,
25090                 //autoCreate : {tag: "div",  size: "20"},
25091                 displayField:'tag',
25092                 typeAhead: false,
25093                 mode: 'local',
25094                 editable : false,
25095                 triggerAction: 'all',
25096                 emptyText:'Add tag',
25097                 selectOnFocus:true,
25098                 width:135,
25099                 listeners : {
25100                     'select': function(c, r, i) {
25101                         editor.insertTag(r.get('tag'));
25102                         editor.focus();
25103                     }
25104                 }
25105
25106             });
25107             tb.addField(this.formatCombo);
25108             
25109         }
25110         
25111         if(!this.disable.format){
25112             tb.add(
25113                 btn('bold'),
25114                 btn('italic'),
25115                 btn('underline')
25116             );
25117         };
25118         if(!this.disable.fontSize){
25119             tb.add(
25120                 '-',
25121                 
25122                 
25123                 btn('increasefontsize', false, editor.adjustFont),
25124                 btn('decreasefontsize', false, editor.adjustFont)
25125             );
25126         };
25127         
25128         
25129         if(this.disable.colors){
25130             tb.add(
25131                 '-', {
25132                     id:editor.frameId +'-forecolor',
25133                     cls:'x-btn-icon x-edit-forecolor',
25134                     clickEvent:'mousedown',
25135                     tooltip: this.buttonTips['forecolor'] || undefined,
25136                     tabIndex:-1,
25137                     menu : new Roo.menu.ColorMenu({
25138                         allowReselect: true,
25139                         focus: Roo.emptyFn,
25140                         value:'000000',
25141                         plain:true,
25142                         selectHandler: function(cp, color){
25143                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
25144                             editor.deferFocus();
25145                         },
25146                         scope: editor,
25147                         clickEvent:'mousedown'
25148                     })
25149                 }, {
25150                     id:editor.frameId +'backcolor',
25151                     cls:'x-btn-icon x-edit-backcolor',
25152                     clickEvent:'mousedown',
25153                     tooltip: this.buttonTips['backcolor'] || undefined,
25154                     tabIndex:-1,
25155                     menu : new Roo.menu.ColorMenu({
25156                         focus: Roo.emptyFn,
25157                         value:'FFFFFF',
25158                         plain:true,
25159                         allowReselect: true,
25160                         selectHandler: function(cp, color){
25161                             if(Roo.isGecko){
25162                                 editor.execCmd('useCSS', false);
25163                                 editor.execCmd('hilitecolor', color);
25164                                 editor.execCmd('useCSS', true);
25165                                 editor.deferFocus();
25166                             }else{
25167                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
25168                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
25169                                 editor.deferFocus();
25170                             }
25171                         },
25172                         scope:editor,
25173                         clickEvent:'mousedown'
25174                     })
25175                 }
25176             );
25177         };
25178         // now add all the items...
25179         
25180
25181         if(!this.disable.alignments){
25182             tb.add(
25183                 '-',
25184                 btn('justifyleft'),
25185                 btn('justifycenter'),
25186                 btn('justifyright')
25187             );
25188         };
25189
25190         //if(!Roo.isSafari){
25191             if(!this.disable.links){
25192                 tb.add(
25193                     '-',
25194                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
25195                 );
25196             };
25197
25198             if(!this.disable.lists){
25199                 tb.add(
25200                     '-',
25201                     btn('insertorderedlist'),
25202                     btn('insertunorderedlist')
25203                 );
25204             }
25205             if(!this.disable.sourceEdit){
25206                 tb.add(
25207                     '-',
25208                     btn('sourceedit', true, function(btn){
25209                         this.toggleSourceEdit(btn.pressed);
25210                     })
25211                 );
25212             }
25213         //}
25214         
25215         var smenu = { };
25216         // special menu.. - needs to be tidied up..
25217         if (!this.disable.special) {
25218             smenu = {
25219                 text: "&#169;",
25220                 cls: 'x-edit-none',
25221                 menu : {
25222                     items : []
25223                    }
25224             };
25225             for (var i =0; i < this.specialChars.length; i++) {
25226                 smenu.menu.items.push({
25227                     
25228                     html: this.specialChars[i],
25229                     handler: function(a,b) {
25230                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
25231                         
25232                     },
25233                     tabIndex:-1
25234                 });
25235             }
25236             
25237             
25238             tb.add(smenu);
25239             
25240             
25241         }
25242         if (this.btns) {
25243             for(var i =0; i< this.btns.length;i++) {
25244                 var b = this.btns[i];
25245                 b.cls =  'x-edit-none';
25246                 b.scope = editor;
25247                 tb.add(b);
25248             }
25249         
25250         }
25251         
25252         
25253         
25254         // disable everything...
25255         
25256         this.tb.items.each(function(item){
25257            if(item.id != editor.frameId+ '-sourceedit'){
25258                 item.disable();
25259             }
25260         });
25261         this.rendered = true;
25262         
25263         // the all the btns;
25264         editor.on('editorevent', this.updateToolbar, this);
25265         // other toolbars need to implement this..
25266         //editor.on('editmodechange', this.updateToolbar, this);
25267     },
25268     
25269     
25270     
25271     /**
25272      * Protected method that will not generally be called directly. It triggers
25273      * a toolbar update by reading the markup state of the current selection in the editor.
25274      */
25275     updateToolbar: function(){
25276
25277         if(!this.editor.activated){
25278             this.editor.onFirstFocus();
25279             return;
25280         }
25281
25282         var btns = this.tb.items.map, 
25283             doc = this.editor.doc,
25284             frameId = this.editor.frameId;
25285
25286         if(!this.disable.font && !Roo.isSafari){
25287             /*
25288             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
25289             if(name != this.fontSelect.dom.value){
25290                 this.fontSelect.dom.value = name;
25291             }
25292             */
25293         }
25294         if(!this.disable.format){
25295             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
25296             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
25297             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
25298         }
25299         if(!this.disable.alignments){
25300             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
25301             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
25302             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
25303         }
25304         if(!Roo.isSafari && !this.disable.lists){
25305             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
25306             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
25307         }
25308         
25309         var ans = this.editor.getAllAncestors();
25310         if (this.formatCombo) {
25311             
25312             
25313             var store = this.formatCombo.store;
25314             this.formatCombo.setValue("");
25315             for (var i =0; i < ans.length;i++) {
25316                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
25317                     // select it..
25318                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
25319                     break;
25320                 }
25321             }
25322         }
25323         
25324         
25325         
25326         // hides menus... - so this cant be on a menu...
25327         Roo.menu.MenuMgr.hideAll();
25328
25329         //this.editorsyncValue();
25330     },
25331    
25332     
25333     createFontOptions : function(){
25334         var buf = [], fs = this.fontFamilies, ff, lc;
25335         for(var i = 0, len = fs.length; i< len; i++){
25336             ff = fs[i];
25337             lc = ff.toLowerCase();
25338             buf.push(
25339                 '<option value="',lc,'" style="font-family:',ff,';"',
25340                     (this.defaultFont == lc ? ' selected="true">' : '>'),
25341                     ff,
25342                 '</option>'
25343             );
25344         }
25345         return buf.join('');
25346     },
25347     
25348     toggleSourceEdit : function(sourceEditMode){
25349         if(sourceEditMode === undefined){
25350             sourceEditMode = !this.sourceEditMode;
25351         }
25352         this.sourceEditMode = sourceEditMode === true;
25353         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
25354         // just toggle the button?
25355         if(btn.pressed !== this.editor.sourceEditMode){
25356             btn.toggle(this.editor.sourceEditMode);
25357             return;
25358         }
25359         
25360         if(this.sourceEditMode){
25361             this.tb.items.each(function(item){
25362                 if(item.cmd != 'sourceedit'){
25363                     item.disable();
25364                 }
25365             });
25366           
25367         }else{
25368             if(this.initialized){
25369                 this.tb.items.each(function(item){
25370                     item.enable();
25371                 });
25372             }
25373             
25374         }
25375         // tell the editor that it's been pressed..
25376         this.editor.toggleSourceEdit(sourceEditMode);
25377        
25378     },
25379      /**
25380      * Object collection of toolbar tooltips for the buttons in the editor. The key
25381      * is the command id associated with that button and the value is a valid QuickTips object.
25382      * For example:
25383 <pre><code>
25384 {
25385     bold : {
25386         title: 'Bold (Ctrl+B)',
25387         text: 'Make the selected text bold.',
25388         cls: 'x-html-editor-tip'
25389     },
25390     italic : {
25391         title: 'Italic (Ctrl+I)',
25392         text: 'Make the selected text italic.',
25393         cls: 'x-html-editor-tip'
25394     },
25395     ...
25396 </code></pre>
25397     * @type Object
25398      */
25399     buttonTips : {
25400         bold : {
25401             title: 'Bold (Ctrl+B)',
25402             text: 'Make the selected text bold.',
25403             cls: 'x-html-editor-tip'
25404         },
25405         italic : {
25406             title: 'Italic (Ctrl+I)',
25407             text: 'Make the selected text italic.',
25408             cls: 'x-html-editor-tip'
25409         },
25410         underline : {
25411             title: 'Underline (Ctrl+U)',
25412             text: 'Underline the selected text.',
25413             cls: 'x-html-editor-tip'
25414         },
25415         increasefontsize : {
25416             title: 'Grow Text',
25417             text: 'Increase the font size.',
25418             cls: 'x-html-editor-tip'
25419         },
25420         decreasefontsize : {
25421             title: 'Shrink Text',
25422             text: 'Decrease the font size.',
25423             cls: 'x-html-editor-tip'
25424         },
25425         backcolor : {
25426             title: 'Text Highlight Color',
25427             text: 'Change the background color of the selected text.',
25428             cls: 'x-html-editor-tip'
25429         },
25430         forecolor : {
25431             title: 'Font Color',
25432             text: 'Change the color of the selected text.',
25433             cls: 'x-html-editor-tip'
25434         },
25435         justifyleft : {
25436             title: 'Align Text Left',
25437             text: 'Align text to the left.',
25438             cls: 'x-html-editor-tip'
25439         },
25440         justifycenter : {
25441             title: 'Center Text',
25442             text: 'Center text in the editor.',
25443             cls: 'x-html-editor-tip'
25444         },
25445         justifyright : {
25446             title: 'Align Text Right',
25447             text: 'Align text to the right.',
25448             cls: 'x-html-editor-tip'
25449         },
25450         insertunorderedlist : {
25451             title: 'Bullet List',
25452             text: 'Start a bulleted list.',
25453             cls: 'x-html-editor-tip'
25454         },
25455         insertorderedlist : {
25456             title: 'Numbered List',
25457             text: 'Start a numbered list.',
25458             cls: 'x-html-editor-tip'
25459         },
25460         createlink : {
25461             title: 'Hyperlink',
25462             text: 'Make the selected text a hyperlink.',
25463             cls: 'x-html-editor-tip'
25464         },
25465         sourceedit : {
25466             title: 'Source Edit',
25467             text: 'Switch to source editing mode.',
25468             cls: 'x-html-editor-tip'
25469         }
25470     },
25471     // private
25472     onDestroy : function(){
25473         if(this.rendered){
25474             
25475             this.tb.items.each(function(item){
25476                 if(item.menu){
25477                     item.menu.removeAll();
25478                     if(item.menu.el){
25479                         item.menu.el.destroy();
25480                     }
25481                 }
25482                 item.destroy();
25483             });
25484              
25485         }
25486     },
25487     onFirstFocus: function() {
25488         this.tb.items.each(function(item){
25489            item.enable();
25490         });
25491     }
25492 });
25493
25494
25495
25496
25497 // <script type="text/javascript">
25498 /*
25499  * Based on
25500  * Ext JS Library 1.1.1
25501  * Copyright(c) 2006-2007, Ext JS, LLC.
25502  *  
25503  
25504  */
25505
25506  
25507 /**
25508  * @class Roo.form.HtmlEditor.ToolbarContext
25509  * Context Toolbar
25510  * 
25511  * Usage:
25512  *
25513  new Roo.form.HtmlEditor({
25514     ....
25515     toolbars : [
25516         new Roo.form.HtmlEditor.ToolbarStandard(),
25517         new Roo.form.HtmlEditor.ToolbarContext()
25518         })
25519     }
25520      
25521  * 
25522  * @config : {Object} disable List of elements to disable.. (not done yet.)
25523  * 
25524  * 
25525  */
25526
25527 Roo.form.HtmlEditor.ToolbarContext = function(config)
25528 {
25529     
25530     Roo.apply(this, config);
25531     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25532     // dont call parent... till later.
25533 }
25534 Roo.form.HtmlEditor.ToolbarContext.types = {
25535     'IMG' : {
25536         width : {
25537             title: "Width",
25538             width: 40
25539         },
25540         height:  {
25541             title: "Height",
25542             width: 40
25543         },
25544         align: {
25545             title: "Align",
25546             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
25547             width : 80
25548             
25549         },
25550         border: {
25551             title: "Border",
25552             width: 40
25553         },
25554         alt: {
25555             title: "Alt",
25556             width: 120
25557         },
25558         src : {
25559             title: "Src",
25560             width: 220
25561         }
25562         
25563     },
25564     'A' : {
25565         name : {
25566             title: "Name",
25567             width: 50
25568         },
25569         href:  {
25570             title: "Href",
25571             width: 220
25572         } // border?
25573         
25574     },
25575     'TABLE' : {
25576         rows : {
25577             title: "Rows",
25578             width: 20
25579         },
25580         cols : {
25581             title: "Cols",
25582             width: 20
25583         },
25584         width : {
25585             title: "Width",
25586             width: 40
25587         },
25588         height : {
25589             title: "Height",
25590             width: 40
25591         },
25592         border : {
25593             title: "Border",
25594             width: 20
25595         }
25596     },
25597     'TD' : {
25598         width : {
25599             title: "Width",
25600             width: 40
25601         },
25602         height : {
25603             title: "Height",
25604             width: 40
25605         },   
25606         align: {
25607             title: "Align",
25608             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
25609             width: 40
25610         },
25611         valign: {
25612             title: "Valign",
25613             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
25614             width: 40
25615         },
25616         colspan: {
25617             title: "Colspan",
25618             width: 20
25619             
25620         }
25621     },
25622     'INPUT' : {
25623         name : {
25624             title: "name",
25625             width: 120
25626         },
25627         value : {
25628             title: "Value",
25629             width: 120
25630         },
25631         width : {
25632             title: "Width",
25633             width: 40
25634         }
25635     },
25636     'LABEL' : {
25637         'for' : {
25638             title: "For",
25639             width: 120
25640         }
25641     },
25642     'TEXTAREA' : {
25643           name : {
25644             title: "name",
25645             width: 120
25646         },
25647         rows : {
25648             title: "Rows",
25649             width: 20
25650         },
25651         cols : {
25652             title: "Cols",
25653             width: 20
25654         }
25655     },
25656     'SELECT' : {
25657         name : {
25658             title: "name",
25659             width: 120
25660         },
25661         selectoptions : {
25662             title: "Options",
25663             width: 200
25664         }
25665     },
25666     'BODY' : {
25667         title : {
25668             title: "title",
25669             width: 120,
25670             disabled : true
25671         }
25672     }
25673 };
25674
25675
25676
25677 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
25678     
25679     tb: false,
25680     
25681     rendered: false,
25682     
25683     editor : false,
25684     /**
25685      * @cfg {Object} disable  List of toolbar elements to disable
25686          
25687      */
25688     disable : false,
25689     
25690     
25691     
25692     toolbars : false,
25693     
25694     init : function(editor)
25695     {
25696         this.editor = editor;
25697         
25698         
25699         var fid = editor.frameId;
25700         var etb = this;
25701         function btn(id, toggle, handler){
25702             var xid = fid + '-'+ id ;
25703             return {
25704                 id : xid,
25705                 cmd : id,
25706                 cls : 'x-btn-icon x-edit-'+id,
25707                 enableToggle:toggle !== false,
25708                 scope: editor, // was editor...
25709                 handler:handler||editor.relayBtnCmd,
25710                 clickEvent:'mousedown',
25711                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
25712                 tabIndex:-1
25713             };
25714         }
25715         // create a new element.
25716         var wdiv = editor.wrap.createChild({
25717                 tag: 'div'
25718             }, editor.wrap.dom.firstChild.nextSibling, true);
25719         
25720         // can we do this more than once??
25721         
25722          // stop form submits
25723       
25724  
25725         // disable everything...
25726         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25727         this.toolbars = {};
25728            
25729         for (var i in  ty) {
25730           
25731             this.toolbars[i] = this.buildToolbar(ty[i],i);
25732         }
25733         this.tb = this.toolbars.BODY;
25734         this.tb.el.show();
25735         
25736          
25737         this.rendered = true;
25738         
25739         // the all the btns;
25740         editor.on('editorevent', this.updateToolbar, this);
25741         // other toolbars need to implement this..
25742         //editor.on('editmodechange', this.updateToolbar, this);
25743     },
25744     
25745     
25746     
25747     /**
25748      * Protected method that will not generally be called directly. It triggers
25749      * a toolbar update by reading the markup state of the current selection in the editor.
25750      */
25751     updateToolbar: function(){
25752
25753         if(!this.editor.activated){
25754             this.editor.onFirstFocus();
25755             return;
25756         }
25757
25758         
25759         var ans = this.editor.getAllAncestors();
25760         
25761         // pick
25762         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
25763         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
25764         sel = sel ? sel : this.editor.doc.body;
25765         sel = sel.tagName.length ? sel : this.editor.doc.body;
25766         var tn = sel.tagName.toUpperCase();
25767         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
25768         tn = sel.tagName.toUpperCase();
25769         if (this.tb.name  == tn) {
25770             return; // no change
25771         }
25772         this.tb.el.hide();
25773         ///console.log("show: " + tn);
25774         this.tb =  this.toolbars[tn];
25775         this.tb.el.show();
25776         this.tb.fields.each(function(e) {
25777             e.setValue(sel.getAttribute(e.name));
25778         });
25779         this.tb.selectedNode = sel;
25780         
25781         
25782         Roo.menu.MenuMgr.hideAll();
25783
25784         //this.editorsyncValue();
25785     },
25786    
25787        
25788     // private
25789     onDestroy : function(){
25790         if(this.rendered){
25791             
25792             this.tb.items.each(function(item){
25793                 if(item.menu){
25794                     item.menu.removeAll();
25795                     if(item.menu.el){
25796                         item.menu.el.destroy();
25797                     }
25798                 }
25799                 item.destroy();
25800             });
25801              
25802         }
25803     },
25804     onFirstFocus: function() {
25805         // need to do this for all the toolbars..
25806         this.tb.items.each(function(item){
25807            item.enable();
25808         });
25809     },
25810     buildToolbar: function(tlist, nm)
25811     {
25812         var editor = this.editor;
25813          // create a new element.
25814         var wdiv = editor.wrap.createChild({
25815                 tag: 'div'
25816             }, editor.wrap.dom.firstChild.nextSibling, true);
25817         
25818        
25819         var tb = new Roo.Toolbar(wdiv);
25820         tb.add(nm+ ":&nbsp;");
25821         for (var i in tlist) {
25822             var item = tlist[i];
25823             tb.add(item.title + ":&nbsp;");
25824             if (item.opts) {
25825                 // fixme
25826                 
25827               
25828                 tb.addField( new Roo.form.ComboBox({
25829                     store: new Roo.data.SimpleStore({
25830                         id : 'val',
25831                         fields: ['val'],
25832                         data : item.opts // from states.js
25833                     }),
25834                     name : i,
25835                     displayField:'val',
25836                     typeAhead: false,
25837                     mode: 'local',
25838                     editable : false,
25839                     triggerAction: 'all',
25840                     emptyText:'Select',
25841                     selectOnFocus:true,
25842                     width: item.width ? item.width  : 130,
25843                     listeners : {
25844                         'select': function(c, r, i) {
25845                             tb.selectedNode.setAttribute(c.name, r.get('val'));
25846                         }
25847                     }
25848
25849                 }));
25850                 continue;
25851                     
25852                 
25853                 
25854                 
25855                 
25856                 tb.addField( new Roo.form.TextField({
25857                     name: i,
25858                     width: 100,
25859                     //allowBlank:false,
25860                     value: ''
25861                 }));
25862                 continue;
25863             }
25864             tb.addField( new Roo.form.TextField({
25865                 name: i,
25866                 width: item.width,
25867                 //allowBlank:true,
25868                 value: '',
25869                 listeners: {
25870                     'change' : function(f, nv, ov) {
25871                         tb.selectedNode.setAttribute(f.name, nv);
25872                     }
25873                 }
25874             }));
25875              
25876         }
25877         tb.el.on('click', function(e){
25878             e.preventDefault(); // what does this do?
25879         });
25880         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
25881         tb.el.hide();
25882         tb.name = nm;
25883         // dont need to disable them... as they will get hidden
25884         return tb;
25885          
25886         
25887     }
25888     
25889     
25890     
25891     
25892 });
25893
25894
25895
25896
25897
25898 /*
25899  * Based on:
25900  * Ext JS Library 1.1.1
25901  * Copyright(c) 2006-2007, Ext JS, LLC.
25902  *
25903  * Originally Released Under LGPL - original licence link has changed is not relivant.
25904  *
25905  * Fork - LGPL
25906  * <script type="text/javascript">
25907  */
25908  
25909 /**
25910  * @class Roo.form.BasicForm
25911  * @extends Roo.util.Observable
25912  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
25913  * @constructor
25914  * @param {String/HTMLElement/Roo.Element} el The form element or its id
25915  * @param {Object} config Configuration options
25916  */
25917 Roo.form.BasicForm = function(el, config){
25918     this.allItems = [];
25919     this.childForms = [];
25920     Roo.apply(this, config);
25921     /*
25922      * The Roo.form.Field items in this form.
25923      * @type MixedCollection
25924      */
25925      
25926      
25927     this.items = new Roo.util.MixedCollection(false, function(o){
25928         return o.id || (o.id = Roo.id());
25929     });
25930     this.addEvents({
25931         /**
25932          * @event beforeaction
25933          * Fires before any action is performed. Return false to cancel the action.
25934          * @param {Form} this
25935          * @param {Action} action The action to be performed
25936          */
25937         beforeaction: true,
25938         /**
25939          * @event actionfailed
25940          * Fires when an action fails.
25941          * @param {Form} this
25942          * @param {Action} action The action that failed
25943          */
25944         actionfailed : true,
25945         /**
25946          * @event actioncomplete
25947          * Fires when an action is completed.
25948          * @param {Form} this
25949          * @param {Action} action The action that completed
25950          */
25951         actioncomplete : true
25952     });
25953     if(el){
25954         this.initEl(el);
25955     }
25956     Roo.form.BasicForm.superclass.constructor.call(this);
25957 };
25958
25959 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
25960     /**
25961      * @cfg {String} method
25962      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
25963      */
25964     /**
25965      * @cfg {DataReader} reader
25966      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
25967      * This is optional as there is built-in support for processing JSON.
25968      */
25969     /**
25970      * @cfg {DataReader} errorReader
25971      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
25972      * This is completely optional as there is built-in support for processing JSON.
25973      */
25974     /**
25975      * @cfg {String} url
25976      * The URL to use for form actions if one isn't supplied in the action options.
25977      */
25978     /**
25979      * @cfg {Boolean} fileUpload
25980      * Set to true if this form is a file upload.
25981      */
25982     /**
25983      * @cfg {Object} baseParams
25984      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
25985      */
25986     /**
25987      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
25988      */
25989     timeout: 30,
25990
25991     // private
25992     activeAction : null,
25993
25994     /**
25995      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
25996      * or setValues() data instead of when the form was first created.
25997      */
25998     trackResetOnLoad : false,
25999     
26000     
26001     /**
26002      * childForms - used for multi-tab forms
26003      * @type {Array}
26004      */
26005     childForms : false,
26006     
26007     /**
26008      * allItems - full list of fields.
26009      * @type {Array}
26010      */
26011     allItems : false,
26012     
26013     /**
26014      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
26015      * element by passing it or its id or mask the form itself by passing in true.
26016      * @type Mixed
26017      */
26018     waitMsgTarget : undefined,
26019
26020     // private
26021     initEl : function(el){
26022         this.el = Roo.get(el);
26023         this.id = this.el.id || Roo.id();
26024         this.el.on('submit', this.onSubmit, this);
26025         this.el.addClass('x-form');
26026     },
26027
26028     // private
26029     onSubmit : function(e){
26030         e.stopEvent();
26031     },
26032
26033     /**
26034      * Returns true if client-side validation on the form is successful.
26035      * @return Boolean
26036      */
26037     isValid : function(){
26038         var valid = true;
26039         this.items.each(function(f){
26040            if(!f.validate()){
26041                valid = false;
26042            }
26043         });
26044         return valid;
26045     },
26046
26047     /**
26048      * Returns true if any fields in this form have changed since their original load.
26049      * @return Boolean
26050      */
26051     isDirty : function(){
26052         var dirty = false;
26053         this.items.each(function(f){
26054            if(f.isDirty()){
26055                dirty = true;
26056                return false;
26057            }
26058         });
26059         return dirty;
26060     },
26061
26062     /**
26063      * Performs a predefined action (submit or load) or custom actions you define on this form.
26064      * @param {String} actionName The name of the action type
26065      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
26066      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
26067      * accept other config options):
26068      * <pre>
26069 Property          Type             Description
26070 ----------------  ---------------  ----------------------------------------------------------------------------------
26071 url               String           The url for the action (defaults to the form's url)
26072 method            String           The form method to use (defaults to the form's method, or POST if not defined)
26073 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
26074 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
26075                                    validate the form on the client (defaults to false)
26076      * </pre>
26077      * @return {BasicForm} this
26078      */
26079     doAction : function(action, options){
26080         if(typeof action == 'string'){
26081             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
26082         }
26083         if(this.fireEvent('beforeaction', this, action) !== false){
26084             this.beforeAction(action);
26085             action.run.defer(100, action);
26086         }
26087         return this;
26088     },
26089
26090     /**
26091      * Shortcut to do a submit action.
26092      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26093      * @return {BasicForm} this
26094      */
26095     submit : function(options){
26096         this.doAction('submit', options);
26097         return this;
26098     },
26099
26100     /**
26101      * Shortcut to do a load action.
26102      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
26103      * @return {BasicForm} this
26104      */
26105     load : function(options){
26106         this.doAction('load', options);
26107         return this;
26108     },
26109
26110     /**
26111      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
26112      * @param {Record} record The record to edit
26113      * @return {BasicForm} this
26114      */
26115     updateRecord : function(record){
26116         record.beginEdit();
26117         var fs = record.fields;
26118         fs.each(function(f){
26119             var field = this.findField(f.name);
26120             if(field){
26121                 record.set(f.name, field.getValue());
26122             }
26123         }, this);
26124         record.endEdit();
26125         return this;
26126     },
26127
26128     /**
26129      * Loads an Roo.data.Record into this form.
26130      * @param {Record} record The record to load
26131      * @return {BasicForm} this
26132      */
26133     loadRecord : function(record){
26134         this.setValues(record.data);
26135         return this;
26136     },
26137
26138     // private
26139     beforeAction : function(action){
26140         var o = action.options;
26141         if(o.waitMsg){
26142             if(this.waitMsgTarget === true){
26143                 this.el.mask(o.waitMsg, 'x-mask-loading');
26144             }else if(this.waitMsgTarget){
26145                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
26146                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
26147             }else{
26148                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
26149             }
26150         }
26151     },
26152
26153     // private
26154     afterAction : function(action, success){
26155         this.activeAction = null;
26156         var o = action.options;
26157         if(o.waitMsg){
26158             if(this.waitMsgTarget === true){
26159                 this.el.unmask();
26160             }else if(this.waitMsgTarget){
26161                 this.waitMsgTarget.unmask();
26162             }else{
26163                 Roo.MessageBox.updateProgress(1);
26164                 Roo.MessageBox.hide();
26165             }
26166         }
26167         if(success){
26168             if(o.reset){
26169                 this.reset();
26170             }
26171             Roo.callback(o.success, o.scope, [this, action]);
26172             this.fireEvent('actioncomplete', this, action);
26173         }else{
26174             Roo.callback(o.failure, o.scope, [this, action]);
26175             this.fireEvent('actionfailed', this, action);
26176         }
26177     },
26178
26179     /**
26180      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
26181      * @param {String} id The value to search for
26182      * @return Field
26183      */
26184     findField : function(id){
26185         var field = this.items.get(id);
26186         if(!field){
26187             this.items.each(function(f){
26188                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
26189                     field = f;
26190                     return false;
26191                 }
26192             });
26193         }
26194         return field || null;
26195     },
26196
26197     /**
26198      * Add a secondary form to this one, 
26199      * Used to provide tabbed forms. One form is primary, with hidden values 
26200      * which mirror the elements from the other forms.
26201      * 
26202      * @param {Roo.form.Form} form to add.
26203      * 
26204      */
26205     addForm : function(form)
26206     {
26207        
26208         if (this.childForms.indexOf(form) > -1) {
26209             // already added..
26210             return;
26211         }
26212         this.childForms.push(form);
26213         var n = '';
26214         Roo.each(form.allItems, function (fe) {
26215             
26216             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
26217             if (this.findField(n)) { // already added..
26218                 return;
26219             }
26220             var add = new Roo.form.Hidden({
26221                 name : n
26222             });
26223             add.render(this.el);
26224             
26225             this.add( add );
26226         }, this);
26227         
26228     },
26229     /**
26230      * Mark fields in this form invalid in bulk.
26231      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
26232      * @return {BasicForm} this
26233      */
26234     markInvalid : function(errors){
26235         if(errors instanceof Array){
26236             for(var i = 0, len = errors.length; i < len; i++){
26237                 var fieldError = errors[i];
26238                 var f = this.findField(fieldError.id);
26239                 if(f){
26240                     f.markInvalid(fieldError.msg);
26241                 }
26242             }
26243         }else{
26244             var field, id;
26245             for(id in errors){
26246                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
26247                     field.markInvalid(errors[id]);
26248                 }
26249             }
26250         }
26251         Roo.each(this.childForms || [], function (f) {
26252             f.markInvalid(errors);
26253         });
26254         
26255         return this;
26256     },
26257
26258     /**
26259      * Set values for fields in this form in bulk.
26260      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
26261      * @return {BasicForm} this
26262      */
26263     setValues : function(values){
26264         if(values instanceof Array){ // array of objects
26265             for(var i = 0, len = values.length; i < len; i++){
26266                 var v = values[i];
26267                 var f = this.findField(v.id);
26268                 if(f){
26269                     f.setValue(v.value);
26270                     if(this.trackResetOnLoad){
26271                         f.originalValue = f.getValue();
26272                     }
26273                 }
26274             }
26275         }else{ // object hash
26276             var field, id;
26277             for(id in values){
26278                 if(typeof values[id] != 'function' && (field = this.findField(id))){
26279                     
26280                     if (field.setFromData && 
26281                         field.valueField && 
26282                         field.displayField &&
26283                         // combos' with local stores can 
26284                         // be queried via setValue()
26285                         // to set their value..
26286                         (field.store && !field.store.isLocal)
26287                         ) {
26288                         // it's a combo
26289                         var sd = { };
26290                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
26291                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
26292                         field.setFromData(sd);
26293                         
26294                     } else {
26295                         field.setValue(values[id]);
26296                     }
26297                     
26298                     
26299                     if(this.trackResetOnLoad){
26300                         field.originalValue = field.getValue();
26301                     }
26302                 }
26303             }
26304         }
26305          
26306         Roo.each(this.childForms || [], function (f) {
26307             f.setValues(values);
26308         });
26309                 
26310         return this;
26311     },
26312
26313     /**
26314      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
26315      * they are returned as an array.
26316      * @param {Boolean} asString
26317      * @return {Object}
26318      */
26319     getValues : function(asString){
26320         if (this.childForms) {
26321             // copy values from the child forms
26322             Roo.each(this.childForms, function (f) {
26323                 this.setValues(f.getValues());
26324             }, this);
26325         }
26326         
26327         
26328         
26329         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
26330         if(asString === true){
26331             return fs;
26332         }
26333         return Roo.urlDecode(fs);
26334     },
26335     
26336     /**
26337      * Returns the fields in this form as an object with key/value pairs. 
26338      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
26339      * @return {Object}
26340      */
26341     getFieldValues : function()
26342     {
26343         if (this.childForms) {
26344             // copy values from the child forms
26345             Roo.each(this.childForms, function (f) {
26346                 this.setValues(f.getValues());
26347             }, this);
26348         }
26349         
26350         var ret = {};
26351         this.items.each(function(f){
26352             if (!f.getName()) {
26353                 return;
26354             }
26355             var v = f.getValue();
26356             if ((typeof(v) == 'object') && f.getRawValue) {
26357                 v = f.getRawValue() ; // dates..
26358             }
26359             ret[f.getName()] = v;
26360         });
26361         
26362         return ret;
26363     },
26364
26365     /**
26366      * Clears all invalid messages in this form.
26367      * @return {BasicForm} this
26368      */
26369     clearInvalid : function(){
26370         this.items.each(function(f){
26371            f.clearInvalid();
26372         });
26373         
26374         Roo.each(this.childForms || [], function (f) {
26375             f.clearInvalid();
26376         });
26377         
26378         
26379         return this;
26380     },
26381
26382     /**
26383      * Resets this form.
26384      * @return {BasicForm} this
26385      */
26386     reset : function(){
26387         this.items.each(function(f){
26388             f.reset();
26389         });
26390         
26391         Roo.each(this.childForms || [], function (f) {
26392             f.reset();
26393         });
26394        
26395         
26396         return this;
26397     },
26398
26399     /**
26400      * Add Roo.form components to this form.
26401      * @param {Field} field1
26402      * @param {Field} field2 (optional)
26403      * @param {Field} etc (optional)
26404      * @return {BasicForm} this
26405      */
26406     add : function(){
26407         this.items.addAll(Array.prototype.slice.call(arguments, 0));
26408         return this;
26409     },
26410
26411
26412     /**
26413      * Removes a field from the items collection (does NOT remove its markup).
26414      * @param {Field} field
26415      * @return {BasicForm} this
26416      */
26417     remove : function(field){
26418         this.items.remove(field);
26419         return this;
26420     },
26421
26422     /**
26423      * Looks at the fields in this form, checks them for an id attribute,
26424      * and calls applyTo on the existing dom element with that id.
26425      * @return {BasicForm} this
26426      */
26427     render : function(){
26428         this.items.each(function(f){
26429             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
26430                 f.applyTo(f.id);
26431             }
26432         });
26433         return this;
26434     },
26435
26436     /**
26437      * Calls {@link Ext#apply} for all fields in this form with the passed object.
26438      * @param {Object} values
26439      * @return {BasicForm} this
26440      */
26441     applyToFields : function(o){
26442         this.items.each(function(f){
26443            Roo.apply(f, o);
26444         });
26445         return this;
26446     },
26447
26448     /**
26449      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
26450      * @param {Object} values
26451      * @return {BasicForm} this
26452      */
26453     applyIfToFields : function(o){
26454         this.items.each(function(f){
26455            Roo.applyIf(f, o);
26456         });
26457         return this;
26458     }
26459 });
26460
26461 // back compat
26462 Roo.BasicForm = Roo.form.BasicForm;/*
26463  * Based on:
26464  * Ext JS Library 1.1.1
26465  * Copyright(c) 2006-2007, Ext JS, LLC.
26466  *
26467  * Originally Released Under LGPL - original licence link has changed is not relivant.
26468  *
26469  * Fork - LGPL
26470  * <script type="text/javascript">
26471  */
26472
26473 /**
26474  * @class Roo.form.Form
26475  * @extends Roo.form.BasicForm
26476  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
26477  * @constructor
26478  * @param {Object} config Configuration options
26479  */
26480 Roo.form.Form = function(config){
26481     var xitems =  [];
26482     if (config.items) {
26483         xitems = config.items;
26484         delete config.items;
26485     }
26486    
26487     
26488     Roo.form.Form.superclass.constructor.call(this, null, config);
26489     this.url = this.url || this.action;
26490     if(!this.root){
26491         this.root = new Roo.form.Layout(Roo.applyIf({
26492             id: Roo.id()
26493         }, config));
26494     }
26495     this.active = this.root;
26496     /**
26497      * Array of all the buttons that have been added to this form via {@link addButton}
26498      * @type Array
26499      */
26500     this.buttons = [];
26501     this.allItems = [];
26502     this.addEvents({
26503         /**
26504          * @event clientvalidation
26505          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
26506          * @param {Form} this
26507          * @param {Boolean} valid true if the form has passed client-side validation
26508          */
26509         clientvalidation: true,
26510         /**
26511          * @event rendered
26512          * Fires when the form is rendered
26513          * @param {Roo.form.Form} form
26514          */
26515         rendered : true
26516     });
26517     
26518     if (this.progressUrl) {
26519             // push a hidden field onto the list of fields..
26520             this.addxtype( {
26521                     xns: Roo.form, 
26522                     xtype : 'Hidden', 
26523                     name : 'UPLOAD_IDENTIFIER' 
26524             });
26525         }
26526         
26527     
26528     Roo.each(xitems, this.addxtype, this);
26529     
26530     
26531     
26532 };
26533
26534 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
26535     /**
26536      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
26537      */
26538     /**
26539      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
26540      */
26541     /**
26542      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
26543      */
26544     buttonAlign:'center',
26545
26546     /**
26547      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
26548      */
26549     minButtonWidth:75,
26550
26551     /**
26552      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
26553      * This property cascades to child containers if not set.
26554      */
26555     labelAlign:'left',
26556
26557     /**
26558      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
26559      * fires a looping event with that state. This is required to bind buttons to the valid
26560      * state using the config value formBind:true on the button.
26561      */
26562     monitorValid : false,
26563
26564     /**
26565      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
26566      */
26567     monitorPoll : 200,
26568     
26569     /**
26570      * @cfg {String} progressUrl - Url to return progress data 
26571      */
26572     
26573     progressUrl : false,
26574   
26575     /**
26576      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
26577      * fields are added and the column is closed. If no fields are passed the column remains open
26578      * until end() is called.
26579      * @param {Object} config The config to pass to the column
26580      * @param {Field} field1 (optional)
26581      * @param {Field} field2 (optional)
26582      * @param {Field} etc (optional)
26583      * @return Column The column container object
26584      */
26585     column : function(c){
26586         var col = new Roo.form.Column(c);
26587         this.start(col);
26588         if(arguments.length > 1){ // duplicate code required because of Opera
26589             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26590             this.end();
26591         }
26592         return col;
26593     },
26594
26595     /**
26596      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
26597      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
26598      * until end() is called.
26599      * @param {Object} config The config to pass to the fieldset
26600      * @param {Field} field1 (optional)
26601      * @param {Field} field2 (optional)
26602      * @param {Field} etc (optional)
26603      * @return FieldSet The fieldset container object
26604      */
26605     fieldset : function(c){
26606         var fs = new Roo.form.FieldSet(c);
26607         this.start(fs);
26608         if(arguments.length > 1){ // duplicate code required because of Opera
26609             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26610             this.end();
26611         }
26612         return fs;
26613     },
26614
26615     /**
26616      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
26617      * fields are added and the container is closed. If no fields are passed the container remains open
26618      * until end() is called.
26619      * @param {Object} config The config to pass to the Layout
26620      * @param {Field} field1 (optional)
26621      * @param {Field} field2 (optional)
26622      * @param {Field} etc (optional)
26623      * @return Layout The container object
26624      */
26625     container : function(c){
26626         var l = new Roo.form.Layout(c);
26627         this.start(l);
26628         if(arguments.length > 1){ // duplicate code required because of Opera
26629             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
26630             this.end();
26631         }
26632         return l;
26633     },
26634
26635     /**
26636      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
26637      * @param {Object} container A Roo.form.Layout or subclass of Layout
26638      * @return {Form} this
26639      */
26640     start : function(c){
26641         // cascade label info
26642         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
26643         this.active.stack.push(c);
26644         c.ownerCt = this.active;
26645         this.active = c;
26646         return this;
26647     },
26648
26649     /**
26650      * Closes the current open container
26651      * @return {Form} this
26652      */
26653     end : function(){
26654         if(this.active == this.root){
26655             return this;
26656         }
26657         this.active = this.active.ownerCt;
26658         return this;
26659     },
26660
26661     /**
26662      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
26663      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
26664      * as the label of the field.
26665      * @param {Field} field1
26666      * @param {Field} field2 (optional)
26667      * @param {Field} etc. (optional)
26668      * @return {Form} this
26669      */
26670     add : function(){
26671         this.active.stack.push.apply(this.active.stack, arguments);
26672         this.allItems.push.apply(this.allItems,arguments);
26673         var r = [];
26674         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
26675             if(a[i].isFormField){
26676                 r.push(a[i]);
26677             }
26678         }
26679         if(r.length > 0){
26680             Roo.form.Form.superclass.add.apply(this, r);
26681         }
26682         return this;
26683     },
26684     
26685
26686     
26687     
26688     
26689      /**
26690      * Find any element that has been added to a form, using it's ID or name
26691      * This can include framesets, columns etc. along with regular fields..
26692      * @param {String} id - id or name to find.
26693      
26694      * @return {Element} e - or false if nothing found.
26695      */
26696     findbyId : function(id)
26697     {
26698         var ret = false;
26699         if (!id) {
26700             return ret;
26701         }
26702         Ext.each(this.allItems, function(f){
26703             if (f.id == id || f.name == id ){
26704                 ret = f;
26705                 return false;
26706             }
26707         });
26708         return ret;
26709     },
26710
26711     
26712     
26713     /**
26714      * Render this form into the passed container. This should only be called once!
26715      * @param {String/HTMLElement/Element} container The element this component should be rendered into
26716      * @return {Form} this
26717      */
26718     render : function(ct)
26719     {
26720         
26721         
26722         
26723         ct = Roo.get(ct);
26724         var o = this.autoCreate || {
26725             tag: 'form',
26726             method : this.method || 'POST',
26727             id : this.id || Roo.id()
26728         };
26729         this.initEl(ct.createChild(o));
26730
26731         this.root.render(this.el);
26732         
26733        
26734              
26735         this.items.each(function(f){
26736             f.render('x-form-el-'+f.id);
26737         });
26738
26739         if(this.buttons.length > 0){
26740             // tables are required to maintain order and for correct IE layout
26741             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
26742                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
26743                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
26744             }}, null, true);
26745             var tr = tb.getElementsByTagName('tr')[0];
26746             for(var i = 0, len = this.buttons.length; i < len; i++) {
26747                 var b = this.buttons[i];
26748                 var td = document.createElement('td');
26749                 td.className = 'x-form-btn-td';
26750                 b.render(tr.appendChild(td));
26751             }
26752         }
26753         if(this.monitorValid){ // initialize after render
26754             this.startMonitoring();
26755         }
26756         this.fireEvent('rendered', this);
26757         return this;
26758     },
26759
26760     /**
26761      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
26762      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
26763      * object or a valid Roo.DomHelper element config
26764      * @param {Function} handler The function called when the button is clicked
26765      * @param {Object} scope (optional) The scope of the handler function
26766      * @return {Roo.Button}
26767      */
26768     addButton : function(config, handler, scope){
26769         var bc = {
26770             handler: handler,
26771             scope: scope,
26772             minWidth: this.minButtonWidth,
26773             hideParent:true
26774         };
26775         if(typeof config == "string"){
26776             bc.text = config;
26777         }else{
26778             Roo.apply(bc, config);
26779         }
26780         var btn = new Roo.Button(null, bc);
26781         this.buttons.push(btn);
26782         return btn;
26783     },
26784
26785      /**
26786      * Adds a series of form elements (using the xtype property as the factory method.
26787      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
26788      * @param {Object} config 
26789      */
26790     
26791     addxtype : function()
26792     {
26793         var ar = Array.prototype.slice.call(arguments, 0);
26794         var ret = false;
26795         for(var i = 0; i < ar.length; i++) {
26796             if (!ar[i]) {
26797                 continue; // skip -- if this happends something invalid got sent, we 
26798                 // should ignore it, as basically that interface element will not show up
26799                 // and that should be pretty obvious!!
26800             }
26801             
26802             if (Roo.form[ar[i].xtype]) {
26803                 ar[i].form = this;
26804                 var fe = Roo.factory(ar[i], Roo.form);
26805                 if (!ret) {
26806                     ret = fe;
26807                 }
26808                 fe.form = this;
26809                 if (fe.store) {
26810                     fe.store.form = this;
26811                 }
26812                 if (fe.isLayout) {  
26813                          
26814                     this.start(fe);
26815                     this.allItems.push(fe);
26816                     if (fe.items && fe.addxtype) {
26817                         fe.addxtype.apply(fe, fe.items);
26818                         delete fe.items;
26819                     }
26820                      this.end();
26821                     continue;
26822                 }
26823                 
26824                 
26825                  
26826                 this.add(fe);
26827               //  console.log('adding ' + ar[i].xtype);
26828             }
26829             if (ar[i].xtype == 'Button') {  
26830                 //console.log('adding button');
26831                 //console.log(ar[i]);
26832                 this.addButton(ar[i]);
26833                 this.allItems.push(fe);
26834                 continue;
26835             }
26836             
26837             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
26838                 alert('end is not supported on xtype any more, use items');
26839             //    this.end();
26840             //    //console.log('adding end');
26841             }
26842             
26843         }
26844         return ret;
26845     },
26846     
26847     /**
26848      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26849      * option "monitorValid"
26850      */
26851     startMonitoring : function(){
26852         if(!this.bound){
26853             this.bound = true;
26854             Roo.TaskMgr.start({
26855                 run : this.bindHandler,
26856                 interval : this.monitorPoll || 200,
26857                 scope: this
26858             });
26859         }
26860     },
26861
26862     /**
26863      * Stops monitoring of the valid state of this form
26864      */
26865     stopMonitoring : function(){
26866         this.bound = false;
26867     },
26868
26869     // private
26870     bindHandler : function(){
26871         if(!this.bound){
26872             return false; // stops binding
26873         }
26874         var valid = true;
26875         this.items.each(function(f){
26876             if(!f.isValid(true)){
26877                 valid = false;
26878                 return false;
26879             }
26880         });
26881         for(var i = 0, len = this.buttons.length; i < len; i++){
26882             var btn = this.buttons[i];
26883             if(btn.formBind === true && btn.disabled === valid){
26884                 btn.setDisabled(!valid);
26885             }
26886         }
26887         this.fireEvent('clientvalidation', this, valid);
26888     }
26889     
26890     
26891     
26892     
26893     
26894     
26895     
26896     
26897 });
26898
26899
26900 // back compat
26901 Roo.Form = Roo.form.Form;
26902 /*
26903  * Based on:
26904  * Ext JS Library 1.1.1
26905  * Copyright(c) 2006-2007, Ext JS, LLC.
26906  *
26907  * Originally Released Under LGPL - original licence link has changed is not relivant.
26908  *
26909  * Fork - LGPL
26910  * <script type="text/javascript">
26911  */
26912  
26913  /**
26914  * @class Roo.form.Action
26915  * Internal Class used to handle form actions
26916  * @constructor
26917  * @param {Roo.form.BasicForm} el The form element or its id
26918  * @param {Object} config Configuration options
26919  */
26920  
26921  
26922 // define the action interface
26923 Roo.form.Action = function(form, options){
26924     this.form = form;
26925     this.options = options || {};
26926 };
26927 /**
26928  * Client Validation Failed
26929  * @const 
26930  */
26931 Roo.form.Action.CLIENT_INVALID = 'client';
26932 /**
26933  * Server Validation Failed
26934  * @const 
26935  */
26936  Roo.form.Action.SERVER_INVALID = 'server';
26937  /**
26938  * Connect to Server Failed
26939  * @const 
26940  */
26941 Roo.form.Action.CONNECT_FAILURE = 'connect';
26942 /**
26943  * Reading Data from Server Failed
26944  * @const 
26945  */
26946 Roo.form.Action.LOAD_FAILURE = 'load';
26947
26948 Roo.form.Action.prototype = {
26949     type : 'default',
26950     failureType : undefined,
26951     response : undefined,
26952     result : undefined,
26953
26954     // interface method
26955     run : function(options){
26956
26957     },
26958
26959     // interface method
26960     success : function(response){
26961
26962     },
26963
26964     // interface method
26965     handleResponse : function(response){
26966
26967     },
26968
26969     // default connection failure
26970     failure : function(response){
26971         this.response = response;
26972         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26973         this.form.afterAction(this, false);
26974     },
26975
26976     processResponse : function(response){
26977         this.response = response;
26978         if(!response.responseText){
26979             return true;
26980         }
26981         this.result = this.handleResponse(response);
26982         return this.result;
26983     },
26984
26985     // utility functions used internally
26986     getUrl : function(appendParams){
26987         var url = this.options.url || this.form.url || this.form.el.dom.action;
26988         if(appendParams){
26989             var p = this.getParams();
26990             if(p){
26991                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26992             }
26993         }
26994         return url;
26995     },
26996
26997     getMethod : function(){
26998         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26999     },
27000
27001     getParams : function(){
27002         var bp = this.form.baseParams;
27003         var p = this.options.params;
27004         if(p){
27005             if(typeof p == "object"){
27006                 p = Roo.urlEncode(Roo.applyIf(p, bp));
27007             }else if(typeof p == 'string' && bp){
27008                 p += '&' + Roo.urlEncode(bp);
27009             }
27010         }else if(bp){
27011             p = Roo.urlEncode(bp);
27012         }
27013         return p;
27014     },
27015
27016     createCallback : function(){
27017         return {
27018             success: this.success,
27019             failure: this.failure,
27020             scope: this,
27021             timeout: (this.form.timeout*1000),
27022             upload: this.form.fileUpload ? this.success : undefined
27023         };
27024     }
27025 };
27026
27027 Roo.form.Action.Submit = function(form, options){
27028     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
27029 };
27030
27031 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
27032     type : 'submit',
27033
27034     haveProgress : false,
27035     uploadComplete : false,
27036     
27037     // uploadProgress indicator.
27038     uploadProgress : function()
27039     {
27040         if (!this.form.progressUrl) {
27041             return;
27042         }
27043         
27044         if (!this.haveProgress) {
27045             Roo.MessageBox.progress("Uploading", "Uploading");
27046         }
27047         if (this.uploadComplete) {
27048            Roo.MessageBox.hide();
27049            return;
27050         }
27051         
27052         this.haveProgress = true;
27053    
27054         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
27055         
27056         var c = new Roo.data.Connection();
27057         c.request({
27058             url : this.form.progressUrl,
27059             params: {
27060                 id : uid
27061             },
27062             method: 'GET',
27063             success : function(req){
27064                //console.log(data);
27065                 var rdata = false;
27066                 var edata;
27067                 try  {
27068                    rdata = Roo.decode(req.responseText)
27069                 } catch (e) {
27070                     Roo.log("Invalid data from server..");
27071                     Roo.log(edata);
27072                     return;
27073                 }
27074                 if (!rdata || !rdata.success) {
27075                     Roo.log(rdata);
27076                     return;
27077                 }
27078                 var data = rdata.data;
27079                 
27080                 if (this.uploadComplete) {
27081                    Roo.MessageBox.hide();
27082                    return;
27083                 }
27084                    
27085                 if (data){
27086                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
27087                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
27088                     );
27089                 }
27090                 this.uploadProgress.defer(2000,this);
27091             },
27092        
27093             failure: function(data) {
27094                 Roo.log('progress url failed ');
27095                 Roo.log(data);
27096             },
27097             scope : this
27098         });
27099            
27100     },
27101     
27102     
27103     run : function()
27104     {
27105         // run get Values on the form, so it syncs any secondary forms.
27106         this.form.getValues();
27107         
27108         var o = this.options;
27109         var method = this.getMethod();
27110         var isPost = method == 'POST';
27111         if(o.clientValidation === false || this.form.isValid()){
27112             
27113             if (this.form.progressUrl) {
27114                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
27115                     (new Date() * 1) + '' + Math.random());
27116                     
27117             } 
27118             
27119             Roo.Ajax.request(Roo.apply(this.createCallback(), {
27120                 form:this.form.el.dom,
27121                 url:this.getUrl(!isPost),
27122                 method: method,
27123                 params:isPost ? this.getParams() : null,
27124                 isUpload: this.form.fileUpload
27125             }));
27126             
27127             this.uploadProgress();
27128
27129         }else if (o.clientValidation !== false){ // client validation failed
27130             this.failureType = Roo.form.Action.CLIENT_INVALID;
27131             this.form.afterAction(this, false);
27132         }
27133     },
27134
27135     success : function(response)
27136     {
27137         this.uploadComplete= true;
27138         if (this.haveProgress) {
27139             Roo.MessageBox.hide();
27140         }
27141         
27142         var result = this.processResponse(response);
27143         if(result === true || result.success){
27144             this.form.afterAction(this, true);
27145             return;
27146         }
27147         if(result.errors){
27148             this.form.markInvalid(result.errors);
27149             this.failureType = Roo.form.Action.SERVER_INVALID;
27150         }
27151         this.form.afterAction(this, false);
27152     },
27153     failure : function(response)
27154     {
27155         this.uploadComplete= true;
27156         if (this.haveProgress) {
27157             Roo.MessageBox.hide();
27158         }
27159         
27160         this.response = response;
27161         this.failureType = Roo.form.Action.CONNECT_FAILURE;
27162         this.form.afterAction(this, false);
27163     },
27164     
27165     handleResponse : function(response){
27166         if(this.form.errorReader){
27167             var rs = this.form.errorReader.read(response);
27168             var errors = [];
27169             if(rs.records){
27170                 for(var i = 0, len = rs.records.length; i < len; i++) {
27171                     var r = rs.records[i];
27172                     errors[i] = r.data;
27173                 }
27174             }
27175             if(errors.length < 1){
27176                 errors = null;
27177             }
27178             return {
27179                 success : rs.success,
27180                 errors : errors
27181             };
27182         }
27183         var ret = false;
27184         try {
27185             ret = Roo.decode(response.responseText);
27186         } catch (e) {
27187             ret = {
27188                 success: false,
27189                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
27190                 errors : []
27191             };
27192         }
27193         return ret;
27194         
27195     }
27196 });
27197
27198
27199 Roo.form.Action.Load = function(form, options){
27200     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
27201     this.reader = this.form.reader;
27202 };
27203
27204 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
27205     type : 'load',
27206
27207     run : function(){
27208         Roo.Ajax.request(Roo.apply(
27209                 this.createCallback(), {
27210                     method:this.getMethod(),
27211                     url:this.getUrl(false),
27212                     params:this.getParams()
27213         }));
27214     },
27215
27216     success : function(response){
27217         var result = this.processResponse(response);
27218         if(result === true || !result.success || !result.data){
27219             this.failureType = Roo.form.Action.LOAD_FAILURE;
27220             this.form.afterAction(this, false);
27221             return;
27222         }
27223         this.form.clearInvalid();
27224         this.form.setValues(result.data);
27225         this.form.afterAction(this, true);
27226     },
27227
27228     handleResponse : function(response){
27229         if(this.form.reader){
27230             var rs = this.form.reader.read(response);
27231             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
27232             return {
27233                 success : rs.success,
27234                 data : data
27235             };
27236         }
27237         return Roo.decode(response.responseText);
27238     }
27239 });
27240
27241 Roo.form.Action.ACTION_TYPES = {
27242     'load' : Roo.form.Action.Load,
27243     'submit' : Roo.form.Action.Submit
27244 };/*
27245  * Based on:
27246  * Ext JS Library 1.1.1
27247  * Copyright(c) 2006-2007, Ext JS, LLC.
27248  *
27249  * Originally Released Under LGPL - original licence link has changed is not relivant.
27250  *
27251  * Fork - LGPL
27252  * <script type="text/javascript">
27253  */
27254  
27255 /**
27256  * @class Roo.form.Layout
27257  * @extends Roo.Component
27258  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
27259  * @constructor
27260  * @param {Object} config Configuration options
27261  */
27262 Roo.form.Layout = function(config){
27263     var xitems = [];
27264     if (config.items) {
27265         xitems = config.items;
27266         delete config.items;
27267     }
27268     Roo.form.Layout.superclass.constructor.call(this, config);
27269     this.stack = [];
27270     Roo.each(xitems, this.addxtype, this);
27271      
27272 };
27273
27274 Roo.extend(Roo.form.Layout, Roo.Component, {
27275     /**
27276      * @cfg {String/Object} autoCreate
27277      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
27278      */
27279     /**
27280      * @cfg {String/Object/Function} style
27281      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
27282      * a function which returns such a specification.
27283      */
27284     /**
27285      * @cfg {String} labelAlign
27286      * Valid values are "left," "top" and "right" (defaults to "left")
27287      */
27288     /**
27289      * @cfg {Number} labelWidth
27290      * Fixed width in pixels of all field labels (defaults to undefined)
27291      */
27292     /**
27293      * @cfg {Boolean} clear
27294      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
27295      */
27296     clear : true,
27297     /**
27298      * @cfg {String} labelSeparator
27299      * The separator to use after field labels (defaults to ':')
27300      */
27301     labelSeparator : ':',
27302     /**
27303      * @cfg {Boolean} hideLabels
27304      * True to suppress the display of field labels in this layout (defaults to false)
27305      */
27306     hideLabels : false,
27307
27308     // private
27309     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
27310     
27311     isLayout : true,
27312     
27313     // private
27314     onRender : function(ct, position){
27315         if(this.el){ // from markup
27316             this.el = Roo.get(this.el);
27317         }else {  // generate
27318             var cfg = this.getAutoCreate();
27319             this.el = ct.createChild(cfg, position);
27320         }
27321         if(this.style){
27322             this.el.applyStyles(this.style);
27323         }
27324         if(this.labelAlign){
27325             this.el.addClass('x-form-label-'+this.labelAlign);
27326         }
27327         if(this.hideLabels){
27328             this.labelStyle = "display:none";
27329             this.elementStyle = "padding-left:0;";
27330         }else{
27331             if(typeof this.labelWidth == 'number'){
27332                 this.labelStyle = "width:"+this.labelWidth+"px;";
27333                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
27334             }
27335             if(this.labelAlign == 'top'){
27336                 this.labelStyle = "width:auto;";
27337                 this.elementStyle = "padding-left:0;";
27338             }
27339         }
27340         var stack = this.stack;
27341         var slen = stack.length;
27342         if(slen > 0){
27343             if(!this.fieldTpl){
27344                 var t = new Roo.Template(
27345                     '<div class="x-form-item {5}">',
27346                         '<label for="{0}" style="{2}">{1}{4}</label>',
27347                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27348                         '</div>',
27349                     '</div><div class="x-form-clear-left"></div>'
27350                 );
27351                 t.disableFormats = true;
27352                 t.compile();
27353                 Roo.form.Layout.prototype.fieldTpl = t;
27354             }
27355             for(var i = 0; i < slen; i++) {
27356                 if(stack[i].isFormField){
27357                     this.renderField(stack[i]);
27358                 }else{
27359                     this.renderComponent(stack[i]);
27360                 }
27361             }
27362         }
27363         if(this.clear){
27364             this.el.createChild({cls:'x-form-clear'});
27365         }
27366     },
27367
27368     // private
27369     renderField : function(f){
27370         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
27371                f.id, //0
27372                f.fieldLabel, //1
27373                f.labelStyle||this.labelStyle||'', //2
27374                this.elementStyle||'', //3
27375                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
27376                f.itemCls||this.itemCls||''  //5
27377        ], true).getPrevSibling());
27378     },
27379
27380     // private
27381     renderComponent : function(c){
27382         c.render(c.isLayout ? this.el : this.el.createChild());    
27383     },
27384     /**
27385      * Adds a object form elements (using the xtype property as the factory method.)
27386      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
27387      * @param {Object} config 
27388      */
27389     addxtype : function(o)
27390     {
27391         // create the lement.
27392         o.form = this.form;
27393         var fe = Roo.factory(o, Roo.form);
27394         this.form.allItems.push(fe);
27395         this.stack.push(fe);
27396         
27397         if (fe.isFormField) {
27398             this.form.items.add(fe);
27399         }
27400          
27401         return fe;
27402     }
27403 });
27404
27405 /**
27406  * @class Roo.form.Column
27407  * @extends Roo.form.Layout
27408  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
27409  * @constructor
27410  * @param {Object} config Configuration options
27411  */
27412 Roo.form.Column = function(config){
27413     Roo.form.Column.superclass.constructor.call(this, config);
27414 };
27415
27416 Roo.extend(Roo.form.Column, Roo.form.Layout, {
27417     /**
27418      * @cfg {Number/String} width
27419      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27420      */
27421     /**
27422      * @cfg {String/Object} autoCreate
27423      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
27424      */
27425
27426     // private
27427     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
27428
27429     // private
27430     onRender : function(ct, position){
27431         Roo.form.Column.superclass.onRender.call(this, ct, position);
27432         if(this.width){
27433             this.el.setWidth(this.width);
27434         }
27435     }
27436 });
27437
27438
27439 /**
27440  * @class Roo.form.Row
27441  * @extends Roo.form.Layout
27442  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
27443  * @constructor
27444  * @param {Object} config Configuration options
27445  */
27446
27447  
27448 Roo.form.Row = function(config){
27449     Roo.form.Row.superclass.constructor.call(this, config);
27450 };
27451  
27452 Roo.extend(Roo.form.Row, Roo.form.Layout, {
27453       /**
27454      * @cfg {Number/String} width
27455      * The fixed width of the column in pixels or CSS value (defaults to "auto")
27456      */
27457     /**
27458      * @cfg {Number/String} height
27459      * The fixed height of the column in pixels or CSS value (defaults to "auto")
27460      */
27461     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
27462     
27463     padWidth : 20,
27464     // private
27465     onRender : function(ct, position){
27466         //console.log('row render');
27467         if(!this.rowTpl){
27468             var t = new Roo.Template(
27469                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
27470                     '<label for="{0}" style="{2}">{1}{4}</label>',
27471                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
27472                     '</div>',
27473                 '</div>'
27474             );
27475             t.disableFormats = true;
27476             t.compile();
27477             Roo.form.Layout.prototype.rowTpl = t;
27478         }
27479         this.fieldTpl = this.rowTpl;
27480         
27481         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
27482         var labelWidth = 100;
27483         
27484         if ((this.labelAlign != 'top')) {
27485             if (typeof this.labelWidth == 'number') {
27486                 labelWidth = this.labelWidth
27487             }
27488             this.padWidth =  20 + labelWidth;
27489             
27490         }
27491         
27492         Roo.form.Column.superclass.onRender.call(this, ct, position);
27493         if(this.width){
27494             this.el.setWidth(this.width);
27495         }
27496         if(this.height){
27497             this.el.setHeight(this.height);
27498         }
27499     },
27500     
27501     // private
27502     renderField : function(f){
27503         f.fieldEl = this.fieldTpl.append(this.el, [
27504                f.id, f.fieldLabel,
27505                f.labelStyle||this.labelStyle||'',
27506                this.elementStyle||'',
27507                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
27508                f.itemCls||this.itemCls||'',
27509                f.width ? f.width + this.padWidth : 160 + this.padWidth
27510        ],true);
27511     }
27512 });
27513  
27514
27515 /**
27516  * @class Roo.form.FieldSet
27517  * @extends Roo.form.Layout
27518  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
27519  * @constructor
27520  * @param {Object} config Configuration options
27521  */
27522 Roo.form.FieldSet = function(config){
27523     Roo.form.FieldSet.superclass.constructor.call(this, config);
27524 };
27525
27526 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
27527     /**
27528      * @cfg {String} legend
27529      * The text to display as the legend for the FieldSet (defaults to '')
27530      */
27531     /**
27532      * @cfg {String/Object} autoCreate
27533      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
27534      */
27535
27536     // private
27537     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
27538
27539     // private
27540     onRender : function(ct, position){
27541         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
27542         if(this.legend){
27543             this.setLegend(this.legend);
27544         }
27545     },
27546
27547     // private
27548     setLegend : function(text){
27549         if(this.rendered){
27550             this.el.child('legend').update(text);
27551         }
27552     }
27553 });/*
27554  * Based on:
27555  * Ext JS Library 1.1.1
27556  * Copyright(c) 2006-2007, Ext JS, LLC.
27557  *
27558  * Originally Released Under LGPL - original licence link has changed is not relivant.
27559  *
27560  * Fork - LGPL
27561  * <script type="text/javascript">
27562  */
27563 /**
27564  * @class Roo.form.VTypes
27565  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
27566  * @singleton
27567  */
27568 Roo.form.VTypes = function(){
27569     // closure these in so they are only created once.
27570     var alpha = /^[a-zA-Z_]+$/;
27571     var alphanum = /^[a-zA-Z0-9_]+$/;
27572     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
27573     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
27574
27575     // All these messages and functions are configurable
27576     return {
27577         /**
27578          * The function used to validate email addresses
27579          * @param {String} value The email address
27580          */
27581         'email' : function(v){
27582             return email.test(v);
27583         },
27584         /**
27585          * The error text to display when the email validation function returns false
27586          * @type String
27587          */
27588         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
27589         /**
27590          * The keystroke filter mask to be applied on email input
27591          * @type RegExp
27592          */
27593         'emailMask' : /[a-z0-9_\.\-@]/i,
27594
27595         /**
27596          * The function used to validate URLs
27597          * @param {String} value The URL
27598          */
27599         'url' : function(v){
27600             return url.test(v);
27601         },
27602         /**
27603          * The error text to display when the url validation function returns false
27604          * @type String
27605          */
27606         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
27607         
27608         /**
27609          * The function used to validate alpha values
27610          * @param {String} value The value
27611          */
27612         'alpha' : function(v){
27613             return alpha.test(v);
27614         },
27615         /**
27616          * The error text to display when the alpha validation function returns false
27617          * @type String
27618          */
27619         'alphaText' : 'This field should only contain letters and _',
27620         /**
27621          * The keystroke filter mask to be applied on alpha input
27622          * @type RegExp
27623          */
27624         'alphaMask' : /[a-z_]/i,
27625
27626         /**
27627          * The function used to validate alphanumeric values
27628          * @param {String} value The value
27629          */
27630         'alphanum' : function(v){
27631             return alphanum.test(v);
27632         },
27633         /**
27634          * The error text to display when the alphanumeric validation function returns false
27635          * @type String
27636          */
27637         'alphanumText' : 'This field should only contain letters, numbers and _',
27638         /**
27639          * The keystroke filter mask to be applied on alphanumeric input
27640          * @type RegExp
27641          */
27642         'alphanumMask' : /[a-z0-9_]/i
27643     };
27644 }();//<script type="text/javascript">
27645
27646 /**
27647  * @class Roo.form.FCKeditor
27648  * @extends Roo.form.TextArea
27649  * Wrapper around the FCKEditor http://www.fckeditor.net
27650  * @constructor
27651  * Creates a new FCKeditor
27652  * @param {Object} config Configuration options
27653  */
27654 Roo.form.FCKeditor = function(config){
27655     Roo.form.FCKeditor.superclass.constructor.call(this, config);
27656     this.addEvents({
27657          /**
27658          * @event editorinit
27659          * Fired when the editor is initialized - you can add extra handlers here..
27660          * @param {FCKeditor} this
27661          * @param {Object} the FCK object.
27662          */
27663         editorinit : true
27664     });
27665     
27666     
27667 };
27668 Roo.form.FCKeditor.editors = { };
27669 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
27670 {
27671     //defaultAutoCreate : {
27672     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
27673     //},
27674     // private
27675     /**
27676      * @cfg {Object} fck options - see fck manual for details.
27677      */
27678     fckconfig : false,
27679     
27680     /**
27681      * @cfg {Object} fck toolbar set (Basic or Default)
27682      */
27683     toolbarSet : 'Basic',
27684     /**
27685      * @cfg {Object} fck BasePath
27686      */ 
27687     basePath : '/fckeditor/',
27688     
27689     
27690     frame : false,
27691     
27692     value : '',
27693     
27694    
27695     onRender : function(ct, position)
27696     {
27697         if(!this.el){
27698             this.defaultAutoCreate = {
27699                 tag: "textarea",
27700                 style:"width:300px;height:60px;",
27701                 autocomplete: "off"
27702             };
27703         }
27704         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
27705         /*
27706         if(this.grow){
27707             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
27708             if(this.preventScrollbars){
27709                 this.el.setStyle("overflow", "hidden");
27710             }
27711             this.el.setHeight(this.growMin);
27712         }
27713         */
27714         //console.log('onrender' + this.getId() );
27715         Roo.form.FCKeditor.editors[this.getId()] = this;
27716          
27717
27718         this.replaceTextarea() ;
27719         
27720     },
27721     
27722     getEditor : function() {
27723         return this.fckEditor;
27724     },
27725     /**
27726      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
27727      * @param {Mixed} value The value to set
27728      */
27729     
27730     
27731     setValue : function(value)
27732     {
27733         //console.log('setValue: ' + value);
27734         
27735         if(typeof(value) == 'undefined') { // not sure why this is happending...
27736             return;
27737         }
27738         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27739         
27740         //if(!this.el || !this.getEditor()) {
27741         //    this.value = value;
27742             //this.setValue.defer(100,this,[value]);    
27743         //    return;
27744         //} 
27745         
27746         if(!this.getEditor()) {
27747             return;
27748         }
27749         
27750         this.getEditor().SetData(value);
27751         
27752         //
27753
27754     },
27755
27756     /**
27757      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
27758      * @return {Mixed} value The field value
27759      */
27760     getValue : function()
27761     {
27762         
27763         if (this.frame && this.frame.dom.style.display == 'none') {
27764             return Roo.form.FCKeditor.superclass.getValue.call(this);
27765         }
27766         
27767         if(!this.el || !this.getEditor()) {
27768            
27769            // this.getValue.defer(100,this); 
27770             return this.value;
27771         }
27772        
27773         
27774         var value=this.getEditor().GetData();
27775         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
27776         return Roo.form.FCKeditor.superclass.getValue.call(this);
27777         
27778
27779     },
27780
27781     /**
27782      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
27783      * @return {Mixed} value The field value
27784      */
27785     getRawValue : function()
27786     {
27787         if (this.frame && this.frame.dom.style.display == 'none') {
27788             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27789         }
27790         
27791         if(!this.el || !this.getEditor()) {
27792             //this.getRawValue.defer(100,this); 
27793             return this.value;
27794             return;
27795         }
27796         
27797         
27798         
27799         var value=this.getEditor().GetData();
27800         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
27801         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
27802          
27803     },
27804     
27805     setSize : function(w,h) {
27806         
27807         
27808         
27809         //if (this.frame && this.frame.dom.style.display == 'none') {
27810         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27811         //    return;
27812         //}
27813         //if(!this.el || !this.getEditor()) {
27814         //    this.setSize.defer(100,this, [w,h]); 
27815         //    return;
27816         //}
27817         
27818         
27819         
27820         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
27821         
27822         this.frame.dom.setAttribute('width', w);
27823         this.frame.dom.setAttribute('height', h);
27824         this.frame.setSize(w,h);
27825         
27826     },
27827     
27828     toggleSourceEdit : function(value) {
27829         
27830       
27831          
27832         this.el.dom.style.display = value ? '' : 'none';
27833         this.frame.dom.style.display = value ?  'none' : '';
27834         
27835     },
27836     
27837     
27838     focus: function(tag)
27839     {
27840         if (this.frame.dom.style.display == 'none') {
27841             return Roo.form.FCKeditor.superclass.focus.call(this);
27842         }
27843         if(!this.el || !this.getEditor()) {
27844             this.focus.defer(100,this, [tag]); 
27845             return;
27846         }
27847         
27848         
27849         
27850         
27851         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27852         this.getEditor().Focus();
27853         if (tgs.length) {
27854             if (!this.getEditor().Selection.GetSelection()) {
27855                 this.focus.defer(100,this, [tag]); 
27856                 return;
27857             }
27858             
27859             
27860             var r = this.getEditor().EditorDocument.createRange();
27861             r.setStart(tgs[0],0);
27862             r.setEnd(tgs[0],0);
27863             this.getEditor().Selection.GetSelection().removeAllRanges();
27864             this.getEditor().Selection.GetSelection().addRange(r);
27865             this.getEditor().Focus();
27866         }
27867         
27868     },
27869     
27870     
27871     
27872     replaceTextarea : function()
27873     {
27874         if ( document.getElementById( this.getId() + '___Frame' ) )
27875             return ;
27876         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27877         //{
27878             // We must check the elements firstly using the Id and then the name.
27879         var oTextarea = document.getElementById( this.getId() );
27880         
27881         var colElementsByName = document.getElementsByName( this.getId() ) ;
27882          
27883         oTextarea.style.display = 'none' ;
27884
27885         if ( oTextarea.tabIndex ) {            
27886             this.TabIndex = oTextarea.tabIndex ;
27887         }
27888         
27889         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27890         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27891         this.frame = Roo.get(this.getId() + '___Frame')
27892     },
27893     
27894     _getConfigHtml : function()
27895     {
27896         var sConfig = '' ;
27897
27898         for ( var o in this.fckconfig ) {
27899             sConfig += sConfig.length > 0  ? '&amp;' : '';
27900             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27901         }
27902
27903         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27904     },
27905     
27906     
27907     _getIFrameHtml : function()
27908     {
27909         var sFile = 'fckeditor.html' ;
27910         /* no idea what this is about..
27911         try
27912         {
27913             if ( (/fcksource=true/i).test( window.top.location.search ) )
27914                 sFile = 'fckeditor.original.html' ;
27915         }
27916         catch (e) { 
27917         */
27918
27919         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27920         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27921         
27922         
27923         var html = '<iframe id="' + this.getId() +
27924             '___Frame" src="' + sLink +
27925             '" width="' + this.width +
27926             '" height="' + this.height + '"' +
27927             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27928             ' frameborder="0" scrolling="no"></iframe>' ;
27929
27930         return html ;
27931     },
27932     
27933     _insertHtmlBefore : function( html, element )
27934     {
27935         if ( element.insertAdjacentHTML )       {
27936             // IE
27937             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27938         } else { // Gecko
27939             var oRange = document.createRange() ;
27940             oRange.setStartBefore( element ) ;
27941             var oFragment = oRange.createContextualFragment( html );
27942             element.parentNode.insertBefore( oFragment, element ) ;
27943         }
27944     }
27945     
27946     
27947   
27948     
27949     
27950     
27951     
27952
27953 });
27954
27955 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27956
27957 function FCKeditor_OnComplete(editorInstance){
27958     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27959     f.fckEditor = editorInstance;
27960     //console.log("loaded");
27961     f.fireEvent('editorinit', f, editorInstance);
27962
27963   
27964
27965  
27966
27967
27968
27969
27970
27971
27972
27973
27974
27975
27976
27977
27978
27979
27980
27981 //<script type="text/javascript">
27982 /**
27983  * @class Roo.form.GridField
27984  * @extends Roo.form.Field
27985  * Embed a grid (or editable grid into a form)
27986  * STATUS ALPHA
27987  * 
27988  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27989  * it needs 
27990  * xgrid.store = Roo.data.Store
27991  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27992  * xgrid.store.reader = Roo.data.JsonReader 
27993  * 
27994  * 
27995  * @constructor
27996  * Creates a new GridField
27997  * @param {Object} config Configuration options
27998  */
27999 Roo.form.GridField = function(config){
28000     Roo.form.GridField.superclass.constructor.call(this, config);
28001      
28002 };
28003
28004 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
28005     /**
28006      * @cfg {Number} width  - used to restrict width of grid..
28007      */
28008     width : 100,
28009     /**
28010      * @cfg {Number} height - used to restrict height of grid..
28011      */
28012     height : 50,
28013      /**
28014      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
28015          * 
28016          *}
28017      */
28018     xgrid : false, 
28019     /**
28020      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28021      * {tag: "input", type: "checkbox", autocomplete: "off"})
28022      */
28023    // defaultAutoCreate : { tag: 'div' },
28024     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28025     /**
28026      * @cfg {String} addTitle Text to include for adding a title.
28027      */
28028     addTitle : false,
28029     //
28030     onResize : function(){
28031         Roo.form.Field.superclass.onResize.apply(this, arguments);
28032     },
28033
28034     initEvents : function(){
28035         // Roo.form.Checkbox.superclass.initEvents.call(this);
28036         // has no events...
28037        
28038     },
28039
28040
28041     getResizeEl : function(){
28042         return this.wrap;
28043     },
28044
28045     getPositionEl : function(){
28046         return this.wrap;
28047     },
28048
28049     // private
28050     onRender : function(ct, position){
28051         
28052         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
28053         var style = this.style;
28054         delete this.style;
28055         
28056         Roo.form.GridField.superclass.onRender.call(this, ct, position);
28057         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
28058         this.viewEl = this.wrap.createChild({ tag: 'div' });
28059         if (style) {
28060             this.viewEl.applyStyles(style);
28061         }
28062         if (this.width) {
28063             this.viewEl.setWidth(this.width);
28064         }
28065         if (this.height) {
28066             this.viewEl.setHeight(this.height);
28067         }
28068         //if(this.inputValue !== undefined){
28069         //this.setValue(this.value);
28070         
28071         
28072         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
28073         
28074         
28075         this.grid.render();
28076         this.grid.getDataSource().on('remove', this.refreshValue, this);
28077         this.grid.getDataSource().on('update', this.refreshValue, this);
28078         this.grid.on('afteredit', this.refreshValue, this);
28079  
28080     },
28081      
28082     
28083     /**
28084      * Sets the value of the item. 
28085      * @param {String} either an object  or a string..
28086      */
28087     setValue : function(v){
28088         //this.value = v;
28089         v = v || []; // empty set..
28090         // this does not seem smart - it really only affects memoryproxy grids..
28091         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
28092             var ds = this.grid.getDataSource();
28093             // assumes a json reader..
28094             var data = {}
28095             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
28096             ds.loadData( data);
28097         }
28098         Roo.form.GridField.superclass.setValue.call(this, v);
28099         this.refreshValue();
28100         // should load data in the grid really....
28101     },
28102     
28103     // private
28104     refreshValue: function() {
28105          var val = [];
28106         this.grid.getDataSource().each(function(r) {
28107             val.push(r.data);
28108         });
28109         this.el.dom.value = Roo.encode(val);
28110     }
28111     
28112      
28113     
28114     
28115 });/*
28116  * Based on:
28117  * Ext JS Library 1.1.1
28118  * Copyright(c) 2006-2007, Ext JS, LLC.
28119  *
28120  * Originally Released Under LGPL - original licence link has changed is not relivant.
28121  *
28122  * Fork - LGPL
28123  * <script type="text/javascript">
28124  */
28125 /**
28126  * @class Roo.form.DisplayField
28127  * @extends Roo.form.Field
28128  * A generic Field to display non-editable data.
28129  * @constructor
28130  * Creates a new Display Field item.
28131  * @param {Object} config Configuration options
28132  */
28133 Roo.form.DisplayField = function(config){
28134     Roo.form.DisplayField.superclass.constructor.call(this, config);
28135     
28136 };
28137
28138 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
28139     inputType:      'hidden',
28140     allowBlank:     true,
28141     readOnly:         true,
28142     
28143  
28144     /**
28145      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
28146      */
28147     focusClass : undefined,
28148     /**
28149      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
28150      */
28151     fieldClass: 'x-form-field',
28152     
28153      /**
28154      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
28155      */
28156     valueRenderer: undefined,
28157     
28158     width: 100,
28159     /**
28160      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
28161      * {tag: "input", type: "checkbox", autocomplete: "off"})
28162      */
28163      
28164  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
28165
28166     onResize : function(){
28167         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
28168         
28169     },
28170
28171     initEvents : function(){
28172         // Roo.form.Checkbox.superclass.initEvents.call(this);
28173         // has no events...
28174        
28175     },
28176
28177
28178     getResizeEl : function(){
28179         return this.wrap;
28180     },
28181
28182     getPositionEl : function(){
28183         return this.wrap;
28184     },
28185
28186     // private
28187     onRender : function(ct, position){
28188         
28189         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
28190         //if(this.inputValue !== undefined){
28191         this.wrap = this.el.wrap();
28192         
28193         this.viewEl = this.wrap.createChild({ tag: 'div'});
28194         
28195         if (this.bodyStyle) {
28196             this.viewEl.applyStyles(this.bodyStyle);
28197         }
28198         //this.viewEl.setStyle('padding', '2px');
28199         
28200         this.setValue(this.value);
28201         
28202     },
28203 /*
28204     // private
28205     initValue : Roo.emptyFn,
28206
28207   */
28208
28209         // private
28210     onClick : function(){
28211         
28212     },
28213
28214     /**
28215      * Sets the checked state of the checkbox.
28216      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
28217      */
28218     setValue : function(v){
28219         this.value = v;
28220         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
28221         // this might be called before we have a dom element..
28222         if (!this.viewEl) {
28223             return;
28224         }
28225         this.viewEl.dom.innerHTML = html;
28226         Roo.form.DisplayField.superclass.setValue.call(this, v);
28227
28228     }
28229 });//<script type="text/javasscript">
28230  
28231
28232 /**
28233  * @class Roo.DDView
28234  * A DnD enabled version of Roo.View.
28235  * @param {Element/String} container The Element in which to create the View.
28236  * @param {String} tpl The template string used to create the markup for each element of the View
28237  * @param {Object} config The configuration properties. These include all the config options of
28238  * {@link Roo.View} plus some specific to this class.<br>
28239  * <p>
28240  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28241  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28242  * <p>
28243  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28244 .x-view-drag-insert-above {
28245         border-top:1px dotted #3366cc;
28246 }
28247 .x-view-drag-insert-below {
28248         border-bottom:1px dotted #3366cc;
28249 }
28250 </code></pre>
28251  * 
28252  */
28253  
28254 Roo.DDView = function(container, tpl, config) {
28255     Roo.DDView.superclass.constructor.apply(this, arguments);
28256     this.getEl().setStyle("outline", "0px none");
28257     this.getEl().unselectable();
28258     if (this.dragGroup) {
28259                 this.setDraggable(this.dragGroup.split(","));
28260     }
28261     if (this.dropGroup) {
28262                 this.setDroppable(this.dropGroup.split(","));
28263     }
28264     if (this.deletable) {
28265         this.setDeletable();
28266     }
28267     this.isDirtyFlag = false;
28268         this.addEvents({
28269                 "drop" : true
28270         });
28271 };
28272
28273 Roo.extend(Roo.DDView, Roo.View, {
28274 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28275 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28276 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28277 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28278
28279         isFormField: true,
28280
28281         reset: Roo.emptyFn,
28282         
28283         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28284
28285         validate: function() {
28286                 return true;
28287         },
28288         
28289         destroy: function() {
28290                 this.purgeListeners();
28291                 this.getEl.removeAllListeners();
28292                 this.getEl().remove();
28293                 if (this.dragZone) {
28294                         if (this.dragZone.destroy) {
28295                                 this.dragZone.destroy();
28296                         }
28297                 }
28298                 if (this.dropZone) {
28299                         if (this.dropZone.destroy) {
28300                                 this.dropZone.destroy();
28301                         }
28302                 }
28303         },
28304
28305 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28306         getName: function() {
28307                 return this.name;
28308         },
28309
28310 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28311         setValue: function(v) {
28312                 if (!this.store) {
28313                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28314                 }
28315                 var data = {};
28316                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28317                 this.store.proxy = new Roo.data.MemoryProxy(data);
28318                 this.store.load();
28319         },
28320
28321 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28322         getValue: function() {
28323                 var result = '(';
28324                 this.store.each(function(rec) {
28325                         result += rec.id + ',';
28326                 });
28327                 return result.substr(0, result.length - 1) + ')';
28328         },
28329         
28330         getIds: function() {
28331                 var i = 0, result = new Array(this.store.getCount());
28332                 this.store.each(function(rec) {
28333                         result[i++] = rec.id;
28334                 });
28335                 return result;
28336         },
28337         
28338         isDirty: function() {
28339                 return this.isDirtyFlag;
28340         },
28341
28342 /**
28343  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28344  *      whole Element becomes the target, and this causes the drop gesture to append.
28345  */
28346     getTargetFromEvent : function(e) {
28347                 var target = e.getTarget();
28348                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28349                 target = target.parentNode;
28350                 }
28351                 if (!target) {
28352                         target = this.el.dom.lastChild || this.el.dom;
28353                 }
28354                 return target;
28355     },
28356
28357 /**
28358  *      Create the drag data which consists of an object which has the property "ddel" as
28359  *      the drag proxy element. 
28360  */
28361     getDragData : function(e) {
28362         var target = this.findItemFromChild(e.getTarget());
28363                 if(target) {
28364                         this.handleSelection(e);
28365                         var selNodes = this.getSelectedNodes();
28366             var dragData = {
28367                 source: this,
28368                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28369                 nodes: selNodes,
28370                 records: []
28371                         };
28372                         var selectedIndices = this.getSelectedIndexes();
28373                         for (var i = 0; i < selectedIndices.length; i++) {
28374                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28375                         }
28376                         if (selNodes.length == 1) {
28377                                 dragData.ddel = target.cloneNode(true); // the div element
28378                         } else {
28379                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28380                                 div.className = 'multi-proxy';
28381                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28382                                         div.appendChild(selNodes[i].cloneNode(true));
28383                                 }
28384                                 dragData.ddel = div;
28385                         }
28386             //console.log(dragData)
28387             //console.log(dragData.ddel.innerHTML)
28388                         return dragData;
28389                 }
28390         //console.log('nodragData')
28391                 return false;
28392     },
28393     
28394 /**     Specify to which ddGroup items in this DDView may be dragged. */
28395     setDraggable: function(ddGroup) {
28396         if (ddGroup instanceof Array) {
28397                 Roo.each(ddGroup, this.setDraggable, this);
28398                 return;
28399         }
28400         if (this.dragZone) {
28401                 this.dragZone.addToGroup(ddGroup);
28402         } else {
28403                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28404                                 containerScroll: true,
28405                                 ddGroup: ddGroup 
28406
28407                         });
28408 //                      Draggability implies selection. DragZone's mousedown selects the element.
28409                         if (!this.multiSelect) { this.singleSelect = true; }
28410
28411 //                      Wire the DragZone's handlers up to methods in *this*
28412                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28413                 }
28414     },
28415
28416 /**     Specify from which ddGroup this DDView accepts drops. */
28417     setDroppable: function(ddGroup) {
28418         if (ddGroup instanceof Array) {
28419                 Roo.each(ddGroup, this.setDroppable, this);
28420                 return;
28421         }
28422         if (this.dropZone) {
28423                 this.dropZone.addToGroup(ddGroup);
28424         } else {
28425                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28426                                 containerScroll: true,
28427                                 ddGroup: ddGroup
28428                         });
28429
28430 //                      Wire the DropZone's handlers up to methods in *this*
28431                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28432                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28433                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28434                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28435                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28436                 }
28437     },
28438
28439 /**     Decide whether to drop above or below a View node. */
28440     getDropPoint : function(e, n, dd){
28441         if (n == this.el.dom) { return "above"; }
28442                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28443                 var c = t + (b - t) / 2;
28444                 var y = Roo.lib.Event.getPageY(e);
28445                 if(y <= c) {
28446                         return "above";
28447                 }else{
28448                         return "below";
28449                 }
28450     },
28451
28452     onNodeEnter : function(n, dd, e, data){
28453                 return false;
28454     },
28455     
28456     onNodeOver : function(n, dd, e, data){
28457                 var pt = this.getDropPoint(e, n, dd);
28458                 // set the insert point style on the target node
28459                 var dragElClass = this.dropNotAllowed;
28460                 if (pt) {
28461                         var targetElClass;
28462                         if (pt == "above"){
28463                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28464                                 targetElClass = "x-view-drag-insert-above";
28465                         } else {
28466                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28467                                 targetElClass = "x-view-drag-insert-below";
28468                         }
28469                         if (this.lastInsertClass != targetElClass){
28470                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28471                                 this.lastInsertClass = targetElClass;
28472                         }
28473                 }
28474                 return dragElClass;
28475         },
28476
28477     onNodeOut : function(n, dd, e, data){
28478                 this.removeDropIndicators(n);
28479     },
28480
28481     onNodeDrop : function(n, dd, e, data){
28482         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28483                 return false;
28484         }
28485         var pt = this.getDropPoint(e, n, dd);
28486                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28487                 if (pt == "below") { insertAt++; }
28488                 for (var i = 0; i < data.records.length; i++) {
28489                         var r = data.records[i];
28490                         var dup = this.store.getById(r.id);
28491                         if (dup && (dd != this.dragZone)) {
28492                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28493                         } else {
28494                                 if (data.copy) {
28495                                         this.store.insert(insertAt++, r.copy());
28496                                 } else {
28497                                         data.source.isDirtyFlag = true;
28498                                         r.store.remove(r);
28499                                         this.store.insert(insertAt++, r);
28500                                 }
28501                                 this.isDirtyFlag = true;
28502                         }
28503                 }
28504                 this.dragZone.cachedTarget = null;
28505                 return true;
28506     },
28507
28508     removeDropIndicators : function(n){
28509                 if(n){
28510                         Roo.fly(n).removeClass([
28511                                 "x-view-drag-insert-above",
28512                                 "x-view-drag-insert-below"]);
28513                         this.lastInsertClass = "_noclass";
28514                 }
28515     },
28516
28517 /**
28518  *      Utility method. Add a delete option to the DDView's context menu.
28519  *      @param {String} imageUrl The URL of the "delete" icon image.
28520  */
28521         setDeletable: function(imageUrl) {
28522                 if (!this.singleSelect && !this.multiSelect) {
28523                         this.singleSelect = true;
28524                 }
28525                 var c = this.getContextMenu();
28526                 this.contextMenu.on("itemclick", function(item) {
28527                         switch (item.id) {
28528                                 case "delete":
28529                                         this.remove(this.getSelectedIndexes());
28530                                         break;
28531                         }
28532                 }, this);
28533                 this.contextMenu.add({
28534                         icon: imageUrl,
28535                         id: "delete",
28536                         text: 'Delete'
28537                 });
28538         },
28539         
28540 /**     Return the context menu for this DDView. */
28541         getContextMenu: function() {
28542                 if (!this.contextMenu) {
28543 //                      Create the View's context menu
28544                         this.contextMenu = new Roo.menu.Menu({
28545                                 id: this.id + "-contextmenu"
28546                         });
28547                         this.el.on("contextmenu", this.showContextMenu, this);
28548                 }
28549                 return this.contextMenu;
28550         },
28551         
28552         disableContextMenu: function() {
28553                 if (this.contextMenu) {
28554                         this.el.un("contextmenu", this.showContextMenu, this);
28555                 }
28556         },
28557
28558         showContextMenu: function(e, item) {
28559         item = this.findItemFromChild(e.getTarget());
28560                 if (item) {
28561                         e.stopEvent();
28562                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28563                         this.contextMenu.showAt(e.getXY());
28564             }
28565     },
28566
28567 /**
28568  *      Remove {@link Roo.data.Record}s at the specified indices.
28569  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28570  */
28571     remove: function(selectedIndices) {
28572                 selectedIndices = [].concat(selectedIndices);
28573                 for (var i = 0; i < selectedIndices.length; i++) {
28574                         var rec = this.store.getAt(selectedIndices[i]);
28575                         this.store.remove(rec);
28576                 }
28577     },
28578
28579 /**
28580  *      Double click fires the event, but also, if this is draggable, and there is only one other
28581  *      related DropZone, it transfers the selected node.
28582  */
28583     onDblClick : function(e){
28584         var item = this.findItemFromChild(e.getTarget());
28585         if(item){
28586             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28587                 return false;
28588             }
28589             if (this.dragGroup) {
28590                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28591                     while (targets.indexOf(this.dropZone) > -1) {
28592                             targets.remove(this.dropZone);
28593                                 }
28594                     if (targets.length == 1) {
28595                                         this.dragZone.cachedTarget = null;
28596                         var el = Roo.get(targets[0].getEl());
28597                         var box = el.getBox(true);
28598                         targets[0].onNodeDrop(el.dom, {
28599                                 target: el.dom,
28600                                 xy: [box.x, box.y + box.height - 1]
28601                         }, null, this.getDragData(e));
28602                     }
28603                 }
28604         }
28605     },
28606     
28607     handleSelection: function(e) {
28608                 this.dragZone.cachedTarget = null;
28609         var item = this.findItemFromChild(e.getTarget());
28610         if (!item) {
28611                 this.clearSelections(true);
28612                 return;
28613         }
28614                 if (item && (this.multiSelect || this.singleSelect)){
28615                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28616                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28617                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28618                                 this.unselect(item);
28619                         } else {
28620                                 this.select(item, this.multiSelect && e.ctrlKey);
28621                                 this.lastSelection = item;
28622                         }
28623                 }
28624     },
28625
28626     onItemClick : function(item, index, e){
28627                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28628                         return false;
28629                 }
28630                 return true;
28631     },
28632
28633     unselect : function(nodeInfo, suppressEvent){
28634                 var node = this.getNode(nodeInfo);
28635                 if(node && this.isSelected(node)){
28636                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28637                                 Roo.fly(node).removeClass(this.selectedClass);
28638                                 this.selections.remove(node);
28639                                 if(!suppressEvent){
28640                                         this.fireEvent("selectionchange", this, this.selections);
28641                                 }
28642                         }
28643                 }
28644     }
28645 });
28646 /*
28647  * Based on:
28648  * Ext JS Library 1.1.1
28649  * Copyright(c) 2006-2007, Ext JS, LLC.
28650  *
28651  * Originally Released Under LGPL - original licence link has changed is not relivant.
28652  *
28653  * Fork - LGPL
28654  * <script type="text/javascript">
28655  */
28656  
28657 /**
28658  * @class Roo.LayoutManager
28659  * @extends Roo.util.Observable
28660  * Base class for layout managers.
28661  */
28662 Roo.LayoutManager = function(container, config){
28663     Roo.LayoutManager.superclass.constructor.call(this);
28664     this.el = Roo.get(container);
28665     // ie scrollbar fix
28666     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28667         document.body.scroll = "no";
28668     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28669         this.el.position('relative');
28670     }
28671     this.id = this.el.id;
28672     this.el.addClass("x-layout-container");
28673     /** false to disable window resize monitoring @type Boolean */
28674     this.monitorWindowResize = true;
28675     this.regions = {};
28676     this.addEvents({
28677         /**
28678          * @event layout
28679          * Fires when a layout is performed. 
28680          * @param {Roo.LayoutManager} this
28681          */
28682         "layout" : true,
28683         /**
28684          * @event regionresized
28685          * Fires when the user resizes a region. 
28686          * @param {Roo.LayoutRegion} region The resized region
28687          * @param {Number} newSize The new size (width for east/west, height for north/south)
28688          */
28689         "regionresized" : true,
28690         /**
28691          * @event regioncollapsed
28692          * Fires when a region is collapsed. 
28693          * @param {Roo.LayoutRegion} region The collapsed region
28694          */
28695         "regioncollapsed" : true,
28696         /**
28697          * @event regionexpanded
28698          * Fires when a region is expanded.  
28699          * @param {Roo.LayoutRegion} region The expanded region
28700          */
28701         "regionexpanded" : true
28702     });
28703     this.updating = false;
28704     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28705 };
28706
28707 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28708     /**
28709      * Returns true if this layout is currently being updated
28710      * @return {Boolean}
28711      */
28712     isUpdating : function(){
28713         return this.updating; 
28714     },
28715     
28716     /**
28717      * Suspend the LayoutManager from doing auto-layouts while
28718      * making multiple add or remove calls
28719      */
28720     beginUpdate : function(){
28721         this.updating = true;    
28722     },
28723     
28724     /**
28725      * Restore auto-layouts and optionally disable the manager from performing a layout
28726      * @param {Boolean} noLayout true to disable a layout update 
28727      */
28728     endUpdate : function(noLayout){
28729         this.updating = false;
28730         if(!noLayout){
28731             this.layout();
28732         }    
28733     },
28734     
28735     layout: function(){
28736         
28737     },
28738     
28739     onRegionResized : function(region, newSize){
28740         this.fireEvent("regionresized", region, newSize);
28741         this.layout();
28742     },
28743     
28744     onRegionCollapsed : function(region){
28745         this.fireEvent("regioncollapsed", region);
28746     },
28747     
28748     onRegionExpanded : function(region){
28749         this.fireEvent("regionexpanded", region);
28750     },
28751         
28752     /**
28753      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28754      * performs box-model adjustments.
28755      * @return {Object} The size as an object {width: (the width), height: (the height)}
28756      */
28757     getViewSize : function(){
28758         var size;
28759         if(this.el.dom != document.body){
28760             size = this.el.getSize();
28761         }else{
28762             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28763         }
28764         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28765         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28766         return size;
28767     },
28768     
28769     /**
28770      * Returns the Element this layout is bound to.
28771      * @return {Roo.Element}
28772      */
28773     getEl : function(){
28774         return this.el;
28775     },
28776     
28777     /**
28778      * Returns the specified region.
28779      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28780      * @return {Roo.LayoutRegion}
28781      */
28782     getRegion : function(target){
28783         return this.regions[target.toLowerCase()];
28784     },
28785     
28786     onWindowResize : function(){
28787         if(this.monitorWindowResize){
28788             this.layout();
28789         }
28790     }
28791 });/*
28792  * Based on:
28793  * Ext JS Library 1.1.1
28794  * Copyright(c) 2006-2007, Ext JS, LLC.
28795  *
28796  * Originally Released Under LGPL - original licence link has changed is not relivant.
28797  *
28798  * Fork - LGPL
28799  * <script type="text/javascript">
28800  */
28801 /**
28802  * @class Roo.BorderLayout
28803  * @extends Roo.LayoutManager
28804  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28805  * please see: <br><br>
28806  * <a href="http://www.jackslocum.com/yui/2006/10/19/cross-browser-web-20-layouts-with-yahoo-ui/">Cross Browser Layouts - Part 1</a><br>
28807  * <a href="http://www.jackslocum.com/yui/2006/10/28/cross-browser-web-20-layouts-part-2-ajax-feed-viewer-20/">Cross Browser Layouts - Part 2</a><br><br>
28808  * Example:
28809  <pre><code>
28810  var layout = new Roo.BorderLayout(document.body, {
28811     north: {
28812         initialSize: 25,
28813         titlebar: false
28814     },
28815     west: {
28816         split:true,
28817         initialSize: 200,
28818         minSize: 175,
28819         maxSize: 400,
28820         titlebar: true,
28821         collapsible: true
28822     },
28823     east: {
28824         split:true,
28825         initialSize: 202,
28826         minSize: 175,
28827         maxSize: 400,
28828         titlebar: true,
28829         collapsible: true
28830     },
28831     south: {
28832         split:true,
28833         initialSize: 100,
28834         minSize: 100,
28835         maxSize: 200,
28836         titlebar: true,
28837         collapsible: true
28838     },
28839     center: {
28840         titlebar: true,
28841         autoScroll:true,
28842         resizeTabs: true,
28843         minTabWidth: 50,
28844         preferredTabWidth: 150
28845     }
28846 });
28847
28848 // shorthand
28849 var CP = Roo.ContentPanel;
28850
28851 layout.beginUpdate();
28852 layout.add("north", new CP("north", "North"));
28853 layout.add("south", new CP("south", {title: "South", closable: true}));
28854 layout.add("west", new CP("west", {title: "West"}));
28855 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
28856 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
28857 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
28858 layout.getRegion("center").showPanel("center1");
28859 layout.endUpdate();
28860 </code></pre>
28861
28862 <b>The container the layout is rendered into can be either the body element or any other element.
28863 If it is not the body element, the container needs to either be an absolute positioned element,
28864 or you will need to add "position:relative" to the css of the container.  You will also need to specify
28865 the container size if it is not the body element.</b>
28866
28867 * @constructor
28868 * Create a new BorderLayout
28869 * @param {String/HTMLElement/Element} container The container this layout is bound to
28870 * @param {Object} config Configuration options
28871  */
28872 Roo.BorderLayout = function(container, config){
28873     config = config || {};
28874     Roo.BorderLayout.superclass.constructor.call(this, container, config);
28875     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
28876     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
28877         var target = this.factory.validRegions[i];
28878         if(config[target]){
28879             this.addRegion(target, config[target]);
28880         }
28881     }
28882 };
28883
28884 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
28885     /**
28886      * Creates and adds a new region if it doesn't already exist.
28887      * @param {String} target The target region key (north, south, east, west or center).
28888      * @param {Object} config The regions config object
28889      * @return {BorderLayoutRegion} The new region
28890      */
28891     addRegion : function(target, config){
28892         if(!this.regions[target]){
28893             var r = this.factory.create(target, this, config);
28894             this.bindRegion(target, r);
28895         }
28896         return this.regions[target];
28897     },
28898
28899     // private (kinda)
28900     bindRegion : function(name, r){
28901         this.regions[name] = r;
28902         r.on("visibilitychange", this.layout, this);
28903         r.on("paneladded", this.layout, this);
28904         r.on("panelremoved", this.layout, this);
28905         r.on("invalidated", this.layout, this);
28906         r.on("resized", this.onRegionResized, this);
28907         r.on("collapsed", this.onRegionCollapsed, this);
28908         r.on("expanded", this.onRegionExpanded, this);
28909     },
28910
28911     /**
28912      * Performs a layout update.
28913      */
28914     layout : function(){
28915         if(this.updating) return;
28916         var size = this.getViewSize();
28917         var w = size.width;
28918         var h = size.height;
28919         var centerW = w;
28920         var centerH = h;
28921         var centerY = 0;
28922         var centerX = 0;
28923         //var x = 0, y = 0;
28924
28925         var rs = this.regions;
28926         var north = rs["north"];
28927         var south = rs["south"]; 
28928         var west = rs["west"];
28929         var east = rs["east"];
28930         var center = rs["center"];
28931         //if(this.hideOnLayout){ // not supported anymore
28932             //c.el.setStyle("display", "none");
28933         //}
28934         if(north && north.isVisible()){
28935             var b = north.getBox();
28936             var m = north.getMargins();
28937             b.width = w - (m.left+m.right);
28938             b.x = m.left;
28939             b.y = m.top;
28940             centerY = b.height + b.y + m.bottom;
28941             centerH -= centerY;
28942             north.updateBox(this.safeBox(b));
28943         }
28944         if(south && south.isVisible()){
28945             var b = south.getBox();
28946             var m = south.getMargins();
28947             b.width = w - (m.left+m.right);
28948             b.x = m.left;
28949             var totalHeight = (b.height + m.top + m.bottom);
28950             b.y = h - totalHeight + m.top;
28951             centerH -= totalHeight;
28952             south.updateBox(this.safeBox(b));
28953         }
28954         if(west && west.isVisible()){
28955             var b = west.getBox();
28956             var m = west.getMargins();
28957             b.height = centerH - (m.top+m.bottom);
28958             b.x = m.left;
28959             b.y = centerY + m.top;
28960             var totalWidth = (b.width + m.left + m.right);
28961             centerX += totalWidth;
28962             centerW -= totalWidth;
28963             west.updateBox(this.safeBox(b));
28964         }
28965         if(east && east.isVisible()){
28966             var b = east.getBox();
28967             var m = east.getMargins();
28968             b.height = centerH - (m.top+m.bottom);
28969             var totalWidth = (b.width + m.left + m.right);
28970             b.x = w - totalWidth + m.left;
28971             b.y = centerY + m.top;
28972             centerW -= totalWidth;
28973             east.updateBox(this.safeBox(b));
28974         }
28975         if(center){
28976             var m = center.getMargins();
28977             var centerBox = {
28978                 x: centerX + m.left,
28979                 y: centerY + m.top,
28980                 width: centerW - (m.left+m.right),
28981                 height: centerH - (m.top+m.bottom)
28982             };
28983             //if(this.hideOnLayout){
28984                 //center.el.setStyle("display", "block");
28985             //}
28986             center.updateBox(this.safeBox(centerBox));
28987         }
28988         this.el.repaint();
28989         this.fireEvent("layout", this);
28990     },
28991
28992     // private
28993     safeBox : function(box){
28994         box.width = Math.max(0, box.width);
28995         box.height = Math.max(0, box.height);
28996         return box;
28997     },
28998
28999     /**
29000      * Adds a ContentPanel (or subclass) to this layout.
29001      * @param {String} target The target region key (north, south, east, west or center).
29002      * @param {Roo.ContentPanel} panel The panel to add
29003      * @return {Roo.ContentPanel} The added panel
29004      */
29005     add : function(target, panel){
29006          
29007         target = target.toLowerCase();
29008         return this.regions[target].add(panel);
29009     },
29010
29011     /**
29012      * Remove a ContentPanel (or subclass) to this layout.
29013      * @param {String} target The target region key (north, south, east, west or center).
29014      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29015      * @return {Roo.ContentPanel} The removed panel
29016      */
29017     remove : function(target, panel){
29018         target = target.toLowerCase();
29019         return this.regions[target].remove(panel);
29020     },
29021
29022     /**
29023      * Searches all regions for a panel with the specified id
29024      * @param {String} panelId
29025      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29026      */
29027     findPanel : function(panelId){
29028         var rs = this.regions;
29029         for(var target in rs){
29030             if(typeof rs[target] != "function"){
29031                 var p = rs[target].getPanel(panelId);
29032                 if(p){
29033                     return p;
29034                 }
29035             }
29036         }
29037         return null;
29038     },
29039
29040     /**
29041      * Searches all regions for a panel with the specified id and activates (shows) it.
29042      * @param {String/ContentPanel} panelId The panels id or the panel itself
29043      * @return {Roo.ContentPanel} The shown panel or null
29044      */
29045     showPanel : function(panelId) {
29046       var rs = this.regions;
29047       for(var target in rs){
29048          var r = rs[target];
29049          if(typeof r != "function"){
29050             if(r.hasPanel(panelId)){
29051                return r.showPanel(panelId);
29052             }
29053          }
29054       }
29055       return null;
29056    },
29057
29058    /**
29059      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29060      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29061      */
29062     restoreState : function(provider){
29063         if(!provider){
29064             provider = Roo.state.Manager;
29065         }
29066         var sm = new Roo.LayoutStateManager();
29067         sm.init(this, provider);
29068     },
29069
29070     /**
29071      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29072      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29073      * a valid ContentPanel config object.  Example:
29074      * <pre><code>
29075 // Create the main layout
29076 var layout = new Roo.BorderLayout('main-ct', {
29077     west: {
29078         split:true,
29079         minSize: 175,
29080         titlebar: true
29081     },
29082     center: {
29083         title:'Components'
29084     }
29085 }, 'main-ct');
29086
29087 // Create and add multiple ContentPanels at once via configs
29088 layout.batchAdd({
29089    west: {
29090        id: 'source-files',
29091        autoCreate:true,
29092        title:'Ext Source Files',
29093        autoScroll:true,
29094        fitToFrame:true
29095    },
29096    center : {
29097        el: cview,
29098        autoScroll:true,
29099        fitToFrame:true,
29100        toolbar: tb,
29101        resizeEl:'cbody'
29102    }
29103 });
29104 </code></pre>
29105      * @param {Object} regions An object containing ContentPanel configs by region name
29106      */
29107     batchAdd : function(regions){
29108         this.beginUpdate();
29109         for(var rname in regions){
29110             var lr = this.regions[rname];
29111             if(lr){
29112                 this.addTypedPanels(lr, regions[rname]);
29113             }
29114         }
29115         this.endUpdate();
29116     },
29117
29118     // private
29119     addTypedPanels : function(lr, ps){
29120         if(typeof ps == 'string'){
29121             lr.add(new Roo.ContentPanel(ps));
29122         }
29123         else if(ps instanceof Array){
29124             for(var i =0, len = ps.length; i < len; i++){
29125                 this.addTypedPanels(lr, ps[i]);
29126             }
29127         }
29128         else if(!ps.events){ // raw config?
29129             var el = ps.el;
29130             delete ps.el; // prevent conflict
29131             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29132         }
29133         else {  // panel object assumed!
29134             lr.add(ps);
29135         }
29136     },
29137     /**
29138      * Adds a xtype elements to the layout.
29139      * <pre><code>
29140
29141 layout.addxtype({
29142        xtype : 'ContentPanel',
29143        region: 'west',
29144        items: [ .... ]
29145    }
29146 );
29147
29148 layout.addxtype({
29149         xtype : 'NestedLayoutPanel',
29150         region: 'west',
29151         layout: {
29152            center: { },
29153            west: { }   
29154         },
29155         items : [ ... list of content panels or nested layout panels.. ]
29156    }
29157 );
29158 </code></pre>
29159      * @param {Object} cfg Xtype definition of item to add.
29160      */
29161     addxtype : function(cfg)
29162     {
29163         // basically accepts a pannel...
29164         // can accept a layout region..!?!?
29165        // console.log('BorderLayout add ' + cfg.xtype)
29166         
29167         if (!cfg.xtype.match(/Panel$/)) {
29168             return false;
29169         }
29170         var ret = false;
29171         var region = cfg.region;
29172         delete cfg.region;
29173         
29174           
29175         var xitems = [];
29176         if (cfg.items) {
29177             xitems = cfg.items;
29178             delete cfg.items;
29179         }
29180         
29181         
29182         switch(cfg.xtype) 
29183         {
29184             case 'ContentPanel':  // ContentPanel (el, cfg)
29185             case 'ScrollPanel':  // ContentPanel (el, cfg)
29186                 if(cfg.autoCreate) {
29187                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29188                 } else {
29189                     var el = this.el.createChild();
29190                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29191                 }
29192                 
29193                 this.add(region, ret);
29194                 break;
29195             
29196             
29197             case 'TreePanel': // our new panel!
29198                 cfg.el = this.el.createChild();
29199                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29200                 this.add(region, ret);
29201                 break;
29202             
29203             case 'NestedLayoutPanel': 
29204                 // create a new Layout (which is  a Border Layout...
29205                 var el = this.el.createChild();
29206                 var clayout = cfg.layout;
29207                 delete cfg.layout;
29208                 clayout.items   = clayout.items  || [];
29209                 // replace this exitems with the clayout ones..
29210                 xitems = clayout.items;
29211                  
29212                 
29213                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29214                     cfg.background = false;
29215                 }
29216                 var layout = new Roo.BorderLayout(el, clayout);
29217                 
29218                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29219                 //console.log('adding nested layout panel '  + cfg.toSource());
29220                 this.add(region, ret);
29221                 
29222                 break;
29223                 
29224             case 'GridPanel': 
29225             
29226                 // needs grid and region
29227                 
29228                 //var el = this.getRegion(region).el.createChild();
29229                 var el = this.el.createChild();
29230                 // create the grid first...
29231                 
29232                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29233                 delete cfg.grid;
29234                 if (region == 'center' && this.active ) {
29235                     cfg.background = false;
29236                 }
29237                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29238                 
29239                 this.add(region, ret);
29240                 if (cfg.background) {
29241                     ret.on('activate', function(gp) {
29242                         if (!gp.grid.rendered) {
29243                             gp.grid.render();
29244                         }
29245                     });
29246                 } else {
29247                     grid.render();
29248                 }
29249                 break;
29250            
29251                
29252                 
29253                 
29254             default: 
29255                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29256                 return;
29257              // GridPanel (grid, cfg)
29258             
29259         }
29260         this.beginUpdate();
29261         // add children..
29262         Roo.each(xitems, function(i)  {
29263             ret.addxtype(i);
29264         });
29265         this.endUpdate();
29266         return ret;
29267         
29268     }
29269 });
29270
29271 /**
29272  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29273  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29274  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29275  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29276  * <pre><code>
29277 // shorthand
29278 var CP = Roo.ContentPanel;
29279
29280 var layout = Roo.BorderLayout.create({
29281     north: {
29282         initialSize: 25,
29283         titlebar: false,
29284         panels: [new CP("north", "North")]
29285     },
29286     west: {
29287         split:true,
29288         initialSize: 200,
29289         minSize: 175,
29290         maxSize: 400,
29291         titlebar: true,
29292         collapsible: true,
29293         panels: [new CP("west", {title: "West"})]
29294     },
29295     east: {
29296         split:true,
29297         initialSize: 202,
29298         minSize: 175,
29299         maxSize: 400,
29300         titlebar: true,
29301         collapsible: true,
29302         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29303     },
29304     south: {
29305         split:true,
29306         initialSize: 100,
29307         minSize: 100,
29308         maxSize: 200,
29309         titlebar: true,
29310         collapsible: true,
29311         panels: [new CP("south", {title: "South", closable: true})]
29312     },
29313     center: {
29314         titlebar: true,
29315         autoScroll:true,
29316         resizeTabs: true,
29317         minTabWidth: 50,
29318         preferredTabWidth: 150,
29319         panels: [
29320             new CP("center1", {title: "Close Me", closable: true}),
29321             new CP("center2", {title: "Center Panel", closable: false})
29322         ]
29323     }
29324 }, document.body);
29325
29326 layout.getRegion("center").showPanel("center1");
29327 </code></pre>
29328  * @param config
29329  * @param targetEl
29330  */
29331 Roo.BorderLayout.create = function(config, targetEl){
29332     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29333     layout.beginUpdate();
29334     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29335     for(var j = 0, jlen = regions.length; j < jlen; j++){
29336         var lr = regions[j];
29337         if(layout.regions[lr] && config[lr].panels){
29338             var r = layout.regions[lr];
29339             var ps = config[lr].panels;
29340             layout.addTypedPanels(r, ps);
29341         }
29342     }
29343     layout.endUpdate();
29344     return layout;
29345 };
29346
29347 // private
29348 Roo.BorderLayout.RegionFactory = {
29349     // private
29350     validRegions : ["north","south","east","west","center"],
29351
29352     // private
29353     create : function(target, mgr, config){
29354         target = target.toLowerCase();
29355         if(config.lightweight || config.basic){
29356             return new Roo.BasicLayoutRegion(mgr, config, target);
29357         }
29358         switch(target){
29359             case "north":
29360                 return new Roo.NorthLayoutRegion(mgr, config);
29361             case "south":
29362                 return new Roo.SouthLayoutRegion(mgr, config);
29363             case "east":
29364                 return new Roo.EastLayoutRegion(mgr, config);
29365             case "west":
29366                 return new Roo.WestLayoutRegion(mgr, config);
29367             case "center":
29368                 return new Roo.CenterLayoutRegion(mgr, config);
29369         }
29370         throw 'Layout region "'+target+'" not supported.';
29371     }
29372 };/*
29373  * Based on:
29374  * Ext JS Library 1.1.1
29375  * Copyright(c) 2006-2007, Ext JS, LLC.
29376  *
29377  * Originally Released Under LGPL - original licence link has changed is not relivant.
29378  *
29379  * Fork - LGPL
29380  * <script type="text/javascript">
29381  */
29382  
29383 /**
29384  * @class Roo.BasicLayoutRegion
29385  * @extends Roo.util.Observable
29386  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29387  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29388  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29389  */
29390 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29391     this.mgr = mgr;
29392     this.position  = pos;
29393     this.events = {
29394         /**
29395          * @scope Roo.BasicLayoutRegion
29396          */
29397         
29398         /**
29399          * @event beforeremove
29400          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29401          * @param {Roo.LayoutRegion} this
29402          * @param {Roo.ContentPanel} panel The panel
29403          * @param {Object} e The cancel event object
29404          */
29405         "beforeremove" : true,
29406         /**
29407          * @event invalidated
29408          * Fires when the layout for this region is changed.
29409          * @param {Roo.LayoutRegion} this
29410          */
29411         "invalidated" : true,
29412         /**
29413          * @event visibilitychange
29414          * Fires when this region is shown or hidden 
29415          * @param {Roo.LayoutRegion} this
29416          * @param {Boolean} visibility true or false
29417          */
29418         "visibilitychange" : true,
29419         /**
29420          * @event paneladded
29421          * Fires when a panel is added. 
29422          * @param {Roo.LayoutRegion} this
29423          * @param {Roo.ContentPanel} panel The panel
29424          */
29425         "paneladded" : true,
29426         /**
29427          * @event panelremoved
29428          * Fires when a panel is removed. 
29429          * @param {Roo.LayoutRegion} this
29430          * @param {Roo.ContentPanel} panel The panel
29431          */
29432         "panelremoved" : true,
29433         /**
29434          * @event collapsed
29435          * Fires when this region is collapsed.
29436          * @param {Roo.LayoutRegion} this
29437          */
29438         "collapsed" : true,
29439         /**
29440          * @event expanded
29441          * Fires when this region is expanded.
29442          * @param {Roo.LayoutRegion} this
29443          */
29444         "expanded" : true,
29445         /**
29446          * @event slideshow
29447          * Fires when this region is slid into view.
29448          * @param {Roo.LayoutRegion} this
29449          */
29450         "slideshow" : true,
29451         /**
29452          * @event slidehide
29453          * Fires when this region slides out of view. 
29454          * @param {Roo.LayoutRegion} this
29455          */
29456         "slidehide" : true,
29457         /**
29458          * @event panelactivated
29459          * Fires when a panel is activated. 
29460          * @param {Roo.LayoutRegion} this
29461          * @param {Roo.ContentPanel} panel The activated panel
29462          */
29463         "panelactivated" : true,
29464         /**
29465          * @event resized
29466          * Fires when the user resizes this region. 
29467          * @param {Roo.LayoutRegion} this
29468          * @param {Number} newSize The new size (width for east/west, height for north/south)
29469          */
29470         "resized" : true
29471     };
29472     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29473     this.panels = new Roo.util.MixedCollection();
29474     this.panels.getKey = this.getPanelId.createDelegate(this);
29475     this.box = null;
29476     this.activePanel = null;
29477     // ensure listeners are added...
29478     
29479     if (config.listeners || config.events) {
29480         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29481             listeners : config.listeners || {},
29482             events : config.events || {}
29483         });
29484     }
29485     
29486     if(skipConfig !== true){
29487         this.applyConfig(config);
29488     }
29489 };
29490
29491 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29492     getPanelId : function(p){
29493         return p.getId();
29494     },
29495     
29496     applyConfig : function(config){
29497         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29498         this.config = config;
29499         
29500     },
29501     
29502     /**
29503      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29504      * the width, for horizontal (north, south) the height.
29505      * @param {Number} newSize The new width or height
29506      */
29507     resizeTo : function(newSize){
29508         var el = this.el ? this.el :
29509                  (this.activePanel ? this.activePanel.getEl() : null);
29510         if(el){
29511             switch(this.position){
29512                 case "east":
29513                 case "west":
29514                     el.setWidth(newSize);
29515                     this.fireEvent("resized", this, newSize);
29516                 break;
29517                 case "north":
29518                 case "south":
29519                     el.setHeight(newSize);
29520                     this.fireEvent("resized", this, newSize);
29521                 break;                
29522             }
29523         }
29524     },
29525     
29526     getBox : function(){
29527         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29528     },
29529     
29530     getMargins : function(){
29531         return this.margins;
29532     },
29533     
29534     updateBox : function(box){
29535         this.box = box;
29536         var el = this.activePanel.getEl();
29537         el.dom.style.left = box.x + "px";
29538         el.dom.style.top = box.y + "px";
29539         this.activePanel.setSize(box.width, box.height);
29540     },
29541     
29542     /**
29543      * Returns the container element for this region.
29544      * @return {Roo.Element}
29545      */
29546     getEl : function(){
29547         return this.activePanel;
29548     },
29549     
29550     /**
29551      * Returns true if this region is currently visible.
29552      * @return {Boolean}
29553      */
29554     isVisible : function(){
29555         return this.activePanel ? true : false;
29556     },
29557     
29558     setActivePanel : function(panel){
29559         panel = this.getPanel(panel);
29560         if(this.activePanel && this.activePanel != panel){
29561             this.activePanel.setActiveState(false);
29562             this.activePanel.getEl().setLeftTop(-10000,-10000);
29563         }
29564         this.activePanel = panel;
29565         panel.setActiveState(true);
29566         if(this.box){
29567             panel.setSize(this.box.width, this.box.height);
29568         }
29569         this.fireEvent("panelactivated", this, panel);
29570         this.fireEvent("invalidated");
29571     },
29572     
29573     /**
29574      * Show the specified panel.
29575      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29576      * @return {Roo.ContentPanel} The shown panel or null
29577      */
29578     showPanel : function(panel){
29579         if(panel = this.getPanel(panel)){
29580             this.setActivePanel(panel);
29581         }
29582         return panel;
29583     },
29584     
29585     /**
29586      * Get the active panel for this region.
29587      * @return {Roo.ContentPanel} The active panel or null
29588      */
29589     getActivePanel : function(){
29590         return this.activePanel;
29591     },
29592     
29593     /**
29594      * Add the passed ContentPanel(s)
29595      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29596      * @return {Roo.ContentPanel} The panel added (if only one was added)
29597      */
29598     add : function(panel){
29599         if(arguments.length > 1){
29600             for(var i = 0, len = arguments.length; i < len; i++) {
29601                 this.add(arguments[i]);
29602             }
29603             return null;
29604         }
29605         if(this.hasPanel(panel)){
29606             this.showPanel(panel);
29607             return panel;
29608         }
29609         var el = panel.getEl();
29610         if(el.dom.parentNode != this.mgr.el.dom){
29611             this.mgr.el.dom.appendChild(el.dom);
29612         }
29613         if(panel.setRegion){
29614             panel.setRegion(this);
29615         }
29616         this.panels.add(panel);
29617         el.setStyle("position", "absolute");
29618         if(!panel.background){
29619             this.setActivePanel(panel);
29620             if(this.config.initialSize && this.panels.getCount()==1){
29621                 this.resizeTo(this.config.initialSize);
29622             }
29623         }
29624         this.fireEvent("paneladded", this, panel);
29625         return panel;
29626     },
29627     
29628     /**
29629      * Returns true if the panel is in this region.
29630      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29631      * @return {Boolean}
29632      */
29633     hasPanel : function(panel){
29634         if(typeof panel == "object"){ // must be panel obj
29635             panel = panel.getId();
29636         }
29637         return this.getPanel(panel) ? true : false;
29638     },
29639     
29640     /**
29641      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29642      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29643      * @param {Boolean} preservePanel Overrides the config preservePanel option
29644      * @return {Roo.ContentPanel} The panel that was removed
29645      */
29646     remove : function(panel, preservePanel){
29647         panel = this.getPanel(panel);
29648         if(!panel){
29649             return null;
29650         }
29651         var e = {};
29652         this.fireEvent("beforeremove", this, panel, e);
29653         if(e.cancel === true){
29654             return null;
29655         }
29656         var panelId = panel.getId();
29657         this.panels.removeKey(panelId);
29658         return panel;
29659     },
29660     
29661     /**
29662      * Returns the panel specified or null if it's not in this region.
29663      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29664      * @return {Roo.ContentPanel}
29665      */
29666     getPanel : function(id){
29667         if(typeof id == "object"){ // must be panel obj
29668             return id;
29669         }
29670         return this.panels.get(id);
29671     },
29672     
29673     /**
29674      * Returns this regions position (north/south/east/west/center).
29675      * @return {String} 
29676      */
29677     getPosition: function(){
29678         return this.position;    
29679     }
29680 });