roojs-ui.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 (ok-add/ok-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     {
4020         this.valid = true;
4021         this.fireEvent('enter', dd, e, data);
4022         if(this.overClass){
4023             this.el.addClass(this.overClass);
4024         }
4025         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4026             this.valid ? this.dropAllowed : this.dropNotAllowed
4027         );
4028     },
4029
4030     /**
4031      * @hide
4032      */
4033     notifyOver : function(dd, e, data)
4034     {
4035         this.valid = true;
4036         this.fireEvent('over', dd, e, data);
4037         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4038             this.valid ? this.dropAllowed : this.dropNotAllowed
4039         );
4040     },
4041
4042     /**
4043      * @hide
4044      */
4045     notifyOut : function(dd, e, data)
4046     {
4047         this.fireEvent('out', dd, e, data);
4048         if(this.overClass){
4049             this.el.removeClass(this.overClass);
4050         }
4051     },
4052
4053     /**
4054      * @hide
4055      */
4056     notifyDrop : function(dd, e, data)
4057     {
4058         this.success = false;
4059         this.fireEvent('drop', dd, e, data);
4060         return this.success;
4061     }
4062 });/*
4063  * Based on:
4064  * Ext JS Library 1.1.1
4065  * Copyright(c) 2006-2007, Ext JS, LLC.
4066  *
4067  * Originally Released Under LGPL - original licence link has changed is not relivant.
4068  *
4069  * Fork - LGPL
4070  * <script type="text/javascript">
4071  */
4072
4073
4074 /**
4075  * @class Roo.dd.DragZone
4076  * @extends Roo.dd.DragSource
4077  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4078  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4079  * @constructor
4080  * @param {String/HTMLElement/Element} el The container element
4081  * @param {Object} config
4082  */
4083 Roo.dd.DragZone = function(el, config){
4084     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4085     if(this.containerScroll){
4086         Roo.dd.ScrollManager.register(this.el);
4087     }
4088 };
4089
4090 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4091     /**
4092      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4093      * for auto scrolling during drag operations.
4094      */
4095     /**
4096      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4097      * method after a failed drop (defaults to "c3daf9" - light blue)
4098      */
4099
4100     /**
4101      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4102      * for a valid target to drag based on the mouse down. Override this method
4103      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4104      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4105      * @param {EventObject} e The mouse down event
4106      * @return {Object} The dragData
4107      */
4108     getDragData : function(e){
4109         return Roo.dd.Registry.getHandleFromEvent(e);
4110     },
4111     
4112     /**
4113      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4114      * this.dragData.ddel
4115      * @param {Number} x The x position of the click on the dragged object
4116      * @param {Number} y The y position of the click on the dragged object
4117      * @return {Boolean} true to continue the drag, false to cancel
4118      */
4119     onInitDrag : function(x, y){
4120         this.proxy.update(this.dragData.ddel.cloneNode(true));
4121         this.onStartDrag(x, y);
4122         return true;
4123     },
4124     
4125     /**
4126      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4127      */
4128     afterRepair : function(){
4129         if(Roo.enableFx){
4130             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4131         }
4132         this.dragging = false;
4133     },
4134
4135     /**
4136      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4137      * the XY of this.dragData.ddel
4138      * @param {EventObject} e The mouse up event
4139      * @return {Array} The xy location (e.g. [100, 200])
4140      */
4141     getRepairXY : function(e){
4142         return Roo.Element.fly(this.dragData.ddel).getXY();  
4143     }
4144 });/*
4145  * Based on:
4146  * Ext JS Library 1.1.1
4147  * Copyright(c) 2006-2007, Ext JS, LLC.
4148  *
4149  * Originally Released Under LGPL - original licence link has changed is not relivant.
4150  *
4151  * Fork - LGPL
4152  * <script type="text/javascript">
4153  */
4154 /**
4155  * @class Roo.dd.DropZone
4156  * @extends Roo.dd.DropTarget
4157  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4158  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4159  * @constructor
4160  * @param {String/HTMLElement/Element} el The container element
4161  * @param {Object} config
4162  */
4163 Roo.dd.DropZone = function(el, config){
4164     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4165 };
4166
4167 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4168     /**
4169      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4170      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4171      * provide your own custom lookup.
4172      * @param {Event} e The event
4173      * @return {Object} data The custom data
4174      */
4175     getTargetFromEvent : function(e){
4176         return Roo.dd.Registry.getTargetFromEvent(e);
4177     },
4178
4179     /**
4180      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4181      * that it has registered.  This method has no default implementation and should be overridden to provide
4182      * node-specific processing if necessary.
4183      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4184      * {@link #getTargetFromEvent} for this node)
4185      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4186      * @param {Event} e The event
4187      * @param {Object} data An object containing arbitrary data supplied by the drag source
4188      */
4189     onNodeEnter : function(n, dd, e, data){
4190         
4191     },
4192
4193     /**
4194      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4195      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4196      * overridden to provide the proper feedback.
4197      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4198      * {@link #getTargetFromEvent} for this node)
4199      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4200      * @param {Event} e The event
4201      * @param {Object} data An object containing arbitrary data supplied by the drag source
4202      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4203      * underlying {@link Roo.dd.StatusProxy} can be updated
4204      */
4205     onNodeOver : function(n, dd, e, data){
4206         return this.dropAllowed;
4207     },
4208
4209     /**
4210      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4211      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4212      * node-specific processing if necessary.
4213      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4214      * {@link #getTargetFromEvent} for this node)
4215      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4216      * @param {Event} e The event
4217      * @param {Object} data An object containing arbitrary data supplied by the drag source
4218      */
4219     onNodeOut : function(n, dd, e, data){
4220         
4221     },
4222
4223     /**
4224      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4225      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4226      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4227      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4228      * {@link #getTargetFromEvent} for this node)
4229      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4230      * @param {Event} e The event
4231      * @param {Object} data An object containing arbitrary data supplied by the drag source
4232      * @return {Boolean} True if the drop was valid, else false
4233      */
4234     onNodeDrop : function(n, dd, e, data){
4235         return false;
4236     },
4237
4238     /**
4239      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4240      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4241      * it should be overridden to provide the proper feedback if necessary.
4242      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4243      * @param {Event} e The event
4244      * @param {Object} data An object containing arbitrary data supplied by the drag source
4245      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4246      * underlying {@link Roo.dd.StatusProxy} can be updated
4247      */
4248     onContainerOver : function(dd, e, data){
4249         return this.dropNotAllowed;
4250     },
4251
4252     /**
4253      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4254      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4255      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4256      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4257      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4258      * @param {Event} e The event
4259      * @param {Object} data An object containing arbitrary data supplied by the drag source
4260      * @return {Boolean} True if the drop was valid, else false
4261      */
4262     onContainerDrop : function(dd, e, data){
4263         return false;
4264     },
4265
4266     /**
4267      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4268      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4269      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4270      * you should override this method and provide a custom implementation.
4271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4272      * @param {Event} e The event
4273      * @param {Object} data An object containing arbitrary data supplied by the drag source
4274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4275      * underlying {@link Roo.dd.StatusProxy} can be updated
4276      */
4277     notifyEnter : function(dd, e, data){
4278         return this.dropNotAllowed;
4279     },
4280
4281     /**
4282      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4283      * This method will be called on every mouse movement while the drag source is over the drop zone.
4284      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4285      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4286      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4287      * registered node, it will call {@link #onContainerOver}.
4288      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4289      * @param {Event} e The event
4290      * @param {Object} data An object containing arbitrary data supplied by the drag source
4291      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4292      * underlying {@link Roo.dd.StatusProxy} can be updated
4293      */
4294     notifyOver : function(dd, e, data){
4295         var n = this.getTargetFromEvent(e);
4296         if(!n){ // not over valid drop target
4297             if(this.lastOverNode){
4298                 this.onNodeOut(this.lastOverNode, dd, e, data);
4299                 this.lastOverNode = null;
4300             }
4301             return this.onContainerOver(dd, e, data);
4302         }
4303         if(this.lastOverNode != n){
4304             if(this.lastOverNode){
4305                 this.onNodeOut(this.lastOverNode, dd, e, data);
4306             }
4307             this.onNodeEnter(n, dd, e, data);
4308             this.lastOverNode = n;
4309         }
4310         return this.onNodeOver(n, dd, e, data);
4311     },
4312
4313     /**
4314      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4315      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4316      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4317      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4318      * @param {Event} e The event
4319      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4320      */
4321     notifyOut : function(dd, e, data){
4322         if(this.lastOverNode){
4323             this.onNodeOut(this.lastOverNode, dd, e, data);
4324             this.lastOverNode = null;
4325         }
4326     },
4327
4328     /**
4329      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4330      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4331      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4332      * otherwise it will call {@link #onContainerDrop}.
4333      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4334      * @param {Event} e The event
4335      * @param {Object} data An object containing arbitrary data supplied by the drag source
4336      * @return {Boolean} True if the drop was valid, else false
4337      */
4338     notifyDrop : function(dd, e, data){
4339         if(this.lastOverNode){
4340             this.onNodeOut(this.lastOverNode, dd, e, data);
4341             this.lastOverNode = null;
4342         }
4343         var n = this.getTargetFromEvent(e);
4344         return n ?
4345             this.onNodeDrop(n, dd, e, data) :
4346             this.onContainerDrop(dd, e, data);
4347     },
4348
4349     // private
4350     triggerCacheRefresh : function(){
4351         Roo.dd.DDM.refreshCache(this.groups);
4352     }  
4353 });/*
4354  * Based on:
4355  * Ext JS Library 1.1.1
4356  * Copyright(c) 2006-2007, Ext JS, LLC.
4357  *
4358  * Originally Released Under LGPL - original licence link has changed is not relivant.
4359  *
4360  * Fork - LGPL
4361  * <script type="text/javascript">
4362  */
4363
4364
4365 /**
4366  * @class Roo.data.SortTypes
4367  * @singleton
4368  * Defines the default sorting (casting?) comparison functions used when sorting data.
4369  */
4370 Roo.data.SortTypes = {
4371     /**
4372      * Default sort that does nothing
4373      * @param {Mixed} s The value being converted
4374      * @return {Mixed} The comparison value
4375      */
4376     none : function(s){
4377         return s;
4378     },
4379     
4380     /**
4381      * The regular expression used to strip tags
4382      * @type {RegExp}
4383      * @property
4384      */
4385     stripTagsRE : /<\/?[^>]+>/gi,
4386     
4387     /**
4388      * Strips all HTML tags to sort on text only
4389      * @param {Mixed} s The value being converted
4390      * @return {String} The comparison value
4391      */
4392     asText : function(s){
4393         return String(s).replace(this.stripTagsRE, "");
4394     },
4395     
4396     /**
4397      * Strips all HTML tags to sort on text only - Case insensitive
4398      * @param {Mixed} s The value being converted
4399      * @return {String} The comparison value
4400      */
4401     asUCText : function(s){
4402         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4403     },
4404     
4405     /**
4406      * Case insensitive string
4407      * @param {Mixed} s The value being converted
4408      * @return {String} The comparison value
4409      */
4410     asUCString : function(s) {
4411         return String(s).toUpperCase();
4412     },
4413     
4414     /**
4415      * Date sorting
4416      * @param {Mixed} s The value being converted
4417      * @return {Number} The comparison value
4418      */
4419     asDate : function(s) {
4420         if(!s){
4421             return 0;
4422         }
4423         if(s instanceof Date){
4424             return s.getTime();
4425         }
4426         return Date.parse(String(s));
4427     },
4428     
4429     /**
4430      * Float sorting
4431      * @param {Mixed} s The value being converted
4432      * @return {Float} The comparison value
4433      */
4434     asFloat : function(s) {
4435         var val = parseFloat(String(s).replace(/,/g, ""));
4436         if(isNaN(val)) val = 0;
4437         return val;
4438     },
4439     
4440     /**
4441      * Integer sorting
4442      * @param {Mixed} s The value being converted
4443      * @return {Number} The comparison value
4444      */
4445     asInt : function(s) {
4446         var val = parseInt(String(s).replace(/,/g, ""));
4447         if(isNaN(val)) val = 0;
4448         return val;
4449     }
4450 };/*
4451  * Based on:
4452  * Ext JS Library 1.1.1
4453  * Copyright(c) 2006-2007, Ext JS, LLC.
4454  *
4455  * Originally Released Under LGPL - original licence link has changed is not relivant.
4456  *
4457  * Fork - LGPL
4458  * <script type="text/javascript">
4459  */
4460
4461 /**
4462 * @class Roo.data.Record
4463  * Instances of this class encapsulate both record <em>definition</em> information, and record
4464  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4465  * to access Records cached in an {@link Roo.data.Store} object.<br>
4466  * <p>
4467  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4468  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4469  * objects.<br>
4470  * <p>
4471  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4472  * @constructor
4473  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4474  * {@link #create}. The parameters are the same.
4475  * @param {Array} data An associative Array of data values keyed by the field name.
4476  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4477  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4478  * not specified an integer id is generated.
4479  */
4480 Roo.data.Record = function(data, id){
4481     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4482     this.data = data;
4483 };
4484
4485 /**
4486  * Generate a constructor for a specific record layout.
4487  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4488  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4489  * Each field definition object may contain the following properties: <ul>
4490  * <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,
4491  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4492  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4493  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4494  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4495  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4496  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4497  * this may be omitted.</p></li>
4498  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4499  * <ul><li>auto (Default, implies no conversion)</li>
4500  * <li>string</li>
4501  * <li>int</li>
4502  * <li>float</li>
4503  * <li>boolean</li>
4504  * <li>date</li></ul></p></li>
4505  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4506  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4507  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4508  * by the Reader into an object that will be stored in the Record. It is passed the
4509  * following parameters:<ul>
4510  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4511  * </ul></p></li>
4512  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4513  * </ul>
4514  * <br>usage:<br><pre><code>
4515 var TopicRecord = Roo.data.Record.create(
4516     {name: 'title', mapping: 'topic_title'},
4517     {name: 'author', mapping: 'username'},
4518     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4519     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4520     {name: 'lastPoster', mapping: 'user2'},
4521     {name: 'excerpt', mapping: 'post_text'}
4522 );
4523
4524 var myNewRecord = new TopicRecord({
4525     title: 'Do my job please',
4526     author: 'noobie',
4527     totalPosts: 1,
4528     lastPost: new Date(),
4529     lastPoster: 'Animal',
4530     excerpt: 'No way dude!'
4531 });
4532 myStore.add(myNewRecord);
4533 </code></pre>
4534  * @method create
4535  * @static
4536  */
4537 Roo.data.Record.create = function(o){
4538     var f = function(){
4539         f.superclass.constructor.apply(this, arguments);
4540     };
4541     Roo.extend(f, Roo.data.Record);
4542     var p = f.prototype;
4543     p.fields = new Roo.util.MixedCollection(false, function(field){
4544         return field.name;
4545     });
4546     for(var i = 0, len = o.length; i < len; i++){
4547         p.fields.add(new Roo.data.Field(o[i]));
4548     }
4549     f.getField = function(name){
4550         return p.fields.get(name);  
4551     };
4552     return f;
4553 };
4554
4555 Roo.data.Record.AUTO_ID = 1000;
4556 Roo.data.Record.EDIT = 'edit';
4557 Roo.data.Record.REJECT = 'reject';
4558 Roo.data.Record.COMMIT = 'commit';
4559
4560 Roo.data.Record.prototype = {
4561     /**
4562      * Readonly flag - true if this record has been modified.
4563      * @type Boolean
4564      */
4565     dirty : false,
4566     editing : false,
4567     error: null,
4568     modified: null,
4569
4570     // private
4571     join : function(store){
4572         this.store = store;
4573     },
4574
4575     /**
4576      * Set the named field to the specified value.
4577      * @param {String} name The name of the field to set.
4578      * @param {Object} value The value to set the field to.
4579      */
4580     set : function(name, value){
4581         if(this.data[name] == value){
4582             return;
4583         }
4584         this.dirty = true;
4585         if(!this.modified){
4586             this.modified = {};
4587         }
4588         if(typeof this.modified[name] == 'undefined'){
4589             this.modified[name] = this.data[name];
4590         }
4591         this.data[name] = value;
4592         if(!this.editing && this.store){
4593             this.store.afterEdit(this);
4594         }       
4595     },
4596
4597     /**
4598      * Get the value of the named field.
4599      * @param {String} name The name of the field to get the value of.
4600      * @return {Object} The value of the field.
4601      */
4602     get : function(name){
4603         return this.data[name]; 
4604     },
4605
4606     // private
4607     beginEdit : function(){
4608         this.editing = true;
4609         this.modified = {}; 
4610     },
4611
4612     // private
4613     cancelEdit : function(){
4614         this.editing = false;
4615         delete this.modified;
4616     },
4617
4618     // private
4619     endEdit : function(){
4620         this.editing = false;
4621         if(this.dirty && this.store){
4622             this.store.afterEdit(this);
4623         }
4624     },
4625
4626     /**
4627      * Usually called by the {@link Roo.data.Store} which owns the Record.
4628      * Rejects all changes made to the Record since either creation, or the last commit operation.
4629      * Modified fields are reverted to their original values.
4630      * <p>
4631      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4632      * of reject operations.
4633      */
4634     reject : function(){
4635         var m = this.modified;
4636         for(var n in m){
4637             if(typeof m[n] != "function"){
4638                 this.data[n] = m[n];
4639             }
4640         }
4641         this.dirty = false;
4642         delete this.modified;
4643         this.editing = false;
4644         if(this.store){
4645             this.store.afterReject(this);
4646         }
4647     },
4648
4649     /**
4650      * Usually called by the {@link Roo.data.Store} which owns the Record.
4651      * Commits all changes made to the Record since either creation, or the last commit operation.
4652      * <p>
4653      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4654      * of commit operations.
4655      */
4656     commit : function(){
4657         this.dirty = false;
4658         delete this.modified;
4659         this.editing = false;
4660         if(this.store){
4661             this.store.afterCommit(this);
4662         }
4663     },
4664
4665     // private
4666     hasError : function(){
4667         return this.error != null;
4668     },
4669
4670     // private
4671     clearError : function(){
4672         this.error = null;
4673     },
4674
4675     /**
4676      * Creates a copy of this record.
4677      * @param {String} id (optional) A new record id if you don't want to use this record's id
4678      * @return {Record}
4679      */
4680     copy : function(newId) {
4681         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4682     }
4683 };/*
4684  * Based on:
4685  * Ext JS Library 1.1.1
4686  * Copyright(c) 2006-2007, Ext JS, LLC.
4687  *
4688  * Originally Released Under LGPL - original licence link has changed is not relivant.
4689  *
4690  * Fork - LGPL
4691  * <script type="text/javascript">
4692  */
4693
4694
4695
4696 /**
4697  * @class Roo.data.Store
4698  * @extends Roo.util.Observable
4699  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4700  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4701  * <p>
4702  * 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
4703  * has no knowledge of the format of the data returned by the Proxy.<br>
4704  * <p>
4705  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4706  * instances from the data object. These records are cached and made available through accessor functions.
4707  * @constructor
4708  * Creates a new Store.
4709  * @param {Object} config A config object containing the objects needed for the Store to access data,
4710  * and read the data into Records.
4711  */
4712 Roo.data.Store = function(config){
4713     this.data = new Roo.util.MixedCollection(false);
4714     this.data.getKey = function(o){
4715         return o.id;
4716     };
4717     this.baseParams = {};
4718     // private
4719     this.paramNames = {
4720         "start" : "start",
4721         "limit" : "limit",
4722         "sort" : "sort",
4723         "dir" : "dir",
4724         "multisort" : "_multisort"
4725     };
4726
4727     if(config && config.data){
4728         this.inlineData = config.data;
4729         delete config.data;
4730     }
4731
4732     Roo.apply(this, config);
4733     
4734     if(this.reader){ // reader passed
4735         this.reader = Roo.factory(this.reader, Roo.data);
4736         this.reader.xmodule = this.xmodule || false;
4737         if(!this.recordType){
4738             this.recordType = this.reader.recordType;
4739         }
4740         if(this.reader.onMetaChange){
4741             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4742         }
4743     }
4744
4745     if(this.recordType){
4746         this.fields = this.recordType.prototype.fields;
4747     }
4748     this.modified = [];
4749
4750     this.addEvents({
4751         /**
4752          * @event datachanged
4753          * Fires when the data cache has changed, and a widget which is using this Store
4754          * as a Record cache should refresh its view.
4755          * @param {Store} this
4756          */
4757         datachanged : true,
4758         /**
4759          * @event metachange
4760          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4761          * @param {Store} this
4762          * @param {Object} meta The JSON metadata
4763          */
4764         metachange : true,
4765         /**
4766          * @event add
4767          * Fires when Records have been added to the Store
4768          * @param {Store} this
4769          * @param {Roo.data.Record[]} records The array of Records added
4770          * @param {Number} index The index at which the record(s) were added
4771          */
4772         add : true,
4773         /**
4774          * @event remove
4775          * Fires when a Record has been removed from the Store
4776          * @param {Store} this
4777          * @param {Roo.data.Record} record The Record that was removed
4778          * @param {Number} index The index at which the record was removed
4779          */
4780         remove : true,
4781         /**
4782          * @event update
4783          * Fires when a Record has been updated
4784          * @param {Store} this
4785          * @param {Roo.data.Record} record The Record that was updated
4786          * @param {String} operation The update operation being performed.  Value may be one of:
4787          * <pre><code>
4788  Roo.data.Record.EDIT
4789  Roo.data.Record.REJECT
4790  Roo.data.Record.COMMIT
4791          * </code></pre>
4792          */
4793         update : true,
4794         /**
4795          * @event clear
4796          * Fires when the data cache has been cleared.
4797          * @param {Store} this
4798          */
4799         clear : true,
4800         /**
4801          * @event beforeload
4802          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4803          * the load action will be canceled.
4804          * @param {Store} this
4805          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4806          */
4807         beforeload : true,
4808         /**
4809          * @event load
4810          * Fires after a new set of Records has been loaded.
4811          * @param {Store} this
4812          * @param {Roo.data.Record[]} records The Records that were loaded
4813          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4814          */
4815         load : true,
4816         /**
4817          * @event loadexception
4818          * Fires if an exception occurs in the Proxy during loading.
4819          * Called with the signature of the Proxy's "loadexception" event.
4820          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4821          * 
4822          * @param {Proxy} 
4823          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4824          * @param {Object} load options 
4825          * @param {Object} jsonData from your request (normally this contains the Exception)
4826          */
4827         loadexception : true
4828     });
4829     
4830     if(this.proxy){
4831         this.proxy = Roo.factory(this.proxy, Roo.data);
4832         this.proxy.xmodule = this.xmodule || false;
4833         this.relayEvents(this.proxy,  ["loadexception"]);
4834     }
4835     this.sortToggle = {};
4836     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4837
4838     Roo.data.Store.superclass.constructor.call(this);
4839
4840     if(this.inlineData){
4841         this.loadData(this.inlineData);
4842         delete this.inlineData;
4843     }
4844 };
4845 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4846      /**
4847     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4848     * without a remote query - used by combo/forms at present.
4849     */
4850     
4851     /**
4852     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4853     */
4854     /**
4855     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4856     */
4857     /**
4858     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4859     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4860     */
4861     /**
4862     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4863     * on any HTTP request
4864     */
4865     /**
4866     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4867     */
4868     /**
4869     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4870     */
4871     multiSort: false,
4872     /**
4873     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4874     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4875     */
4876     remoteSort : false,
4877
4878     /**
4879     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4880      * loaded or when a record is removed. (defaults to false).
4881     */
4882     pruneModifiedRecords : false,
4883
4884     // private
4885     lastOptions : null,
4886
4887     /**
4888      * Add Records to the Store and fires the add event.
4889      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4890      */
4891     add : function(records){
4892         records = [].concat(records);
4893         for(var i = 0, len = records.length; i < len; i++){
4894             records[i].join(this);
4895         }
4896         var index = this.data.length;
4897         this.data.addAll(records);
4898         this.fireEvent("add", this, records, index);
4899     },
4900
4901     /**
4902      * Remove a Record from the Store and fires the remove event.
4903      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4904      */
4905     remove : function(record){
4906         var index = this.data.indexOf(record);
4907         this.data.removeAt(index);
4908         if(this.pruneModifiedRecords){
4909             this.modified.remove(record);
4910         }
4911         this.fireEvent("remove", this, record, index);
4912     },
4913
4914     /**
4915      * Remove all Records from the Store and fires the clear event.
4916      */
4917     removeAll : function(){
4918         this.data.clear();
4919         if(this.pruneModifiedRecords){
4920             this.modified = [];
4921         }
4922         this.fireEvent("clear", this);
4923     },
4924
4925     /**
4926      * Inserts Records to the Store at the given index and fires the add event.
4927      * @param {Number} index The start index at which to insert the passed Records.
4928      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4929      */
4930     insert : function(index, records){
4931         records = [].concat(records);
4932         for(var i = 0, len = records.length; i < len; i++){
4933             this.data.insert(index, records[i]);
4934             records[i].join(this);
4935         }
4936         this.fireEvent("add", this, records, index);
4937     },
4938
4939     /**
4940      * Get the index within the cache of the passed Record.
4941      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4942      * @return {Number} The index of the passed Record. Returns -1 if not found.
4943      */
4944     indexOf : function(record){
4945         return this.data.indexOf(record);
4946     },
4947
4948     /**
4949      * Get the index within the cache of the Record with the passed id.
4950      * @param {String} id The id of the Record to find.
4951      * @return {Number} The index of the Record. Returns -1 if not found.
4952      */
4953     indexOfId : function(id){
4954         return this.data.indexOfKey(id);
4955     },
4956
4957     /**
4958      * Get the Record with the specified id.
4959      * @param {String} id The id of the Record to find.
4960      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4961      */
4962     getById : function(id){
4963         return this.data.key(id);
4964     },
4965
4966     /**
4967      * Get the Record at the specified index.
4968      * @param {Number} index The index of the Record to find.
4969      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4970      */
4971     getAt : function(index){
4972         return this.data.itemAt(index);
4973     },
4974
4975     /**
4976      * Returns a range of Records between specified indices.
4977      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4978      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4979      * @return {Roo.data.Record[]} An array of Records
4980      */
4981     getRange : function(start, end){
4982         return this.data.getRange(start, end);
4983     },
4984
4985     // private
4986     storeOptions : function(o){
4987         o = Roo.apply({}, o);
4988         delete o.callback;
4989         delete o.scope;
4990         this.lastOptions = o;
4991     },
4992
4993     /**
4994      * Loads the Record cache from the configured Proxy using the configured Reader.
4995      * <p>
4996      * If using remote paging, then the first load call must specify the <em>start</em>
4997      * and <em>limit</em> properties in the options.params property to establish the initial
4998      * position within the dataset, and the number of Records to cache on each read from the Proxy.
4999      * <p>
5000      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5001      * and this call will return before the new data has been loaded. Perform any post-processing
5002      * in a callback function, or in a "load" event handler.</strong>
5003      * <p>
5004      * @param {Object} options An object containing properties which control loading options:<ul>
5005      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5006      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5007      * passed the following arguments:<ul>
5008      * <li>r : Roo.data.Record[]</li>
5009      * <li>options: Options object from the load call</li>
5010      * <li>success: Boolean success indicator</li></ul></li>
5011      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5012      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5013      * </ul>
5014      */
5015     load : function(options){
5016         options = options || {};
5017         if(this.fireEvent("beforeload", this, options) !== false){
5018             this.storeOptions(options);
5019             var p = Roo.apply(options.params || {}, this.baseParams);
5020             // if meta was not loaded from remote source.. try requesting it.
5021             if (!this.reader.metaFromRemote) {
5022                 p._requestMeta = 1;
5023             }
5024             if(this.sortInfo && this.remoteSort){
5025                 var pn = this.paramNames;
5026                 p[pn["sort"]] = this.sortInfo.field;
5027                 p[pn["dir"]] = this.sortInfo.direction;
5028             }
5029             if (this.multiSort) {
5030                 var pn = this.paramNames;
5031                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5032             }
5033             
5034             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5035         }
5036     },
5037
5038     /**
5039      * Reloads the Record cache from the configured Proxy using the configured Reader and
5040      * the options from the last load operation performed.
5041      * @param {Object} options (optional) An object containing properties which may override the options
5042      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5043      * the most recently used options are reused).
5044      */
5045     reload : function(options){
5046         this.load(Roo.applyIf(options||{}, this.lastOptions));
5047     },
5048
5049     // private
5050     // Called as a callback by the Reader during a load operation.
5051     loadRecords : function(o, options, success){
5052         if(!o || success === false){
5053             if(success !== false){
5054                 this.fireEvent("load", this, [], options);
5055             }
5056             if(options.callback){
5057                 options.callback.call(options.scope || this, [], options, false);
5058             }
5059             return;
5060         }
5061         // if data returned failure - throw an exception.
5062         if (o.success === false) {
5063             // show a message if no listener is registered.
5064             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5065                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5066             }
5067             // loadmask wil be hooked into this..
5068             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5069             return;
5070         }
5071         var r = o.records, t = o.totalRecords || r.length;
5072         if(!options || options.add !== true){
5073             if(this.pruneModifiedRecords){
5074                 this.modified = [];
5075             }
5076             for(var i = 0, len = r.length; i < len; i++){
5077                 r[i].join(this);
5078             }
5079             if(this.snapshot){
5080                 this.data = this.snapshot;
5081                 delete this.snapshot;
5082             }
5083             this.data.clear();
5084             this.data.addAll(r);
5085             this.totalLength = t;
5086             this.applySort();
5087             this.fireEvent("datachanged", this);
5088         }else{
5089             this.totalLength = Math.max(t, this.data.length+r.length);
5090             this.add(r);
5091         }
5092         this.fireEvent("load", this, r, options);
5093         if(options.callback){
5094             options.callback.call(options.scope || this, r, options, true);
5095         }
5096     },
5097
5098
5099     /**
5100      * Loads data from a passed data block. A Reader which understands the format of the data
5101      * must have been configured in the constructor.
5102      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5103      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5104      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5105      */
5106     loadData : function(o, append){
5107         var r = this.reader.readRecords(o);
5108         this.loadRecords(r, {add: append}, true);
5109     },
5110
5111     /**
5112      * Gets the number of cached records.
5113      * <p>
5114      * <em>If using paging, this may not be the total size of the dataset. If the data object
5115      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5116      * the data set size</em>
5117      */
5118     getCount : function(){
5119         return this.data.length || 0;
5120     },
5121
5122     /**
5123      * Gets the total number of records in the dataset as returned by the server.
5124      * <p>
5125      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5126      * the dataset size</em>
5127      */
5128     getTotalCount : function(){
5129         return this.totalLength || 0;
5130     },
5131
5132     /**
5133      * Returns the sort state of the Store as an object with two properties:
5134      * <pre><code>
5135  field {String} The name of the field by which the Records are sorted
5136  direction {String} The sort order, "ASC" or "DESC"
5137      * </code></pre>
5138      */
5139     getSortState : function(){
5140         return this.sortInfo;
5141     },
5142
5143     // private
5144     applySort : function(){
5145         if(this.sortInfo && !this.remoteSort){
5146             var s = this.sortInfo, f = s.field;
5147             var st = this.fields.get(f).sortType;
5148             var fn = function(r1, r2){
5149                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5150                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5151             };
5152             this.data.sort(s.direction, fn);
5153             if(this.snapshot && this.snapshot != this.data){
5154                 this.snapshot.sort(s.direction, fn);
5155             }
5156         }
5157     },
5158
5159     /**
5160      * Sets the default sort column and order to be used by the next load operation.
5161      * @param {String} fieldName The name of the field to sort by.
5162      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5163      */
5164     setDefaultSort : function(field, dir){
5165         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5166     },
5167
5168     /**
5169      * Sort the Records.
5170      * If remote sorting is used, the sort is performed on the server, and the cache is
5171      * reloaded. If local sorting is used, the cache is sorted internally.
5172      * @param {String} fieldName The name of the field to sort by.
5173      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5174      */
5175     sort : function(fieldName, dir){
5176         var f = this.fields.get(fieldName);
5177         if(!dir){
5178             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5179             
5180             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5181                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5182             }else{
5183                 dir = f.sortDir;
5184             }
5185         }
5186         this.sortToggle[f.name] = dir;
5187         this.sortInfo = {field: f.name, direction: dir};
5188         if(!this.remoteSort){
5189             this.applySort();
5190             this.fireEvent("datachanged", this);
5191         }else{
5192             this.load(this.lastOptions);
5193         }
5194     },
5195
5196     /**
5197      * Calls the specified function for each of the Records in the cache.
5198      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5199      * Returning <em>false</em> aborts and exits the iteration.
5200      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5201      */
5202     each : function(fn, scope){
5203         this.data.each(fn, scope);
5204     },
5205
5206     /**
5207      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5208      * (e.g., during paging).
5209      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5210      */
5211     getModifiedRecords : function(){
5212         return this.modified;
5213     },
5214
5215     // private
5216     createFilterFn : function(property, value, anyMatch){
5217         if(!value.exec){ // not a regex
5218             value = String(value);
5219             if(value.length == 0){
5220                 return false;
5221             }
5222             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5223         }
5224         return function(r){
5225             return value.test(r.data[property]);
5226         };
5227     },
5228
5229     /**
5230      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5231      * @param {String} property A field on your records
5232      * @param {Number} start The record index to start at (defaults to 0)
5233      * @param {Number} end The last record index to include (defaults to length - 1)
5234      * @return {Number} The sum
5235      */
5236     sum : function(property, start, end){
5237         var rs = this.data.items, v = 0;
5238         start = start || 0;
5239         end = (end || end === 0) ? end : rs.length-1;
5240
5241         for(var i = start; i <= end; i++){
5242             v += (rs[i].data[property] || 0);
5243         }
5244         return v;
5245     },
5246
5247     /**
5248      * Filter the records by a specified property.
5249      * @param {String} field A field on your records
5250      * @param {String/RegExp} value Either a string that the field
5251      * should start with or a RegExp to test against the field
5252      * @param {Boolean} anyMatch True to match any part not just the beginning
5253      */
5254     filter : function(property, value, anyMatch){
5255         var fn = this.createFilterFn(property, value, anyMatch);
5256         return fn ? this.filterBy(fn) : this.clearFilter();
5257     },
5258
5259     /**
5260      * Filter by a function. The specified function will be called with each
5261      * record in this data source. If the function returns true the record is included,
5262      * otherwise it is filtered.
5263      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5264      * @param {Object} scope (optional) The scope of the function (defaults to this)
5265      */
5266     filterBy : function(fn, scope){
5267         this.snapshot = this.snapshot || this.data;
5268         this.data = this.queryBy(fn, scope||this);
5269         this.fireEvent("datachanged", this);
5270     },
5271
5272     /**
5273      * Query the records by a specified property.
5274      * @param {String} field A field on your records
5275      * @param {String/RegExp} value Either a string that the field
5276      * should start with or a RegExp to test against the field
5277      * @param {Boolean} anyMatch True to match any part not just the beginning
5278      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5279      */
5280     query : function(property, value, anyMatch){
5281         var fn = this.createFilterFn(property, value, anyMatch);
5282         return fn ? this.queryBy(fn) : this.data.clone();
5283     },
5284
5285     /**
5286      * Query by a function. The specified function will be called with each
5287      * record in this data source. If the function returns true the record is included
5288      * in the results.
5289      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5290      * @param {Object} scope (optional) The scope of the function (defaults to this)
5291       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5292      **/
5293     queryBy : function(fn, scope){
5294         var data = this.snapshot || this.data;
5295         return data.filterBy(fn, scope||this);
5296     },
5297
5298     /**
5299      * Collects unique values for a particular dataIndex from this store.
5300      * @param {String} dataIndex The property to collect
5301      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5302      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5303      * @return {Array} An array of the unique values
5304      **/
5305     collect : function(dataIndex, allowNull, bypassFilter){
5306         var d = (bypassFilter === true && this.snapshot) ?
5307                 this.snapshot.items : this.data.items;
5308         var v, sv, r = [], l = {};
5309         for(var i = 0, len = d.length; i < len; i++){
5310             v = d[i].data[dataIndex];
5311             sv = String(v);
5312             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5313                 l[sv] = true;
5314                 r[r.length] = v;
5315             }
5316         }
5317         return r;
5318     },
5319
5320     /**
5321      * Revert to a view of the Record cache with no filtering applied.
5322      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5323      */
5324     clearFilter : function(suppressEvent){
5325         if(this.snapshot && this.snapshot != this.data){
5326             this.data = this.snapshot;
5327             delete this.snapshot;
5328             if(suppressEvent !== true){
5329                 this.fireEvent("datachanged", this);
5330             }
5331         }
5332     },
5333
5334     // private
5335     afterEdit : function(record){
5336         if(this.modified.indexOf(record) == -1){
5337             this.modified.push(record);
5338         }
5339         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5340     },
5341     
5342     // private
5343     afterReject : function(record){
5344         this.modified.remove(record);
5345         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5346     },
5347
5348     // private
5349     afterCommit : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5352     },
5353
5354     /**
5355      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5356      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5357      */
5358     commitChanges : function(){
5359         var m = this.modified.slice(0);
5360         this.modified = [];
5361         for(var i = 0, len = m.length; i < len; i++){
5362             m[i].commit();
5363         }
5364     },
5365
5366     /**
5367      * Cancel outstanding changes on all changed records.
5368      */
5369     rejectChanges : function(){
5370         var m = this.modified.slice(0);
5371         this.modified = [];
5372         for(var i = 0, len = m.length; i < len; i++){
5373             m[i].reject();
5374         }
5375     },
5376
5377     onMetaChange : function(meta, rtype, o){
5378         this.recordType = rtype;
5379         this.fields = rtype.prototype.fields;
5380         delete this.snapshot;
5381         this.sortInfo = meta.sortInfo || this.sortInfo;
5382         this.modified = [];
5383         this.fireEvent('metachange', this, this.reader.meta);
5384     }
5385 });/*
5386  * Based on:
5387  * Ext JS Library 1.1.1
5388  * Copyright(c) 2006-2007, Ext JS, LLC.
5389  *
5390  * Originally Released Under LGPL - original licence link has changed is not relivant.
5391  *
5392  * Fork - LGPL
5393  * <script type="text/javascript">
5394  */
5395
5396 /**
5397  * @class Roo.data.SimpleStore
5398  * @extends Roo.data.Store
5399  * Small helper class to make creating Stores from Array data easier.
5400  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5401  * @cfg {Array} fields An array of field definition objects, or field name strings.
5402  * @cfg {Array} data The multi-dimensional array of data
5403  * @constructor
5404  * @param {Object} config
5405  */
5406 Roo.data.SimpleStore = function(config){
5407     Roo.data.SimpleStore.superclass.constructor.call(this, {
5408         isLocal : true,
5409         reader: new Roo.data.ArrayReader({
5410                 id: config.id
5411             },
5412             Roo.data.Record.create(config.fields)
5413         ),
5414         proxy : new Roo.data.MemoryProxy(config.data)
5415     });
5416     this.load();
5417 };
5418 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5419  * Based on:
5420  * Ext JS Library 1.1.1
5421  * Copyright(c) 2006-2007, Ext JS, LLC.
5422  *
5423  * Originally Released Under LGPL - original licence link has changed is not relivant.
5424  *
5425  * Fork - LGPL
5426  * <script type="text/javascript">
5427  */
5428
5429 /**
5430 /**
5431  * @extends Roo.data.Store
5432  * @class Roo.data.JsonStore
5433  * Small helper class to make creating Stores for JSON data easier. <br/>
5434 <pre><code>
5435 var store = new Roo.data.JsonStore({
5436     url: 'get-images.php',
5437     root: 'images',
5438     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5439 });
5440 </code></pre>
5441  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5442  * JsonReader and HttpProxy (unless inline data is provided).</b>
5443  * @cfg {Array} fields An array of field definition objects, or field name strings.
5444  * @constructor
5445  * @param {Object} config
5446  */
5447 Roo.data.JsonStore = function(c){
5448     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5449         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5450         reader: new Roo.data.JsonReader(c, c.fields)
5451     }));
5452 };
5453 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5454  * Based on:
5455  * Ext JS Library 1.1.1
5456  * Copyright(c) 2006-2007, Ext JS, LLC.
5457  *
5458  * Originally Released Under LGPL - original licence link has changed is not relivant.
5459  *
5460  * Fork - LGPL
5461  * <script type="text/javascript">
5462  */
5463
5464  
5465 Roo.data.Field = function(config){
5466     if(typeof config == "string"){
5467         config = {name: config};
5468     }
5469     Roo.apply(this, config);
5470     
5471     if(!this.type){
5472         this.type = "auto";
5473     }
5474     
5475     var st = Roo.data.SortTypes;
5476     // named sortTypes are supported, here we look them up
5477     if(typeof this.sortType == "string"){
5478         this.sortType = st[this.sortType];
5479     }
5480     
5481     // set default sortType for strings and dates
5482     if(!this.sortType){
5483         switch(this.type){
5484             case "string":
5485                 this.sortType = st.asUCString;
5486                 break;
5487             case "date":
5488                 this.sortType = st.asDate;
5489                 break;
5490             default:
5491                 this.sortType = st.none;
5492         }
5493     }
5494
5495     // define once
5496     var stripRe = /[\$,%]/g;
5497
5498     // prebuilt conversion function for this field, instead of
5499     // switching every time we're reading a value
5500     if(!this.convert){
5501         var cv, dateFormat = this.dateFormat;
5502         switch(this.type){
5503             case "":
5504             case "auto":
5505             case undefined:
5506                 cv = function(v){ return v; };
5507                 break;
5508             case "string":
5509                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5510                 break;
5511             case "int":
5512                 cv = function(v){
5513                     return v !== undefined && v !== null && v !== '' ?
5514                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5515                     };
5516                 break;
5517             case "float":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5521                     };
5522                 break;
5523             case "bool":
5524             case "boolean":
5525                 cv = function(v){ return v === true || v === "true" || v == 1; };
5526                 break;
5527             case "date":
5528                 cv = function(v){
5529                     if(!v){
5530                         return '';
5531                     }
5532                     if(v instanceof Date){
5533                         return v;
5534                     }
5535                     if(dateFormat){
5536                         if(dateFormat == "timestamp"){
5537                             return new Date(v*1000);
5538                         }
5539                         return Date.parseDate(v, dateFormat);
5540                     }
5541                     var parsed = Date.parse(v);
5542                     return parsed ? new Date(parsed) : null;
5543                 };
5544              break;
5545             
5546         }
5547         this.convert = cv;
5548     }
5549 };
5550
5551 Roo.data.Field.prototype = {
5552     dateFormat: null,
5553     defaultValue: "",
5554     mapping: null,
5555     sortType : null,
5556     sortDir : "ASC"
5557 };/*
5558  * Based on:
5559  * Ext JS Library 1.1.1
5560  * Copyright(c) 2006-2007, Ext JS, LLC.
5561  *
5562  * Originally Released Under LGPL - original licence link has changed is not relivant.
5563  *
5564  * Fork - LGPL
5565  * <script type="text/javascript">
5566  */
5567  
5568 // Base class for reading structured data from a data source.  This class is intended to be
5569 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5570
5571 /**
5572  * @class Roo.data.DataReader
5573  * Base class for reading structured data from a data source.  This class is intended to be
5574  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5575  */
5576
5577 Roo.data.DataReader = function(meta, recordType){
5578     
5579     this.meta = meta;
5580     
5581     this.recordType = recordType instanceof Array ? 
5582         Roo.data.Record.create(recordType) : recordType;
5583 };
5584
5585 Roo.data.DataReader.prototype = {
5586      /**
5587      * Create an empty record
5588      * @param {Object} data (optional) - overlay some values
5589      * @return {Roo.data.Record} record created.
5590      */
5591     newRow :  function(d) {
5592         var da =  {};
5593         this.recordType.prototype.fields.each(function(c) {
5594             switch( c.type) {
5595                 case 'int' : da[c.name] = 0; break;
5596                 case 'date' : da[c.name] = new Date(); break;
5597                 case 'float' : da[c.name] = 0.0; break;
5598                 case 'boolean' : da[c.name] = false; break;
5599                 default : da[c.name] = ""; break;
5600             }
5601             
5602         });
5603         return new this.recordType(Roo.apply(da, d));
5604     }
5605     
5606 };/*
5607  * Based on:
5608  * Ext JS Library 1.1.1
5609  * Copyright(c) 2006-2007, Ext JS, LLC.
5610  *
5611  * Originally Released Under LGPL - original licence link has changed is not relivant.
5612  *
5613  * Fork - LGPL
5614  * <script type="text/javascript">
5615  */
5616
5617 /**
5618  * @class Roo.data.DataProxy
5619  * @extends Roo.data.Observable
5620  * This class is an abstract base class for implementations which provide retrieval of
5621  * unformatted data objects.<br>
5622  * <p>
5623  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5624  * (of the appropriate type which knows how to parse the data object) to provide a block of
5625  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5626  * <p>
5627  * Custom implementations must implement the load method as described in
5628  * {@link Roo.data.HttpProxy#load}.
5629  */
5630 Roo.data.DataProxy = function(){
5631     this.addEvents({
5632         /**
5633          * @event beforeload
5634          * Fires before a network request is made to retrieve a data object.
5635          * @param {Object} This DataProxy object.
5636          * @param {Object} params The params parameter to the load function.
5637          */
5638         beforeload : true,
5639         /**
5640          * @event load
5641          * Fires before the load method's callback is called.
5642          * @param {Object} This DataProxy object.
5643          * @param {Object} o The data object.
5644          * @param {Object} arg The callback argument object passed to the load function.
5645          */
5646         load : true,
5647         /**
5648          * @event loadexception
5649          * Fires if an Exception occurs during data retrieval.
5650          * @param {Object} This DataProxy object.
5651          * @param {Object} o The data object.
5652          * @param {Object} arg The callback argument object passed to the load function.
5653          * @param {Object} e The Exception.
5654          */
5655         loadexception : true
5656     });
5657     Roo.data.DataProxy.superclass.constructor.call(this);
5658 };
5659
5660 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5661
5662     /**
5663      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5664      */
5665 /*
5666  * Based on:
5667  * Ext JS Library 1.1.1
5668  * Copyright(c) 2006-2007, Ext JS, LLC.
5669  *
5670  * Originally Released Under LGPL - original licence link has changed is not relivant.
5671  *
5672  * Fork - LGPL
5673  * <script type="text/javascript">
5674  */
5675 /**
5676  * @class Roo.data.MemoryProxy
5677  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5678  * to the Reader when its load method is called.
5679  * @constructor
5680  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5681  */
5682 Roo.data.MemoryProxy = function(data){
5683     if (data.data) {
5684         data = data.data;
5685     }
5686     Roo.data.MemoryProxy.superclass.constructor.call(this);
5687     this.data = data;
5688 };
5689
5690 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5691     /**
5692      * Load data from the requested source (in this case an in-memory
5693      * data object passed to the constructor), read the data object into
5694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5695      * process that block using the passed callback.
5696      * @param {Object} params This parameter is not used by the MemoryProxy class.
5697      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5698      * object into a block of Roo.data.Records.
5699      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5700      * The function must be passed <ul>
5701      * <li>The Record block object</li>
5702      * <li>The "arg" argument from the load function</li>
5703      * <li>A boolean success indicator</li>
5704      * </ul>
5705      * @param {Object} scope The scope in which to call the callback
5706      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5707      */
5708     load : function(params, reader, callback, scope, arg){
5709         params = params || {};
5710         var result;
5711         try {
5712             result = reader.readRecords(this.data);
5713         }catch(e){
5714             this.fireEvent("loadexception", this, arg, null, e);
5715             callback.call(scope, null, arg, false);
5716             return;
5717         }
5718         callback.call(scope, result, arg, true);
5719     },
5720     
5721     // private
5722     update : function(params, records){
5723         
5724     }
5725 });/*
5726  * Based on:
5727  * Ext JS Library 1.1.1
5728  * Copyright(c) 2006-2007, Ext JS, LLC.
5729  *
5730  * Originally Released Under LGPL - original licence link has changed is not relivant.
5731  *
5732  * Fork - LGPL
5733  * <script type="text/javascript">
5734  */
5735 /**
5736  * @class Roo.data.HttpProxy
5737  * @extends Roo.data.DataProxy
5738  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5739  * configured to reference a certain URL.<br><br>
5740  * <p>
5741  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5742  * from which the running page was served.<br><br>
5743  * <p>
5744  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5745  * <p>
5746  * Be aware that to enable the browser to parse an XML document, the server must set
5747  * the Content-Type header in the HTTP response to "text/xml".
5748  * @constructor
5749  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5750  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5751  * will be used to make the request.
5752  */
5753 Roo.data.HttpProxy = function(conn){
5754     Roo.data.HttpProxy.superclass.constructor.call(this);
5755     // is conn a conn config or a real conn?
5756     this.conn = conn;
5757     this.useAjax = !conn || !conn.events;
5758   
5759 };
5760
5761 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5762     // thse are take from connection...
5763     
5764     /**
5765      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5766      */
5767     /**
5768      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5769      * extra parameters to each request made by this object. (defaults to undefined)
5770      */
5771     /**
5772      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5773      *  to each request made by this object. (defaults to undefined)
5774      */
5775     /**
5776      * @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)
5777      */
5778     /**
5779      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5780      */
5781      /**
5782      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5783      * @type Boolean
5784      */
5785   
5786
5787     /**
5788      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5789      * @type Boolean
5790      */
5791     /**
5792      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5793      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5794      * a finer-grained basis than the DataProxy events.
5795      */
5796     getConnection : function(){
5797         return this.useAjax ? Roo.Ajax : this.conn;
5798     },
5799
5800     /**
5801      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5802      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5803      * process that block using the passed callback.
5804      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5805      * for the request to the remote server.
5806      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5807      * object into a block of Roo.data.Records.
5808      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5809      * The function must be passed <ul>
5810      * <li>The Record block object</li>
5811      * <li>The "arg" argument from the load function</li>
5812      * <li>A boolean success indicator</li>
5813      * </ul>
5814      * @param {Object} scope The scope in which to call the callback
5815      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5816      */
5817     load : function(params, reader, callback, scope, arg){
5818         if(this.fireEvent("beforeload", this, params) !== false){
5819             var  o = {
5820                 params : params || {},
5821                 request: {
5822                     callback : callback,
5823                     scope : scope,
5824                     arg : arg
5825                 },
5826                 reader: reader,
5827                 callback : this.loadResponse,
5828                 scope: this
5829             };
5830             if(this.useAjax){
5831                 Roo.applyIf(o, this.conn);
5832                 if(this.activeRequest){
5833                     Roo.Ajax.abort(this.activeRequest);
5834                 }
5835                 this.activeRequest = Roo.Ajax.request(o);
5836             }else{
5837                 this.conn.request(o);
5838             }
5839         }else{
5840             callback.call(scope||this, null, arg, false);
5841         }
5842     },
5843
5844     // private
5845     loadResponse : function(o, success, response){
5846         delete this.activeRequest;
5847         if(!success){
5848             this.fireEvent("loadexception", this, o, response);
5849             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5850             return;
5851         }
5852         var result;
5853         try {
5854             result = o.reader.read(response);
5855         }catch(e){
5856             this.fireEvent("loadexception", this, o, response, e);
5857             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5858             return;
5859         }
5860         
5861         this.fireEvent("load", this, o, o.request.arg);
5862         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5863     },
5864
5865     // private
5866     update : function(dataSet){
5867
5868     },
5869
5870     // private
5871     updateResponse : function(dataSet){
5872
5873     }
5874 });/*
5875  * Based on:
5876  * Ext JS Library 1.1.1
5877  * Copyright(c) 2006-2007, Ext JS, LLC.
5878  *
5879  * Originally Released Under LGPL - original licence link has changed is not relivant.
5880  *
5881  * Fork - LGPL
5882  * <script type="text/javascript">
5883  */
5884
5885 /**
5886  * @class Roo.data.ScriptTagProxy
5887  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5888  * other than the originating domain of the running page.<br><br>
5889  * <p>
5890  * <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
5891  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5892  * <p>
5893  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5894  * source code that is used as the source inside a &lt;script> tag.<br><br>
5895  * <p>
5896  * In order for the browser to process the returned data, the server must wrap the data object
5897  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5898  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5899  * depending on whether the callback name was passed:
5900  * <p>
5901  * <pre><code>
5902 boolean scriptTag = false;
5903 String cb = request.getParameter("callback");
5904 if (cb != null) {
5905     scriptTag = true;
5906     response.setContentType("text/javascript");
5907 } else {
5908     response.setContentType("application/x-json");
5909 }
5910 Writer out = response.getWriter();
5911 if (scriptTag) {
5912     out.write(cb + "(");
5913 }
5914 out.print(dataBlock.toJsonString());
5915 if (scriptTag) {
5916     out.write(");");
5917 }
5918 </pre></code>
5919  *
5920  * @constructor
5921  * @param {Object} config A configuration object.
5922  */
5923 Roo.data.ScriptTagProxy = function(config){
5924     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5925     Roo.apply(this, config);
5926     this.head = document.getElementsByTagName("head")[0];
5927 };
5928
5929 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5930
5931 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5932     /**
5933      * @cfg {String} url The URL from which to request the data object.
5934      */
5935     /**
5936      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5937      */
5938     timeout : 30000,
5939     /**
5940      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5941      * the server the name of the callback function set up by the load call to process the returned data object.
5942      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5943      * javascript output which calls this named function passing the data object as its only parameter.
5944      */
5945     callbackParam : "callback",
5946     /**
5947      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5948      * name to the request.
5949      */
5950     nocache : true,
5951
5952     /**
5953      * Load data from the configured URL, read the data object into
5954      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5955      * process that block using the passed callback.
5956      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5957      * for the request to the remote server.
5958      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5959      * object into a block of Roo.data.Records.
5960      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5961      * The function must be passed <ul>
5962      * <li>The Record block object</li>
5963      * <li>The "arg" argument from the load function</li>
5964      * <li>A boolean success indicator</li>
5965      * </ul>
5966      * @param {Object} scope The scope in which to call the callback
5967      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5968      */
5969     load : function(params, reader, callback, scope, arg){
5970         if(this.fireEvent("beforeload", this, params) !== false){
5971
5972             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5973
5974             var url = this.url;
5975             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5976             if(this.nocache){
5977                 url += "&_dc=" + (new Date().getTime());
5978             }
5979             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5980             var trans = {
5981                 id : transId,
5982                 cb : "stcCallback"+transId,
5983                 scriptId : "stcScript"+transId,
5984                 params : params,
5985                 arg : arg,
5986                 url : url,
5987                 callback : callback,
5988                 scope : scope,
5989                 reader : reader
5990             };
5991             var conn = this;
5992
5993             window[trans.cb] = function(o){
5994                 conn.handleResponse(o, trans);
5995             };
5996
5997             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
5998
5999             if(this.autoAbort !== false){
6000                 this.abort();
6001             }
6002
6003             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6004
6005             var script = document.createElement("script");
6006             script.setAttribute("src", url);
6007             script.setAttribute("type", "text/javascript");
6008             script.setAttribute("id", trans.scriptId);
6009             this.head.appendChild(script);
6010
6011             this.trans = trans;
6012         }else{
6013             callback.call(scope||this, null, arg, false);
6014         }
6015     },
6016
6017     // private
6018     isLoading : function(){
6019         return this.trans ? true : false;
6020     },
6021
6022     /**
6023      * Abort the current server request.
6024      */
6025     abort : function(){
6026         if(this.isLoading()){
6027             this.destroyTrans(this.trans);
6028         }
6029     },
6030
6031     // private
6032     destroyTrans : function(trans, isLoaded){
6033         this.head.removeChild(document.getElementById(trans.scriptId));
6034         clearTimeout(trans.timeoutId);
6035         if(isLoaded){
6036             window[trans.cb] = undefined;
6037             try{
6038                 delete window[trans.cb];
6039             }catch(e){}
6040         }else{
6041             // if hasn't been loaded, wait for load to remove it to prevent script error
6042             window[trans.cb] = function(){
6043                 window[trans.cb] = undefined;
6044                 try{
6045                     delete window[trans.cb];
6046                 }catch(e){}
6047             };
6048         }
6049     },
6050
6051     // private
6052     handleResponse : function(o, trans){
6053         this.trans = false;
6054         this.destroyTrans(trans, true);
6055         var result;
6056         try {
6057             result = trans.reader.readRecords(o);
6058         }catch(e){
6059             this.fireEvent("loadexception", this, o, trans.arg, e);
6060             trans.callback.call(trans.scope||window, null, trans.arg, false);
6061             return;
6062         }
6063         this.fireEvent("load", this, o, trans.arg);
6064         trans.callback.call(trans.scope||window, result, trans.arg, true);
6065     },
6066
6067     // private
6068     handleFailure : function(trans){
6069         this.trans = false;
6070         this.destroyTrans(trans, false);
6071         this.fireEvent("loadexception", this, null, trans.arg);
6072         trans.callback.call(trans.scope||window, null, trans.arg, false);
6073     }
6074 });/*
6075  * Based on:
6076  * Ext JS Library 1.1.1
6077  * Copyright(c) 2006-2007, Ext JS, LLC.
6078  *
6079  * Originally Released Under LGPL - original licence link has changed is not relivant.
6080  *
6081  * Fork - LGPL
6082  * <script type="text/javascript">
6083  */
6084
6085 /**
6086  * @class Roo.data.JsonReader
6087  * @extends Roo.data.DataReader
6088  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6089  * based on mappings in a provided Roo.data.Record constructor.
6090  * 
6091  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6092  * in the reply previously. 
6093  * 
6094  * <p>
6095  * Example code:
6096  * <pre><code>
6097 var RecordDef = Roo.data.Record.create([
6098     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6099     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6100 ]);
6101 var myReader = new Roo.data.JsonReader({
6102     totalProperty: "results",    // The property which contains the total dataset size (optional)
6103     root: "rows",                // The property which contains an Array of row objects
6104     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6105 }, RecordDef);
6106 </code></pre>
6107  * <p>
6108  * This would consume a JSON file like this:
6109  * <pre><code>
6110 { 'results': 2, 'rows': [
6111     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6112     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6113 }
6114 </code></pre>
6115  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6116  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6117  * paged from the remote server.
6118  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6119  * @cfg {String} root name of the property which contains the Array of row objects.
6120  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6121  * @constructor
6122  * Create a new JsonReader
6123  * @param {Object} meta Metadata configuration options
6124  * @param {Object} recordType Either an Array of field definition objects,
6125  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6126  */
6127 Roo.data.JsonReader = function(meta, recordType){
6128     
6129     meta = meta || {};
6130     // set some defaults:
6131     Roo.applyIf(meta, {
6132         totalProperty: 'total',
6133         successProperty : 'success',
6134         root : 'data',
6135         id : 'id'
6136     });
6137     
6138     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6139 };
6140 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6141     
6142     /**
6143      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6144      * Used by Store query builder to append _requestMeta to params.
6145      * 
6146      */
6147     metaFromRemote : false,
6148     /**
6149      * This method is only used by a DataProxy which has retrieved data from a remote server.
6150      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6151      * @return {Object} data A data block which is used by an Roo.data.Store object as
6152      * a cache of Roo.data.Records.
6153      */
6154     read : function(response){
6155         var json = response.responseText;
6156        
6157         var o = /* eval:var:o */ eval("("+json+")");
6158         if(!o) {
6159             throw {message: "JsonReader.read: Json object not found"};
6160         }
6161         
6162         if(o.metaData){
6163             
6164             delete this.ef;
6165             this.metaFromRemote = true;
6166             this.meta = o.metaData;
6167             this.recordType = Roo.data.Record.create(o.metaData.fields);
6168             this.onMetaChange(this.meta, this.recordType, o);
6169         }
6170         return this.readRecords(o);
6171     },
6172
6173     // private function a store will implement
6174     onMetaChange : function(meta, recordType, o){
6175
6176     },
6177
6178     /**
6179          * @ignore
6180          */
6181     simpleAccess: function(obj, subsc) {
6182         return obj[subsc];
6183     },
6184
6185         /**
6186          * @ignore
6187          */
6188     getJsonAccessor: function(){
6189         var re = /[\[\.]/;
6190         return function(expr) {
6191             try {
6192                 return(re.test(expr))
6193                     ? new Function("obj", "return obj." + expr)
6194                     : function(obj){
6195                         return obj[expr];
6196                     };
6197             } catch(e){}
6198             return Roo.emptyFn;
6199         };
6200     }(),
6201
6202     /**
6203      * Create a data block containing Roo.data.Records from an XML document.
6204      * @param {Object} o An object which contains an Array of row objects in the property specified
6205      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6206      * which contains the total size of the dataset.
6207      * @return {Object} data A data block which is used by an Roo.data.Store object as
6208      * a cache of Roo.data.Records.
6209      */
6210     readRecords : function(o){
6211         /**
6212          * After any data loads, the raw JSON data is available for further custom processing.
6213          * @type Object
6214          */
6215         this.jsonData = o;
6216         var s = this.meta, Record = this.recordType,
6217             f = Record.prototype.fields, fi = f.items, fl = f.length;
6218
6219 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6220         if (!this.ef) {
6221             if(s.totalProperty) {
6222                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6223                 }
6224                 if(s.successProperty) {
6225                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6226                 }
6227                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6228                 if (s.id) {
6229                         var g = this.getJsonAccessor(s.id);
6230                         this.getId = function(rec) {
6231                                 var r = g(rec);
6232                                 return (r === undefined || r === "") ? null : r;
6233                         };
6234                 } else {
6235                         this.getId = function(){return null;};
6236                 }
6237             this.ef = [];
6238             for(var jj = 0; jj < fl; jj++){
6239                 f = fi[jj];
6240                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6241                 this.ef[jj] = this.getJsonAccessor(map);
6242             }
6243         }
6244
6245         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6246         if(s.totalProperty){
6247             var vt = parseInt(this.getTotal(o), 10);
6248             if(!isNaN(vt)){
6249                 totalRecords = vt;
6250             }
6251         }
6252         if(s.successProperty){
6253             var vs = this.getSuccess(o);
6254             if(vs === false || vs === 'false'){
6255                 success = false;
6256             }
6257         }
6258         var records = [];
6259             for(var i = 0; i < c; i++){
6260                     var n = root[i];
6261                 var values = {};
6262                 var id = this.getId(n);
6263                 for(var j = 0; j < fl; j++){
6264                     f = fi[j];
6265                 var v = this.ef[j](n);
6266                 if (!f.convert) {
6267                     Roo.log('missing convert for ' + f.name);
6268                     Roo.log(f);
6269                     continue;
6270                 }
6271                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6272                 }
6273                 var record = new Record(values, id);
6274                 record.json = n;
6275                 records[i] = record;
6276             }
6277             return {
6278                 success : success,
6279                 records : records,
6280                 totalRecords : totalRecords
6281             };
6282     }
6283 });/*
6284  * Based on:
6285  * Ext JS Library 1.1.1
6286  * Copyright(c) 2006-2007, Ext JS, LLC.
6287  *
6288  * Originally Released Under LGPL - original licence link has changed is not relivant.
6289  *
6290  * Fork - LGPL
6291  * <script type="text/javascript">
6292  */
6293
6294 /**
6295  * @class Roo.data.XmlReader
6296  * @extends Roo.data.DataReader
6297  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6298  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6299  * <p>
6300  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6301  * header in the HTTP response must be set to "text/xml".</em>
6302  * <p>
6303  * Example code:
6304  * <pre><code>
6305 var RecordDef = Roo.data.Record.create([
6306    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6307    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6308 ]);
6309 var myReader = new Roo.data.XmlReader({
6310    totalRecords: "results", // The element which contains the total dataset size (optional)
6311    record: "row",           // The repeated element which contains row information
6312    id: "id"                 // The element within the row that provides an ID for the record (optional)
6313 }, RecordDef);
6314 </code></pre>
6315  * <p>
6316  * This would consume an XML file like this:
6317  * <pre><code>
6318 &lt;?xml?>
6319 &lt;dataset>
6320  &lt;results>2&lt;/results>
6321  &lt;row>
6322    &lt;id>1&lt;/id>
6323    &lt;name>Bill&lt;/name>
6324    &lt;occupation>Gardener&lt;/occupation>
6325  &lt;/row>
6326  &lt;row>
6327    &lt;id>2&lt;/id>
6328    &lt;name>Ben&lt;/name>
6329    &lt;occupation>Horticulturalist&lt;/occupation>
6330  &lt;/row>
6331 &lt;/dataset>
6332 </code></pre>
6333  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6334  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6335  * paged from the remote server.
6336  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6337  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6338  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6339  * a record identifier value.
6340  * @constructor
6341  * Create a new XmlReader
6342  * @param {Object} meta Metadata configuration options
6343  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6344  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6345  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6346  */
6347 Roo.data.XmlReader = function(meta, recordType){
6348     meta = meta || {};
6349     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6350 };
6351 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6352     /**
6353      * This method is only used by a DataProxy which has retrieved data from a remote server.
6354          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6355          * to contain a method called 'responseXML' that returns an XML document object.
6356      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6357      * a cache of Roo.data.Records.
6358      */
6359     read : function(response){
6360         var doc = response.responseXML;
6361         if(!doc) {
6362             throw {message: "XmlReader.read: XML Document not available"};
6363         }
6364         return this.readRecords(doc);
6365     },
6366
6367     /**
6368      * Create a data block containing Roo.data.Records from an XML document.
6369          * @param {Object} doc A parsed XML document.
6370      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6371      * a cache of Roo.data.Records.
6372      */
6373     readRecords : function(doc){
6374         /**
6375          * After any data loads/reads, the raw XML Document is available for further custom processing.
6376          * @type XMLDocument
6377          */
6378         this.xmlData = doc;
6379         var root = doc.documentElement || doc;
6380         var q = Roo.DomQuery;
6381         var recordType = this.recordType, fields = recordType.prototype.fields;
6382         var sid = this.meta.id;
6383         var totalRecords = 0, success = true;
6384         if(this.meta.totalRecords){
6385             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6386         }
6387         
6388         if(this.meta.success){
6389             var sv = q.selectValue(this.meta.success, root, true);
6390             success = sv !== false && sv !== 'false';
6391         }
6392         var records = [];
6393         var ns = q.select(this.meta.record, root);
6394         for(var i = 0, len = ns.length; i < len; i++) {
6395                 var n = ns[i];
6396                 var values = {};
6397                 var id = sid ? q.selectValue(sid, n) : undefined;
6398                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6399                     var f = fields.items[j];
6400                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6401                     v = f.convert(v);
6402                     values[f.name] = v;
6403                 }
6404                 var record = new recordType(values, id);
6405                 record.node = n;
6406                 records[records.length] = record;
6407             }
6408
6409             return {
6410                 success : success,
6411                 records : records,
6412                 totalRecords : totalRecords || records.length
6413             };
6414     }
6415 });/*
6416  * Based on:
6417  * Ext JS Library 1.1.1
6418  * Copyright(c) 2006-2007, Ext JS, LLC.
6419  *
6420  * Originally Released Under LGPL - original licence link has changed is not relivant.
6421  *
6422  * Fork - LGPL
6423  * <script type="text/javascript">
6424  */
6425
6426 /**
6427  * @class Roo.data.ArrayReader
6428  * @extends Roo.data.DataReader
6429  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6430  * Each element of that Array represents a row of data fields. The
6431  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6432  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6433  * <p>
6434  * Example code:.
6435  * <pre><code>
6436 var RecordDef = Roo.data.Record.create([
6437     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6438     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6439 ]);
6440 var myReader = new Roo.data.ArrayReader({
6441     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6442 }, RecordDef);
6443 </code></pre>
6444  * <p>
6445  * This would consume an Array like this:
6446  * <pre><code>
6447 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6448   </code></pre>
6449  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6450  * @constructor
6451  * Create a new JsonReader
6452  * @param {Object} meta Metadata configuration options.
6453  * @param {Object} recordType Either an Array of field definition objects
6454  * as specified to {@link Roo.data.Record#create},
6455  * or an {@link Roo.data.Record} object
6456  * created using {@link Roo.data.Record#create}.
6457  */
6458 Roo.data.ArrayReader = function(meta, recordType){
6459     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6460 };
6461
6462 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6463     /**
6464      * Create a data block containing Roo.data.Records from an XML document.
6465      * @param {Object} o An Array of row objects which represents the dataset.
6466      * @return {Object} data A data block which is used by an Roo.data.Store object as
6467      * a cache of Roo.data.Records.
6468      */
6469     readRecords : function(o){
6470         var sid = this.meta ? this.meta.id : null;
6471         var recordType = this.recordType, fields = recordType.prototype.fields;
6472         var records = [];
6473         var root = o;
6474             for(var i = 0; i < root.length; i++){
6475                     var n = root[i];
6476                 var values = {};
6477                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6478                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6479                 var f = fields.items[j];
6480                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6481                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6482                 v = f.convert(v);
6483                 values[f.name] = v;
6484             }
6485                 var record = new recordType(values, id);
6486                 record.json = n;
6487                 records[records.length] = record;
6488             }
6489             return {
6490                 records : records,
6491                 totalRecords : records.length
6492             };
6493     }
6494 });/*
6495  * Based on:
6496  * Ext JS Library 1.1.1
6497  * Copyright(c) 2006-2007, Ext JS, LLC.
6498  *
6499  * Originally Released Under LGPL - original licence link has changed is not relivant.
6500  *
6501  * Fork - LGPL
6502  * <script type="text/javascript">
6503  */
6504
6505
6506 /**
6507  * @class Roo.data.Tree
6508  * @extends Roo.util.Observable
6509  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6510  * in the tree have most standard DOM functionality.
6511  * @constructor
6512  * @param {Node} root (optional) The root node
6513  */
6514 Roo.data.Tree = function(root){
6515    this.nodeHash = {};
6516    /**
6517     * The root node for this tree
6518     * @type Node
6519     */
6520    this.root = null;
6521    if(root){
6522        this.setRootNode(root);
6523    }
6524    this.addEvents({
6525        /**
6526         * @event append
6527         * Fires when a new child node is appended to a node in this tree.
6528         * @param {Tree} tree The owner tree
6529         * @param {Node} parent The parent node
6530         * @param {Node} node The newly appended node
6531         * @param {Number} index The index of the newly appended node
6532         */
6533        "append" : true,
6534        /**
6535         * @event remove
6536         * Fires when a child node is removed from a node in this tree.
6537         * @param {Tree} tree The owner tree
6538         * @param {Node} parent The parent node
6539         * @param {Node} node The child node removed
6540         */
6541        "remove" : true,
6542        /**
6543         * @event move
6544         * Fires when a node is moved to a new location in the tree
6545         * @param {Tree} tree The owner tree
6546         * @param {Node} node The node moved
6547         * @param {Node} oldParent The old parent of this node
6548         * @param {Node} newParent The new parent of this node
6549         * @param {Number} index The index it was moved to
6550         */
6551        "move" : true,
6552        /**
6553         * @event insert
6554         * Fires when a new child node is inserted in a node in this tree.
6555         * @param {Tree} tree The owner tree
6556         * @param {Node} parent The parent node
6557         * @param {Node} node The child node inserted
6558         * @param {Node} refNode The child node the node was inserted before
6559         */
6560        "insert" : true,
6561        /**
6562         * @event beforeappend
6563         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6564         * @param {Tree} tree The owner tree
6565         * @param {Node} parent The parent node
6566         * @param {Node} node The child node to be appended
6567         */
6568        "beforeappend" : true,
6569        /**
6570         * @event beforeremove
6571         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6572         * @param {Tree} tree The owner tree
6573         * @param {Node} parent The parent node
6574         * @param {Node} node The child node to be removed
6575         */
6576        "beforeremove" : true,
6577        /**
6578         * @event beforemove
6579         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6580         * @param {Tree} tree The owner tree
6581         * @param {Node} node The node being moved
6582         * @param {Node} oldParent The parent of the node
6583         * @param {Node} newParent The new parent the node is moving to
6584         * @param {Number} index The index it is being moved to
6585         */
6586        "beforemove" : true,
6587        /**
6588         * @event beforeinsert
6589         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6590         * @param {Tree} tree The owner tree
6591         * @param {Node} parent The parent node
6592         * @param {Node} node The child node to be inserted
6593         * @param {Node} refNode The child node the node is being inserted before
6594         */
6595        "beforeinsert" : true
6596    });
6597
6598     Roo.data.Tree.superclass.constructor.call(this);
6599 };
6600
6601 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6602     pathSeparator: "/",
6603
6604     proxyNodeEvent : function(){
6605         return this.fireEvent.apply(this, arguments);
6606     },
6607
6608     /**
6609      * Returns the root node for this tree.
6610      * @return {Node}
6611      */
6612     getRootNode : function(){
6613         return this.root;
6614     },
6615
6616     /**
6617      * Sets the root node for this tree.
6618      * @param {Node} node
6619      * @return {Node}
6620      */
6621     setRootNode : function(node){
6622         this.root = node;
6623         node.ownerTree = this;
6624         node.isRoot = true;
6625         this.registerNode(node);
6626         return node;
6627     },
6628
6629     /**
6630      * Gets a node in this tree by its id.
6631      * @param {String} id
6632      * @return {Node}
6633      */
6634     getNodeById : function(id){
6635         return this.nodeHash[id];
6636     },
6637
6638     registerNode : function(node){
6639         this.nodeHash[node.id] = node;
6640     },
6641
6642     unregisterNode : function(node){
6643         delete this.nodeHash[node.id];
6644     },
6645
6646     toString : function(){
6647         return "[Tree"+(this.id?" "+this.id:"")+"]";
6648     }
6649 });
6650
6651 /**
6652  * @class Roo.data.Node
6653  * @extends Roo.util.Observable
6654  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6655  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6656  * @constructor
6657  * @param {Object} attributes The attributes/config for the node
6658  */
6659 Roo.data.Node = function(attributes){
6660     /**
6661      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6662      * @type {Object}
6663      */
6664     this.attributes = attributes || {};
6665     this.leaf = this.attributes.leaf;
6666     /**
6667      * The node id. @type String
6668      */
6669     this.id = this.attributes.id;
6670     if(!this.id){
6671         this.id = Roo.id(null, "ynode-");
6672         this.attributes.id = this.id;
6673     }
6674      
6675     
6676     /**
6677      * All child nodes of this node. @type Array
6678      */
6679     this.childNodes = [];
6680     if(!this.childNodes.indexOf){ // indexOf is a must
6681         this.childNodes.indexOf = function(o){
6682             for(var i = 0, len = this.length; i < len; i++){
6683                 if(this[i] == o) {
6684                     return i;
6685                 }
6686             }
6687             return -1;
6688         };
6689     }
6690     /**
6691      * The parent node for this node. @type Node
6692      */
6693     this.parentNode = null;
6694     /**
6695      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6696      */
6697     this.firstChild = null;
6698     /**
6699      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6700      */
6701     this.lastChild = null;
6702     /**
6703      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6704      */
6705     this.previousSibling = null;
6706     /**
6707      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6708      */
6709     this.nextSibling = null;
6710
6711     this.addEvents({
6712        /**
6713         * @event append
6714         * Fires when a new child node is appended
6715         * @param {Tree} tree The owner tree
6716         * @param {Node} this This node
6717         * @param {Node} node The newly appended node
6718         * @param {Number} index The index of the newly appended node
6719         */
6720        "append" : true,
6721        /**
6722         * @event remove
6723         * Fires when a child node is removed
6724         * @param {Tree} tree The owner tree
6725         * @param {Node} this This node
6726         * @param {Node} node The removed node
6727         */
6728        "remove" : true,
6729        /**
6730         * @event move
6731         * Fires when this node is moved to a new location in the tree
6732         * @param {Tree} tree The owner tree
6733         * @param {Node} this This node
6734         * @param {Node} oldParent The old parent of this node
6735         * @param {Node} newParent The new parent of this node
6736         * @param {Number} index The index it was moved to
6737         */
6738        "move" : true,
6739        /**
6740         * @event insert
6741         * Fires when a new child node is inserted.
6742         * @param {Tree} tree The owner tree
6743         * @param {Node} this This node
6744         * @param {Node} node The child node inserted
6745         * @param {Node} refNode The child node the node was inserted before
6746         */
6747        "insert" : true,
6748        /**
6749         * @event beforeappend
6750         * Fires before a new child is appended, return false to cancel the append.
6751         * @param {Tree} tree The owner tree
6752         * @param {Node} this This node
6753         * @param {Node} node The child node to be appended
6754         */
6755        "beforeappend" : true,
6756        /**
6757         * @event beforeremove
6758         * Fires before a child is removed, return false to cancel the remove.
6759         * @param {Tree} tree The owner tree
6760         * @param {Node} this This node
6761         * @param {Node} node The child node to be removed
6762         */
6763        "beforeremove" : true,
6764        /**
6765         * @event beforemove
6766         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6767         * @param {Tree} tree The owner tree
6768         * @param {Node} this This node
6769         * @param {Node} oldParent The parent of this node
6770         * @param {Node} newParent The new parent this node is moving to
6771         * @param {Number} index The index it is being moved to
6772         */
6773        "beforemove" : true,
6774        /**
6775         * @event beforeinsert
6776         * Fires before a new child is inserted, return false to cancel the insert.
6777         * @param {Tree} tree The owner tree
6778         * @param {Node} this This node
6779         * @param {Node} node The child node to be inserted
6780         * @param {Node} refNode The child node the node is being inserted before
6781         */
6782        "beforeinsert" : true
6783    });
6784     this.listeners = this.attributes.listeners;
6785     Roo.data.Node.superclass.constructor.call(this);
6786 };
6787
6788 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6789     fireEvent : function(evtName){
6790         // first do standard event for this node
6791         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6792             return false;
6793         }
6794         // then bubble it up to the tree if the event wasn't cancelled
6795         var ot = this.getOwnerTree();
6796         if(ot){
6797             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6798                 return false;
6799             }
6800         }
6801         return true;
6802     },
6803
6804     /**
6805      * Returns true if this node is a leaf
6806      * @return {Boolean}
6807      */
6808     isLeaf : function(){
6809         return this.leaf === true;
6810     },
6811
6812     // private
6813     setFirstChild : function(node){
6814         this.firstChild = node;
6815     },
6816
6817     //private
6818     setLastChild : function(node){
6819         this.lastChild = node;
6820     },
6821
6822
6823     /**
6824      * Returns true if this node is the last child of its parent
6825      * @return {Boolean}
6826      */
6827     isLast : function(){
6828        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6829     },
6830
6831     /**
6832      * Returns true if this node is the first child of its parent
6833      * @return {Boolean}
6834      */
6835     isFirst : function(){
6836        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6837     },
6838
6839     hasChildNodes : function(){
6840         return !this.isLeaf() && this.childNodes.length > 0;
6841     },
6842
6843     /**
6844      * Insert node(s) as the last child node of this node.
6845      * @param {Node/Array} node The node or Array of nodes to append
6846      * @return {Node} The appended node if single append, or null if an array was passed
6847      */
6848     appendChild : function(node){
6849         var multi = false;
6850         if(node instanceof Array){
6851             multi = node;
6852         }else if(arguments.length > 1){
6853             multi = arguments;
6854         }
6855         // if passed an array or multiple args do them one by one
6856         if(multi){
6857             for(var i = 0, len = multi.length; i < len; i++) {
6858                 this.appendChild(multi[i]);
6859             }
6860         }else{
6861             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6862                 return false;
6863             }
6864             var index = this.childNodes.length;
6865             var oldParent = node.parentNode;
6866             // it's a move, make sure we move it cleanly
6867             if(oldParent){
6868                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6869                     return false;
6870                 }
6871                 oldParent.removeChild(node);
6872             }
6873             index = this.childNodes.length;
6874             if(index == 0){
6875                 this.setFirstChild(node);
6876             }
6877             this.childNodes.push(node);
6878             node.parentNode = this;
6879             var ps = this.childNodes[index-1];
6880             if(ps){
6881                 node.previousSibling = ps;
6882                 ps.nextSibling = node;
6883             }else{
6884                 node.previousSibling = null;
6885             }
6886             node.nextSibling = null;
6887             this.setLastChild(node);
6888             node.setOwnerTree(this.getOwnerTree());
6889             this.fireEvent("append", this.ownerTree, this, node, index);
6890             if(oldParent){
6891                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6892             }
6893             return node;
6894         }
6895     },
6896
6897     /**
6898      * Removes a child node from this node.
6899      * @param {Node} node The node to remove
6900      * @return {Node} The removed node
6901      */
6902     removeChild : function(node){
6903         var index = this.childNodes.indexOf(node);
6904         if(index == -1){
6905             return false;
6906         }
6907         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6908             return false;
6909         }
6910
6911         // remove it from childNodes collection
6912         this.childNodes.splice(index, 1);
6913
6914         // update siblings
6915         if(node.previousSibling){
6916             node.previousSibling.nextSibling = node.nextSibling;
6917         }
6918         if(node.nextSibling){
6919             node.nextSibling.previousSibling = node.previousSibling;
6920         }
6921
6922         // update child refs
6923         if(this.firstChild == node){
6924             this.setFirstChild(node.nextSibling);
6925         }
6926         if(this.lastChild == node){
6927             this.setLastChild(node.previousSibling);
6928         }
6929
6930         node.setOwnerTree(null);
6931         // clear any references from the node
6932         node.parentNode = null;
6933         node.previousSibling = null;
6934         node.nextSibling = null;
6935         this.fireEvent("remove", this.ownerTree, this, node);
6936         return node;
6937     },
6938
6939     /**
6940      * Inserts the first node before the second node in this nodes childNodes collection.
6941      * @param {Node} node The node to insert
6942      * @param {Node} refNode The node to insert before (if null the node is appended)
6943      * @return {Node} The inserted node
6944      */
6945     insertBefore : function(node, refNode){
6946         if(!refNode){ // like standard Dom, refNode can be null for append
6947             return this.appendChild(node);
6948         }
6949         // nothing to do
6950         if(node == refNode){
6951             return false;
6952         }
6953
6954         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6955             return false;
6956         }
6957         var index = this.childNodes.indexOf(refNode);
6958         var oldParent = node.parentNode;
6959         var refIndex = index;
6960
6961         // when moving internally, indexes will change after remove
6962         if(oldParent == this && this.childNodes.indexOf(node) < index){
6963             refIndex--;
6964         }
6965
6966         // it's a move, make sure we move it cleanly
6967         if(oldParent){
6968             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6969                 return false;
6970             }
6971             oldParent.removeChild(node);
6972         }
6973         if(refIndex == 0){
6974             this.setFirstChild(node);
6975         }
6976         this.childNodes.splice(refIndex, 0, node);
6977         node.parentNode = this;
6978         var ps = this.childNodes[refIndex-1];
6979         if(ps){
6980             node.previousSibling = ps;
6981             ps.nextSibling = node;
6982         }else{
6983             node.previousSibling = null;
6984         }
6985         node.nextSibling = refNode;
6986         refNode.previousSibling = node;
6987         node.setOwnerTree(this.getOwnerTree());
6988         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6989         if(oldParent){
6990             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6991         }
6992         return node;
6993     },
6994
6995     /**
6996      * Returns the child node at the specified index.
6997      * @param {Number} index
6998      * @return {Node}
6999      */
7000     item : function(index){
7001         return this.childNodes[index];
7002     },
7003
7004     /**
7005      * Replaces one child node in this node with another.
7006      * @param {Node} newChild The replacement node
7007      * @param {Node} oldChild The node to replace
7008      * @return {Node} The replaced node
7009      */
7010     replaceChild : function(newChild, oldChild){
7011         this.insertBefore(newChild, oldChild);
7012         this.removeChild(oldChild);
7013         return oldChild;
7014     },
7015
7016     /**
7017      * Returns the index of a child node
7018      * @param {Node} node
7019      * @return {Number} The index of the node or -1 if it was not found
7020      */
7021     indexOf : function(child){
7022         return this.childNodes.indexOf(child);
7023     },
7024
7025     /**
7026      * Returns the tree this node is in.
7027      * @return {Tree}
7028      */
7029     getOwnerTree : function(){
7030         // if it doesn't have one, look for one
7031         if(!this.ownerTree){
7032             var p = this;
7033             while(p){
7034                 if(p.ownerTree){
7035                     this.ownerTree = p.ownerTree;
7036                     break;
7037                 }
7038                 p = p.parentNode;
7039             }
7040         }
7041         return this.ownerTree;
7042     },
7043
7044     /**
7045      * Returns depth of this node (the root node has a depth of 0)
7046      * @return {Number}
7047      */
7048     getDepth : function(){
7049         var depth = 0;
7050         var p = this;
7051         while(p.parentNode){
7052             ++depth;
7053             p = p.parentNode;
7054         }
7055         return depth;
7056     },
7057
7058     // private
7059     setOwnerTree : function(tree){
7060         // if it's move, we need to update everyone
7061         if(tree != this.ownerTree){
7062             if(this.ownerTree){
7063                 this.ownerTree.unregisterNode(this);
7064             }
7065             this.ownerTree = tree;
7066             var cs = this.childNodes;
7067             for(var i = 0, len = cs.length; i < len; i++) {
7068                 cs[i].setOwnerTree(tree);
7069             }
7070             if(tree){
7071                 tree.registerNode(this);
7072             }
7073         }
7074     },
7075
7076     /**
7077      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7078      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7079      * @return {String} The path
7080      */
7081     getPath : function(attr){
7082         attr = attr || "id";
7083         var p = this.parentNode;
7084         var b = [this.attributes[attr]];
7085         while(p){
7086             b.unshift(p.attributes[attr]);
7087             p = p.parentNode;
7088         }
7089         var sep = this.getOwnerTree().pathSeparator;
7090         return sep + b.join(sep);
7091     },
7092
7093     /**
7094      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7095      * function call will be the scope provided or the current node. The arguments to the function
7096      * will be the args provided or the current node. If the function returns false at any point,
7097      * the bubble is stopped.
7098      * @param {Function} fn The function to call
7099      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7100      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7101      */
7102     bubble : function(fn, scope, args){
7103         var p = this;
7104         while(p){
7105             if(fn.call(scope || p, args || p) === false){
7106                 break;
7107             }
7108             p = p.parentNode;
7109         }
7110     },
7111
7112     /**
7113      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7114      * function call will be the scope provided or the current node. The arguments to the function
7115      * will be the args provided or the current node. If the function returns false at any point,
7116      * the cascade is stopped on that branch.
7117      * @param {Function} fn The function to call
7118      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7119      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7120      */
7121     cascade : function(fn, scope, args){
7122         if(fn.call(scope || this, args || this) !== false){
7123             var cs = this.childNodes;
7124             for(var i = 0, len = cs.length; i < len; i++) {
7125                 cs[i].cascade(fn, scope, args);
7126             }
7127         }
7128     },
7129
7130     /**
7131      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7132      * function call will be the scope provided or the current node. The arguments to the function
7133      * will be the args provided or the current node. If the function returns false at any point,
7134      * the iteration stops.
7135      * @param {Function} fn The function to call
7136      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7137      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7138      */
7139     eachChild : function(fn, scope, args){
7140         var cs = this.childNodes;
7141         for(var i = 0, len = cs.length; i < len; i++) {
7142                 if(fn.call(scope || this, args || cs[i]) === false){
7143                     break;
7144                 }
7145         }
7146     },
7147
7148     /**
7149      * Finds the first child that has the attribute with the specified value.
7150      * @param {String} attribute The attribute name
7151      * @param {Mixed} value The value to search for
7152      * @return {Node} The found child or null if none was found
7153      */
7154     findChild : function(attribute, value){
7155         var cs = this.childNodes;
7156         for(var i = 0, len = cs.length; i < len; i++) {
7157                 if(cs[i].attributes[attribute] == value){
7158                     return cs[i];
7159                 }
7160         }
7161         return null;
7162     },
7163
7164     /**
7165      * Finds the first child by a custom function. The child matches if the function passed
7166      * returns true.
7167      * @param {Function} fn
7168      * @param {Object} scope (optional)
7169      * @return {Node} The found child or null if none was found
7170      */
7171     findChildBy : function(fn, scope){
7172         var cs = this.childNodes;
7173         for(var i = 0, len = cs.length; i < len; i++) {
7174                 if(fn.call(scope||cs[i], cs[i]) === true){
7175                     return cs[i];
7176                 }
7177         }
7178         return null;
7179     },
7180
7181     /**
7182      * Sorts this nodes children using the supplied sort function
7183      * @param {Function} fn
7184      * @param {Object} scope (optional)
7185      */
7186     sort : function(fn, scope){
7187         var cs = this.childNodes;
7188         var len = cs.length;
7189         if(len > 0){
7190             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7191             cs.sort(sortFn);
7192             for(var i = 0; i < len; i++){
7193                 var n = cs[i];
7194                 n.previousSibling = cs[i-1];
7195                 n.nextSibling = cs[i+1];
7196                 if(i == 0){
7197                     this.setFirstChild(n);
7198                 }
7199                 if(i == len-1){
7200                     this.setLastChild(n);
7201                 }
7202             }
7203         }
7204     },
7205
7206     /**
7207      * Returns true if this node is an ancestor (at any point) of the passed node.
7208      * @param {Node} node
7209      * @return {Boolean}
7210      */
7211     contains : function(node){
7212         return node.isAncestor(this);
7213     },
7214
7215     /**
7216      * Returns true if the passed node is an ancestor (at any point) of this node.
7217      * @param {Node} node
7218      * @return {Boolean}
7219      */
7220     isAncestor : function(node){
7221         var p = this.parentNode;
7222         while(p){
7223             if(p == node){
7224                 return true;
7225             }
7226             p = p.parentNode;
7227         }
7228         return false;
7229     },
7230
7231     toString : function(){
7232         return "[Node"+(this.id?" "+this.id:"")+"]";
7233     }
7234 });/*
7235  * Based on:
7236  * Ext JS Library 1.1.1
7237  * Copyright(c) 2006-2007, Ext JS, LLC.
7238  *
7239  * Originally Released Under LGPL - original licence link has changed is not relivant.
7240  *
7241  * Fork - LGPL
7242  * <script type="text/javascript">
7243  */
7244  
7245
7246 /**
7247  * @class Roo.ComponentMgr
7248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7249  * @singleton
7250  */
7251 Roo.ComponentMgr = function(){
7252     var all = new Roo.util.MixedCollection();
7253
7254     return {
7255         /**
7256          * Registers a component.
7257          * @param {Roo.Component} c The component
7258          */
7259         register : function(c){
7260             all.add(c);
7261         },
7262
7263         /**
7264          * Unregisters a component.
7265          * @param {Roo.Component} c The component
7266          */
7267         unregister : function(c){
7268             all.remove(c);
7269         },
7270
7271         /**
7272          * Returns a component by id
7273          * @param {String} id The component id
7274          */
7275         get : function(id){
7276             return all.get(id);
7277         },
7278
7279         /**
7280          * Registers a function that will be called when a specified component is added to ComponentMgr
7281          * @param {String} id The component id
7282          * @param {Funtction} fn The callback function
7283          * @param {Object} scope The scope of the callback
7284          */
7285         onAvailable : function(id, fn, scope){
7286             all.on("add", function(index, o){
7287                 if(o.id == id){
7288                     fn.call(scope || o, o);
7289                     all.un("add", fn, scope);
7290                 }
7291             });
7292         }
7293     };
7294 }();/*
7295  * Based on:
7296  * Ext JS Library 1.1.1
7297  * Copyright(c) 2006-2007, Ext JS, LLC.
7298  *
7299  * Originally Released Under LGPL - original licence link has changed is not relivant.
7300  *
7301  * Fork - LGPL
7302  * <script type="text/javascript">
7303  */
7304  
7305 /**
7306  * @class Roo.Component
7307  * @extends Roo.util.Observable
7308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7312  * All visual components (widgets) that require rendering into a layout should subclass Component.
7313  * @constructor
7314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7315  * 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
7316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7317  */
7318 Roo.Component = function(config){
7319     config = config || {};
7320     if(config.tagName || config.dom || typeof config == "string"){ // element object
7321         config = {el: config, id: config.id || config};
7322     }
7323     this.initialConfig = config;
7324
7325     Roo.apply(this, config);
7326     this.addEvents({
7327         /**
7328          * @event disable
7329          * Fires after the component is disabled.
7330              * @param {Roo.Component} this
7331              */
7332         disable : true,
7333         /**
7334          * @event enable
7335          * Fires after the component is enabled.
7336              * @param {Roo.Component} this
7337              */
7338         enable : true,
7339         /**
7340          * @event beforeshow
7341          * Fires before the component is shown.  Return false to stop the show.
7342              * @param {Roo.Component} this
7343              */
7344         beforeshow : true,
7345         /**
7346          * @event show
7347          * Fires after the component is shown.
7348              * @param {Roo.Component} this
7349              */
7350         show : true,
7351         /**
7352          * @event beforehide
7353          * Fires before the component is hidden. Return false to stop the hide.
7354              * @param {Roo.Component} this
7355              */
7356         beforehide : true,
7357         /**
7358          * @event hide
7359          * Fires after the component is hidden.
7360              * @param {Roo.Component} this
7361              */
7362         hide : true,
7363         /**
7364          * @event beforerender
7365          * Fires before the component is rendered. Return false to stop the render.
7366              * @param {Roo.Component} this
7367              */
7368         beforerender : true,
7369         /**
7370          * @event render
7371          * Fires after the component is rendered.
7372              * @param {Roo.Component} this
7373              */
7374         render : true,
7375         /**
7376          * @event beforedestroy
7377          * Fires before the component is destroyed. Return false to stop the destroy.
7378              * @param {Roo.Component} this
7379              */
7380         beforedestroy : true,
7381         /**
7382          * @event destroy
7383          * Fires after the component is destroyed.
7384              * @param {Roo.Component} this
7385              */
7386         destroy : true
7387     });
7388     if(!this.id){
7389         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7390     }
7391     Roo.ComponentMgr.register(this);
7392     Roo.Component.superclass.constructor.call(this);
7393     this.initComponent();
7394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7395         this.render(this.renderTo);
7396         delete this.renderTo;
7397     }
7398 };
7399
7400 /** @private */
7401 Roo.Component.AUTO_ID = 1000;
7402
7403 Roo.extend(Roo.Component, Roo.util.Observable, {
7404     /**
7405      * @scope Roo.Component.prototype
7406      * @type {Boolean}
7407      * true if this component is hidden. Read-only.
7408      */
7409     hidden : false,
7410     /**
7411      * @type {Boolean}
7412      * true if this component is disabled. Read-only.
7413      */
7414     disabled : false,
7415     /**
7416      * @type {Boolean}
7417      * true if this component has been rendered. Read-only.
7418      */
7419     rendered : false,
7420     
7421     /** @cfg {String} disableClass
7422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7423      */
7424     disabledClass : "x-item-disabled",
7425         /** @cfg {Boolean} allowDomMove
7426          * Whether the component can move the Dom node when rendering (defaults to true).
7427          */
7428     allowDomMove : true,
7429     /** @cfg {String} hideMode
7430      * How this component should hidden. Supported values are
7431      * "visibility" (css visibility), "offsets" (negative offset position) and
7432      * "display" (css display) - defaults to "display".
7433      */
7434     hideMode: 'display',
7435
7436     /** @private */
7437     ctype : "Roo.Component",
7438
7439     /**
7440      * @cfg {String} actionMode 
7441      * which property holds the element that used for  hide() / show() / disable() / enable()
7442      * default is 'el' 
7443      */
7444     actionMode : "el",
7445
7446     /** @private */
7447     getActionEl : function(){
7448         return this[this.actionMode];
7449     },
7450
7451     initComponent : Roo.emptyFn,
7452     /**
7453      * If this is a lazy rendering component, render it to its container element.
7454      * @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.
7455      */
7456     render : function(container, position){
7457         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7458             if(!container && this.el){
7459                 this.el = Roo.get(this.el);
7460                 container = this.el.dom.parentNode;
7461                 this.allowDomMove = false;
7462             }
7463             this.container = Roo.get(container);
7464             this.rendered = true;
7465             if(position !== undefined){
7466                 if(typeof position == 'number'){
7467                     position = this.container.dom.childNodes[position];
7468                 }else{
7469                     position = Roo.getDom(position);
7470                 }
7471             }
7472             this.onRender(this.container, position || null);
7473             if(this.cls){
7474                 this.el.addClass(this.cls);
7475                 delete this.cls;
7476             }
7477             if(this.style){
7478                 this.el.applyStyles(this.style);
7479                 delete this.style;
7480             }
7481             this.fireEvent("render", this);
7482             this.afterRender(this.container);
7483             if(this.hidden){
7484                 this.hide();
7485             }
7486             if(this.disabled){
7487                 this.disable();
7488             }
7489         }
7490         return this;
7491     },
7492
7493     /** @private */
7494     // default function is not really useful
7495     onRender : function(ct, position){
7496         if(this.el){
7497             this.el = Roo.get(this.el);
7498             if(this.allowDomMove !== false){
7499                 ct.dom.insertBefore(this.el.dom, position);
7500             }
7501         }
7502     },
7503
7504     /** @private */
7505     getAutoCreate : function(){
7506         var cfg = typeof this.autoCreate == "object" ?
7507                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7508         if(this.id && !cfg.id){
7509             cfg.id = this.id;
7510         }
7511         return cfg;
7512     },
7513
7514     /** @private */
7515     afterRender : Roo.emptyFn,
7516
7517     /**
7518      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7519      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7520      */
7521     destroy : function(){
7522         if(this.fireEvent("beforedestroy", this) !== false){
7523             this.purgeListeners();
7524             this.beforeDestroy();
7525             if(this.rendered){
7526                 this.el.removeAllListeners();
7527                 this.el.remove();
7528                 if(this.actionMode == "container"){
7529                     this.container.remove();
7530                 }
7531             }
7532             this.onDestroy();
7533             Roo.ComponentMgr.unregister(this);
7534             this.fireEvent("destroy", this);
7535         }
7536     },
7537
7538         /** @private */
7539     beforeDestroy : function(){
7540
7541     },
7542
7543         /** @private */
7544         onDestroy : function(){
7545
7546     },
7547
7548     /**
7549      * Returns the underlying {@link Roo.Element}.
7550      * @return {Roo.Element} The element
7551      */
7552     getEl : function(){
7553         return this.el;
7554     },
7555
7556     /**
7557      * Returns the id of this component.
7558      * @return {String}
7559      */
7560     getId : function(){
7561         return this.id;
7562     },
7563
7564     /**
7565      * Try to focus this component.
7566      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7567      * @return {Roo.Component} this
7568      */
7569     focus : function(selectText){
7570         if(this.rendered){
7571             this.el.focus();
7572             if(selectText === true){
7573                 this.el.dom.select();
7574             }
7575         }
7576         return this;
7577     },
7578
7579     /** @private */
7580     blur : function(){
7581         if(this.rendered){
7582             this.el.blur();
7583         }
7584         return this;
7585     },
7586
7587     /**
7588      * Disable this component.
7589      * @return {Roo.Component} this
7590      */
7591     disable : function(){
7592         if(this.rendered){
7593             this.onDisable();
7594         }
7595         this.disabled = true;
7596         this.fireEvent("disable", this);
7597         return this;
7598     },
7599
7600         // private
7601     onDisable : function(){
7602         this.getActionEl().addClass(this.disabledClass);
7603         this.el.dom.disabled = true;
7604     },
7605
7606     /**
7607      * Enable this component.
7608      * @return {Roo.Component} this
7609      */
7610     enable : function(){
7611         if(this.rendered){
7612             this.onEnable();
7613         }
7614         this.disabled = false;
7615         this.fireEvent("enable", this);
7616         return this;
7617     },
7618
7619         // private
7620     onEnable : function(){
7621         this.getActionEl().removeClass(this.disabledClass);
7622         this.el.dom.disabled = false;
7623     },
7624
7625     /**
7626      * Convenience function for setting disabled/enabled by boolean.
7627      * @param {Boolean} disabled
7628      */
7629     setDisabled : function(disabled){
7630         this[disabled ? "disable" : "enable"]();
7631     },
7632
7633     /**
7634      * Show this component.
7635      * @return {Roo.Component} this
7636      */
7637     show: function(){
7638         if(this.fireEvent("beforeshow", this) !== false){
7639             this.hidden = false;
7640             if(this.rendered){
7641                 this.onShow();
7642             }
7643             this.fireEvent("show", this);
7644         }
7645         return this;
7646     },
7647
7648     // private
7649     onShow : function(){
7650         var ae = this.getActionEl();
7651         if(this.hideMode == 'visibility'){
7652             ae.dom.style.visibility = "visible";
7653         }else if(this.hideMode == 'offsets'){
7654             ae.removeClass('x-hidden');
7655         }else{
7656             ae.dom.style.display = "";
7657         }
7658     },
7659
7660     /**
7661      * Hide this component.
7662      * @return {Roo.Component} this
7663      */
7664     hide: function(){
7665         if(this.fireEvent("beforehide", this) !== false){
7666             this.hidden = true;
7667             if(this.rendered){
7668                 this.onHide();
7669             }
7670             this.fireEvent("hide", this);
7671         }
7672         return this;
7673     },
7674
7675     // private
7676     onHide : function(){
7677         var ae = this.getActionEl();
7678         if(this.hideMode == 'visibility'){
7679             ae.dom.style.visibility = "hidden";
7680         }else if(this.hideMode == 'offsets'){
7681             ae.addClass('x-hidden');
7682         }else{
7683             ae.dom.style.display = "none";
7684         }
7685     },
7686
7687     /**
7688      * Convenience function to hide or show this component by boolean.
7689      * @param {Boolean} visible True to show, false to hide
7690      * @return {Roo.Component} this
7691      */
7692     setVisible: function(visible){
7693         if(visible) {
7694             this.show();
7695         }else{
7696             this.hide();
7697         }
7698         return this;
7699     },
7700
7701     /**
7702      * Returns true if this component is visible.
7703      */
7704     isVisible : function(){
7705         return this.getActionEl().isVisible();
7706     },
7707
7708     cloneConfig : function(overrides){
7709         overrides = overrides || {};
7710         var id = overrides.id || Roo.id();
7711         var cfg = Roo.applyIf(overrides, this.initialConfig);
7712         cfg.id = id; // prevent dup id
7713         return new this.constructor(cfg);
7714     }
7715 });/*
7716  * Based on:
7717  * Ext JS Library 1.1.1
7718  * Copyright(c) 2006-2007, Ext JS, LLC.
7719  *
7720  * Originally Released Under LGPL - original licence link has changed is not relivant.
7721  *
7722  * Fork - LGPL
7723  * <script type="text/javascript">
7724  */
7725  (function(){ 
7726 /**
7727  * @class Roo.Layer
7728  * @extends Roo.Element
7729  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7730  * automatic maintaining of shadow/shim positions.
7731  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7732  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7733  * you can pass a string with a CSS class name. False turns off the shadow.
7734  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7735  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7736  * @cfg {String} cls CSS class to add to the element
7737  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7738  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7739  * @constructor
7740  * @param {Object} config An object with config options.
7741  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7742  */
7743
7744 Roo.Layer = function(config, existingEl){
7745     config = config || {};
7746     var dh = Roo.DomHelper;
7747     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7748     if(existingEl){
7749         this.dom = Roo.getDom(existingEl);
7750     }
7751     if(!this.dom){
7752         var o = config.dh || {tag: "div", cls: "x-layer"};
7753         this.dom = dh.append(pel, o);
7754     }
7755     if(config.cls){
7756         this.addClass(config.cls);
7757     }
7758     this.constrain = config.constrain !== false;
7759     this.visibilityMode = Roo.Element.VISIBILITY;
7760     if(config.id){
7761         this.id = this.dom.id = config.id;
7762     }else{
7763         this.id = Roo.id(this.dom);
7764     }
7765     this.zindex = config.zindex || this.getZIndex();
7766     this.position("absolute", this.zindex);
7767     if(config.shadow){
7768         this.shadowOffset = config.shadowOffset || 4;
7769         this.shadow = new Roo.Shadow({
7770             offset : this.shadowOffset,
7771             mode : config.shadow
7772         });
7773     }else{
7774         this.shadowOffset = 0;
7775     }
7776     this.useShim = config.shim !== false && Roo.useShims;
7777     this.useDisplay = config.useDisplay;
7778     this.hide();
7779 };
7780
7781 var supr = Roo.Element.prototype;
7782
7783 // shims are shared among layer to keep from having 100 iframes
7784 var shims = [];
7785
7786 Roo.extend(Roo.Layer, Roo.Element, {
7787
7788     getZIndex : function(){
7789         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7790     },
7791
7792     getShim : function(){
7793         if(!this.useShim){
7794             return null;
7795         }
7796         if(this.shim){
7797             return this.shim;
7798         }
7799         var shim = shims.shift();
7800         if(!shim){
7801             shim = this.createShim();
7802             shim.enableDisplayMode('block');
7803             shim.dom.style.display = 'none';
7804             shim.dom.style.visibility = 'visible';
7805         }
7806         var pn = this.dom.parentNode;
7807         if(shim.dom.parentNode != pn){
7808             pn.insertBefore(shim.dom, this.dom);
7809         }
7810         shim.setStyle('z-index', this.getZIndex()-2);
7811         this.shim = shim;
7812         return shim;
7813     },
7814
7815     hideShim : function(){
7816         if(this.shim){
7817             this.shim.setDisplayed(false);
7818             shims.push(this.shim);
7819             delete this.shim;
7820         }
7821     },
7822
7823     disableShadow : function(){
7824         if(this.shadow){
7825             this.shadowDisabled = true;
7826             this.shadow.hide();
7827             this.lastShadowOffset = this.shadowOffset;
7828             this.shadowOffset = 0;
7829         }
7830     },
7831
7832     enableShadow : function(show){
7833         if(this.shadow){
7834             this.shadowDisabled = false;
7835             this.shadowOffset = this.lastShadowOffset;
7836             delete this.lastShadowOffset;
7837             if(show){
7838                 this.sync(true);
7839             }
7840         }
7841     },
7842
7843     // private
7844     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7845     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7846     sync : function(doShow){
7847         var sw = this.shadow;
7848         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7849             var sh = this.getShim();
7850
7851             var w = this.getWidth(),
7852                 h = this.getHeight();
7853
7854             var l = this.getLeft(true),
7855                 t = this.getTop(true);
7856
7857             if(sw && !this.shadowDisabled){
7858                 if(doShow && !sw.isVisible()){
7859                     sw.show(this);
7860                 }else{
7861                     sw.realign(l, t, w, h);
7862                 }
7863                 if(sh){
7864                     if(doShow){
7865                        sh.show();
7866                     }
7867                     // fit the shim behind the shadow, so it is shimmed too
7868                     var a = sw.adjusts, s = sh.dom.style;
7869                     s.left = (Math.min(l, l+a.l))+"px";
7870                     s.top = (Math.min(t, t+a.t))+"px";
7871                     s.width = (w+a.w)+"px";
7872                     s.height = (h+a.h)+"px";
7873                 }
7874             }else if(sh){
7875                 if(doShow){
7876                    sh.show();
7877                 }
7878                 sh.setSize(w, h);
7879                 sh.setLeftTop(l, t);
7880             }
7881             
7882         }
7883     },
7884
7885     // private
7886     destroy : function(){
7887         this.hideShim();
7888         if(this.shadow){
7889             this.shadow.hide();
7890         }
7891         this.removeAllListeners();
7892         var pn = this.dom.parentNode;
7893         if(pn){
7894             pn.removeChild(this.dom);
7895         }
7896         Roo.Element.uncache(this.id);
7897     },
7898
7899     remove : function(){
7900         this.destroy();
7901     },
7902
7903     // private
7904     beginUpdate : function(){
7905         this.updating = true;
7906     },
7907
7908     // private
7909     endUpdate : function(){
7910         this.updating = false;
7911         this.sync(true);
7912     },
7913
7914     // private
7915     hideUnders : function(negOffset){
7916         if(this.shadow){
7917             this.shadow.hide();
7918         }
7919         this.hideShim();
7920     },
7921
7922     // private
7923     constrainXY : function(){
7924         if(this.constrain){
7925             var vw = Roo.lib.Dom.getViewWidth(),
7926                 vh = Roo.lib.Dom.getViewHeight();
7927             var s = Roo.get(document).getScroll();
7928
7929             var xy = this.getXY();
7930             var x = xy[0], y = xy[1];   
7931             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7932             // only move it if it needs it
7933             var moved = false;
7934             // first validate right/bottom
7935             if((x + w) > vw+s.left){
7936                 x = vw - w - this.shadowOffset;
7937                 moved = true;
7938             }
7939             if((y + h) > vh+s.top){
7940                 y = vh - h - this.shadowOffset;
7941                 moved = true;
7942             }
7943             // then make sure top/left isn't negative
7944             if(x < s.left){
7945                 x = s.left;
7946                 moved = true;
7947             }
7948             if(y < s.top){
7949                 y = s.top;
7950                 moved = true;
7951             }
7952             if(moved){
7953                 if(this.avoidY){
7954                     var ay = this.avoidY;
7955                     if(y <= ay && (y+h) >= ay){
7956                         y = ay-h-5;   
7957                     }
7958                 }
7959                 xy = [x, y];
7960                 this.storeXY(xy);
7961                 supr.setXY.call(this, xy);
7962                 this.sync();
7963             }
7964         }
7965     },
7966
7967     isVisible : function(){
7968         return this.visible;    
7969     },
7970
7971     // private
7972     showAction : function(){
7973         this.visible = true; // track visibility to prevent getStyle calls
7974         if(this.useDisplay === true){
7975             this.setDisplayed("");
7976         }else if(this.lastXY){
7977             supr.setXY.call(this, this.lastXY);
7978         }else if(this.lastLT){
7979             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7980         }
7981     },
7982
7983     // private
7984     hideAction : function(){
7985         this.visible = false;
7986         if(this.useDisplay === true){
7987             this.setDisplayed(false);
7988         }else{
7989             this.setLeftTop(-10000,-10000);
7990         }
7991     },
7992
7993     // overridden Element method
7994     setVisible : function(v, a, d, c, e){
7995         if(v){
7996             this.showAction();
7997         }
7998         if(a && v){
7999             var cb = function(){
8000                 this.sync(true);
8001                 if(c){
8002                     c();
8003                 }
8004             }.createDelegate(this);
8005             supr.setVisible.call(this, true, true, d, cb, e);
8006         }else{
8007             if(!v){
8008                 this.hideUnders(true);
8009             }
8010             var cb = c;
8011             if(a){
8012                 cb = function(){
8013                     this.hideAction();
8014                     if(c){
8015                         c();
8016                     }
8017                 }.createDelegate(this);
8018             }
8019             supr.setVisible.call(this, v, a, d, cb, e);
8020             if(v){
8021                 this.sync(true);
8022             }else if(!a){
8023                 this.hideAction();
8024             }
8025         }
8026     },
8027
8028     storeXY : function(xy){
8029         delete this.lastLT;
8030         this.lastXY = xy;
8031     },
8032
8033     storeLeftTop : function(left, top){
8034         delete this.lastXY;
8035         this.lastLT = [left, top];
8036     },
8037
8038     // private
8039     beforeFx : function(){
8040         this.beforeAction();
8041         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8042     },
8043
8044     // private
8045     afterFx : function(){
8046         Roo.Layer.superclass.afterFx.apply(this, arguments);
8047         this.sync(this.isVisible());
8048     },
8049
8050     // private
8051     beforeAction : function(){
8052         if(!this.updating && this.shadow){
8053             this.shadow.hide();
8054         }
8055     },
8056
8057     // overridden Element method
8058     setLeft : function(left){
8059         this.storeLeftTop(left, this.getTop(true));
8060         supr.setLeft.apply(this, arguments);
8061         this.sync();
8062     },
8063
8064     setTop : function(top){
8065         this.storeLeftTop(this.getLeft(true), top);
8066         supr.setTop.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setLeftTop : function(left, top){
8071         this.storeLeftTop(left, top);
8072         supr.setLeftTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setXY : function(xy, a, d, c, e){
8077         this.fixDisplay();
8078         this.beforeAction();
8079         this.storeXY(xy);
8080         var cb = this.createCB(c);
8081         supr.setXY.call(this, xy, a, d, cb, e);
8082         if(!a){
8083             cb();
8084         }
8085     },
8086
8087     // private
8088     createCB : function(c){
8089         var el = this;
8090         return function(){
8091             el.constrainXY();
8092             el.sync(true);
8093             if(c){
8094                 c();
8095             }
8096         };
8097     },
8098
8099     // overridden Element method
8100     setX : function(x, a, d, c, e){
8101         this.setXY([x, this.getY()], a, d, c, e);
8102     },
8103
8104     // overridden Element method
8105     setY : function(y, a, d, c, e){
8106         this.setXY([this.getX(), y], a, d, c, e);
8107     },
8108
8109     // overridden Element method
8110     setSize : function(w, h, a, d, c, e){
8111         this.beforeAction();
8112         var cb = this.createCB(c);
8113         supr.setSize.call(this, w, h, a, d, cb, e);
8114         if(!a){
8115             cb();
8116         }
8117     },
8118
8119     // overridden Element method
8120     setWidth : function(w, a, d, c, e){
8121         this.beforeAction();
8122         var cb = this.createCB(c);
8123         supr.setWidth.call(this, w, a, d, cb, e);
8124         if(!a){
8125             cb();
8126         }
8127     },
8128
8129     // overridden Element method
8130     setHeight : function(h, a, d, c, e){
8131         this.beforeAction();
8132         var cb = this.createCB(c);
8133         supr.setHeight.call(this, h, a, d, cb, e);
8134         if(!a){
8135             cb();
8136         }
8137     },
8138
8139     // overridden Element method
8140     setBounds : function(x, y, w, h, a, d, c, e){
8141         this.beforeAction();
8142         var cb = this.createCB(c);
8143         if(!a){
8144             this.storeXY([x, y]);
8145             supr.setXY.call(this, [x, y]);
8146             supr.setSize.call(this, w, h, a, d, cb, e);
8147             cb();
8148         }else{
8149             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8150         }
8151         return this;
8152     },
8153     
8154     /**
8155      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8156      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8157      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8158      * @param {Number} zindex The new z-index to set
8159      * @return {this} The Layer
8160      */
8161     setZIndex : function(zindex){
8162         this.zindex = zindex;
8163         this.setStyle("z-index", zindex + 2);
8164         if(this.shadow){
8165             this.shadow.setZIndex(zindex + 1);
8166         }
8167         if(this.shim){
8168             this.shim.setStyle("z-index", zindex);
8169         }
8170     }
8171 });
8172 })();/*
8173  * Based on:
8174  * Ext JS Library 1.1.1
8175  * Copyright(c) 2006-2007, Ext JS, LLC.
8176  *
8177  * Originally Released Under LGPL - original licence link has changed is not relivant.
8178  *
8179  * Fork - LGPL
8180  * <script type="text/javascript">
8181  */
8182
8183
8184 /**
8185  * @class Roo.Shadow
8186  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8187  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8188  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8189  * @constructor
8190  * Create a new Shadow
8191  * @param {Object} config The config object
8192  */
8193 Roo.Shadow = function(config){
8194     Roo.apply(this, config);
8195     if(typeof this.mode != "string"){
8196         this.mode = this.defaultMode;
8197     }
8198     var o = this.offset, a = {h: 0};
8199     var rad = Math.floor(this.offset/2);
8200     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8201         case "drop":
8202             a.w = 0;
8203             a.l = a.t = o;
8204             a.t -= 1;
8205             if(Roo.isIE){
8206                 a.l -= this.offset + rad;
8207                 a.t -= this.offset + rad;
8208                 a.w -= rad;
8209                 a.h -= rad;
8210                 a.t += 1;
8211             }
8212         break;
8213         case "sides":
8214             a.w = (o*2);
8215             a.l = -o;
8216             a.t = o-1;
8217             if(Roo.isIE){
8218                 a.l -= (this.offset - rad);
8219                 a.t -= this.offset + rad;
8220                 a.l += 1;
8221                 a.w -= (this.offset - rad)*2;
8222                 a.w -= rad + 1;
8223                 a.h -= 1;
8224             }
8225         break;
8226         case "frame":
8227             a.w = a.h = (o*2);
8228             a.l = a.t = -o;
8229             a.t += 1;
8230             a.h -= 2;
8231             if(Roo.isIE){
8232                 a.l -= (this.offset - rad);
8233                 a.t -= (this.offset - rad);
8234                 a.l += 1;
8235                 a.w -= (this.offset + rad + 1);
8236                 a.h -= (this.offset + rad);
8237                 a.h += 1;
8238             }
8239         break;
8240     };
8241
8242     this.adjusts = a;
8243 };
8244
8245 Roo.Shadow.prototype = {
8246     /**
8247      * @cfg {String} mode
8248      * The shadow display mode.  Supports the following options:<br />
8249      * sides: Shadow displays on both sides and bottom only<br />
8250      * frame: Shadow displays equally on all four sides<br />
8251      * drop: Traditional bottom-right drop shadow (default)
8252      */
8253     /**
8254      * @cfg {String} offset
8255      * The number of pixels to offset the shadow from the element (defaults to 4)
8256      */
8257     offset: 4,
8258
8259     // private
8260     defaultMode: "drop",
8261
8262     /**
8263      * Displays the shadow under the target element
8264      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8265      */
8266     show : function(target){
8267         target = Roo.get(target);
8268         if(!this.el){
8269             this.el = Roo.Shadow.Pool.pull();
8270             if(this.el.dom.nextSibling != target.dom){
8271                 this.el.insertBefore(target);
8272             }
8273         }
8274         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8275         if(Roo.isIE){
8276             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8277         }
8278         this.realign(
8279             target.getLeft(true),
8280             target.getTop(true),
8281             target.getWidth(),
8282             target.getHeight()
8283         );
8284         this.el.dom.style.display = "block";
8285     },
8286
8287     /**
8288      * Returns true if the shadow is visible, else false
8289      */
8290     isVisible : function(){
8291         return this.el ? true : false;  
8292     },
8293
8294     /**
8295      * Direct alignment when values are already available. Show must be called at least once before
8296      * calling this method to ensure it is initialized.
8297      * @param {Number} left The target element left position
8298      * @param {Number} top The target element top position
8299      * @param {Number} width The target element width
8300      * @param {Number} height The target element height
8301      */
8302     realign : function(l, t, w, h){
8303         if(!this.el){
8304             return;
8305         }
8306         var a = this.adjusts, d = this.el.dom, s = d.style;
8307         var iea = 0;
8308         s.left = (l+a.l)+"px";
8309         s.top = (t+a.t)+"px";
8310         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8311  
8312         if(s.width != sws || s.height != shs){
8313             s.width = sws;
8314             s.height = shs;
8315             if(!Roo.isIE){
8316                 var cn = d.childNodes;
8317                 var sww = Math.max(0, (sw-12))+"px";
8318                 cn[0].childNodes[1].style.width = sww;
8319                 cn[1].childNodes[1].style.width = sww;
8320                 cn[2].childNodes[1].style.width = sww;
8321                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8322             }
8323         }
8324     },
8325
8326     /**
8327      * Hides this shadow
8328      */
8329     hide : function(){
8330         if(this.el){
8331             this.el.dom.style.display = "none";
8332             Roo.Shadow.Pool.push(this.el);
8333             delete this.el;
8334         }
8335     },
8336
8337     /**
8338      * Adjust the z-index of this shadow
8339      * @param {Number} zindex The new z-index
8340      */
8341     setZIndex : function(z){
8342         this.zIndex = z;
8343         if(this.el){
8344             this.el.setStyle("z-index", z);
8345         }
8346     }
8347 };
8348
8349 // Private utility class that manages the internal Shadow cache
8350 Roo.Shadow.Pool = function(){
8351     var p = [];
8352     var markup = Roo.isIE ?
8353                  '<div class="x-ie-shadow"></div>' :
8354                  '<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>';
8355     return {
8356         pull : function(){
8357             var sh = p.shift();
8358             if(!sh){
8359                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8360                 sh.autoBoxAdjust = false;
8361             }
8362             return sh;
8363         },
8364
8365         push : function(sh){
8366             p.push(sh);
8367         }
8368     };
8369 }();/*
8370  * Based on:
8371  * Ext JS Library 1.1.1
8372  * Copyright(c) 2006-2007, Ext JS, LLC.
8373  *
8374  * Originally Released Under LGPL - original licence link has changed is not relivant.
8375  *
8376  * Fork - LGPL
8377  * <script type="text/javascript">
8378  */
8379
8380 /**
8381  * @class Roo.BoxComponent
8382  * @extends Roo.Component
8383  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8384  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8385  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8386  * layout containers.
8387  * @constructor
8388  * @param {Roo.Element/String/Object} config The configuration options.
8389  */
8390 Roo.BoxComponent = function(config){
8391     Roo.Component.call(this, config);
8392     this.addEvents({
8393         /**
8394          * @event resize
8395          * Fires after the component is resized.
8396              * @param {Roo.Component} this
8397              * @param {Number} adjWidth The box-adjusted width that was set
8398              * @param {Number} adjHeight The box-adjusted height that was set
8399              * @param {Number} rawWidth The width that was originally specified
8400              * @param {Number} rawHeight The height that was originally specified
8401              */
8402         resize : true,
8403         /**
8404          * @event move
8405          * Fires after the component is moved.
8406              * @param {Roo.Component} this
8407              * @param {Number} x The new x position
8408              * @param {Number} y The new y position
8409              */
8410         move : true
8411     });
8412 };
8413
8414 Roo.extend(Roo.BoxComponent, Roo.Component, {
8415     // private, set in afterRender to signify that the component has been rendered
8416     boxReady : false,
8417     // private, used to defer height settings to subclasses
8418     deferHeight: false,
8419     /** @cfg {Number} width
8420      * width (optional) size of component
8421      */
8422      /** @cfg {Number} height
8423      * height (optional) size of component
8424      */
8425      
8426     /**
8427      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8428      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8429      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8430      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8431      * @return {Roo.BoxComponent} this
8432      */
8433     setSize : function(w, h){
8434         // support for standard size objects
8435         if(typeof w == 'object'){
8436             h = w.height;
8437             w = w.width;
8438         }
8439         // not rendered
8440         if(!this.boxReady){
8441             this.width = w;
8442             this.height = h;
8443             return this;
8444         }
8445
8446         // prevent recalcs when not needed
8447         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8448             return this;
8449         }
8450         this.lastSize = {width: w, height: h};
8451
8452         var adj = this.adjustSize(w, h);
8453         var aw = adj.width, ah = adj.height;
8454         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8455             var rz = this.getResizeEl();
8456             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8457                 rz.setSize(aw, ah);
8458             }else if(!this.deferHeight && ah !== undefined){
8459                 rz.setHeight(ah);
8460             }else if(aw !== undefined){
8461                 rz.setWidth(aw);
8462             }
8463             this.onResize(aw, ah, w, h);
8464             this.fireEvent('resize', this, aw, ah, w, h);
8465         }
8466         return this;
8467     },
8468
8469     /**
8470      * Gets the current size of the component's underlying element.
8471      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8472      */
8473     getSize : function(){
8474         return this.el.getSize();
8475     },
8476
8477     /**
8478      * Gets the current XY position of the component's underlying element.
8479      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8480      * @return {Array} The XY position of the element (e.g., [100, 200])
8481      */
8482     getPosition : function(local){
8483         if(local === true){
8484             return [this.el.getLeft(true), this.el.getTop(true)];
8485         }
8486         return this.xy || this.el.getXY();
8487     },
8488
8489     /**
8490      * Gets the current box measurements of the component's underlying element.
8491      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8492      * @returns {Object} box An object in the format {x, y, width, height}
8493      */
8494     getBox : function(local){
8495         var s = this.el.getSize();
8496         if(local){
8497             s.x = this.el.getLeft(true);
8498             s.y = this.el.getTop(true);
8499         }else{
8500             var xy = this.xy || this.el.getXY();
8501             s.x = xy[0];
8502             s.y = xy[1];
8503         }
8504         return s;
8505     },
8506
8507     /**
8508      * Sets the current box measurements of the component's underlying element.
8509      * @param {Object} box An object in the format {x, y, width, height}
8510      * @returns {Roo.BoxComponent} this
8511      */
8512     updateBox : function(box){
8513         this.setSize(box.width, box.height);
8514         this.setPagePosition(box.x, box.y);
8515         return this;
8516     },
8517
8518     // protected
8519     getResizeEl : function(){
8520         return this.resizeEl || this.el;
8521     },
8522
8523     // protected
8524     getPositionEl : function(){
8525         return this.positionEl || this.el;
8526     },
8527
8528     /**
8529      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8530      * This method fires the move event.
8531      * @param {Number} left The new left
8532      * @param {Number} top The new top
8533      * @returns {Roo.BoxComponent} this
8534      */
8535     setPosition : function(x, y){
8536         this.x = x;
8537         this.y = y;
8538         if(!this.boxReady){
8539             return this;
8540         }
8541         var adj = this.adjustPosition(x, y);
8542         var ax = adj.x, ay = adj.y;
8543
8544         var el = this.getPositionEl();
8545         if(ax !== undefined || ay !== undefined){
8546             if(ax !== undefined && ay !== undefined){
8547                 el.setLeftTop(ax, ay);
8548             }else if(ax !== undefined){
8549                 el.setLeft(ax);
8550             }else if(ay !== undefined){
8551                 el.setTop(ay);
8552             }
8553             this.onPosition(ax, ay);
8554             this.fireEvent('move', this, ax, ay);
8555         }
8556         return this;
8557     },
8558
8559     /**
8560      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8561      * This method fires the move event.
8562      * @param {Number} x The new x position
8563      * @param {Number} y The new y position
8564      * @returns {Roo.BoxComponent} this
8565      */
8566     setPagePosition : function(x, y){
8567         this.pageX = x;
8568         this.pageY = y;
8569         if(!this.boxReady){
8570             return;
8571         }
8572         if(x === undefined || y === undefined){ // cannot translate undefined points
8573             return;
8574         }
8575         var p = this.el.translatePoints(x, y);
8576         this.setPosition(p.left, p.top);
8577         return this;
8578     },
8579
8580     // private
8581     onRender : function(ct, position){
8582         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8583         if(this.resizeEl){
8584             this.resizeEl = Roo.get(this.resizeEl);
8585         }
8586         if(this.positionEl){
8587             this.positionEl = Roo.get(this.positionEl);
8588         }
8589     },
8590
8591     // private
8592     afterRender : function(){
8593         Roo.BoxComponent.superclass.afterRender.call(this);
8594         this.boxReady = true;
8595         this.setSize(this.width, this.height);
8596         if(this.x || this.y){
8597             this.setPosition(this.x, this.y);
8598         }
8599         if(this.pageX || this.pageY){
8600             this.setPagePosition(this.pageX, this.pageY);
8601         }
8602     },
8603
8604     /**
8605      * Force the component's size to recalculate based on the underlying element's current height and width.
8606      * @returns {Roo.BoxComponent} this
8607      */
8608     syncSize : function(){
8609         delete this.lastSize;
8610         this.setSize(this.el.getWidth(), this.el.getHeight());
8611         return this;
8612     },
8613
8614     /**
8615      * Called after the component is resized, this method is empty by default but can be implemented by any
8616      * subclass that needs to perform custom logic after a resize occurs.
8617      * @param {Number} adjWidth The box-adjusted width that was set
8618      * @param {Number} adjHeight The box-adjusted height that was set
8619      * @param {Number} rawWidth The width that was originally specified
8620      * @param {Number} rawHeight The height that was originally specified
8621      */
8622     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8623
8624     },
8625
8626     /**
8627      * Called after the component is moved, this method is empty by default but can be implemented by any
8628      * subclass that needs to perform custom logic after a move occurs.
8629      * @param {Number} x The new x position
8630      * @param {Number} y The new y position
8631      */
8632     onPosition : function(x, y){
8633
8634     },
8635
8636     // private
8637     adjustSize : function(w, h){
8638         if(this.autoWidth){
8639             w = 'auto';
8640         }
8641         if(this.autoHeight){
8642             h = 'auto';
8643         }
8644         return {width : w, height: h};
8645     },
8646
8647     // private
8648     adjustPosition : function(x, y){
8649         return {x : x, y: y};
8650     }
8651 });/*
8652  * Based on:
8653  * Ext JS Library 1.1.1
8654  * Copyright(c) 2006-2007, Ext JS, LLC.
8655  *
8656  * Originally Released Under LGPL - original licence link has changed is not relivant.
8657  *
8658  * Fork - LGPL
8659  * <script type="text/javascript">
8660  */
8661
8662
8663 /**
8664  * @class Roo.SplitBar
8665  * @extends Roo.util.Observable
8666  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8667  * <br><br>
8668  * Usage:
8669  * <pre><code>
8670 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8671                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8672 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8673 split.minSize = 100;
8674 split.maxSize = 600;
8675 split.animate = true;
8676 split.on('moved', splitterMoved);
8677 </code></pre>
8678  * @constructor
8679  * Create a new SplitBar
8680  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8681  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8682  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8683  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8684                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8685                         position of the SplitBar).
8686  */
8687 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8688     
8689     /** @private */
8690     this.el = Roo.get(dragElement, true);
8691     this.el.dom.unselectable = "on";
8692     /** @private */
8693     this.resizingEl = Roo.get(resizingElement, true);
8694
8695     /**
8696      * @private
8697      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8698      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8699      * @type Number
8700      */
8701     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8702     
8703     /**
8704      * The minimum size of the resizing element. (Defaults to 0)
8705      * @type Number
8706      */
8707     this.minSize = 0;
8708     
8709     /**
8710      * The maximum size of the resizing element. (Defaults to 2000)
8711      * @type Number
8712      */
8713     this.maxSize = 2000;
8714     
8715     /**
8716      * Whether to animate the transition to the new size
8717      * @type Boolean
8718      */
8719     this.animate = false;
8720     
8721     /**
8722      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8723      * @type Boolean
8724      */
8725     this.useShim = false;
8726     
8727     /** @private */
8728     this.shim = null;
8729     
8730     if(!existingProxy){
8731         /** @private */
8732         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8733     }else{
8734         this.proxy = Roo.get(existingProxy).dom;
8735     }
8736     /** @private */
8737     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8738     
8739     /** @private */
8740     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8741     
8742     /** @private */
8743     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8744     
8745     /** @private */
8746     this.dragSpecs = {};
8747     
8748     /**
8749      * @private The adapter to use to positon and resize elements
8750      */
8751     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8752     this.adapter.init(this);
8753     
8754     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8755         /** @private */
8756         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8757         this.el.addClass("x-splitbar-h");
8758     }else{
8759         /** @private */
8760         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8761         this.el.addClass("x-splitbar-v");
8762     }
8763     
8764     this.addEvents({
8765         /**
8766          * @event resize
8767          * Fires when the splitter is moved (alias for {@link #event-moved})
8768          * @param {Roo.SplitBar} this
8769          * @param {Number} newSize the new width or height
8770          */
8771         "resize" : true,
8772         /**
8773          * @event moved
8774          * Fires when the splitter is moved
8775          * @param {Roo.SplitBar} this
8776          * @param {Number} newSize the new width or height
8777          */
8778         "moved" : true,
8779         /**
8780          * @event beforeresize
8781          * Fires before the splitter is dragged
8782          * @param {Roo.SplitBar} this
8783          */
8784         "beforeresize" : true,
8785
8786         "beforeapply" : true
8787     });
8788
8789     Roo.util.Observable.call(this);
8790 };
8791
8792 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8793     onStartProxyDrag : function(x, y){
8794         this.fireEvent("beforeresize", this);
8795         if(!this.overlay){
8796             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8797             o.unselectable();
8798             o.enableDisplayMode("block");
8799             // all splitbars share the same overlay
8800             Roo.SplitBar.prototype.overlay = o;
8801         }
8802         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8803         this.overlay.show();
8804         Roo.get(this.proxy).setDisplayed("block");
8805         var size = this.adapter.getElementSize(this);
8806         this.activeMinSize = this.getMinimumSize();;
8807         this.activeMaxSize = this.getMaximumSize();;
8808         var c1 = size - this.activeMinSize;
8809         var c2 = Math.max(this.activeMaxSize - size, 0);
8810         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8811             this.dd.resetConstraints();
8812             this.dd.setXConstraint(
8813                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8814                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8815             );
8816             this.dd.setYConstraint(0, 0);
8817         }else{
8818             this.dd.resetConstraints();
8819             this.dd.setXConstraint(0, 0);
8820             this.dd.setYConstraint(
8821                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8822                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8823             );
8824          }
8825         this.dragSpecs.startSize = size;
8826         this.dragSpecs.startPoint = [x, y];
8827         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8828     },
8829     
8830     /** 
8831      * @private Called after the drag operation by the DDProxy
8832      */
8833     onEndProxyDrag : function(e){
8834         Roo.get(this.proxy).setDisplayed(false);
8835         var endPoint = Roo.lib.Event.getXY(e);
8836         if(this.overlay){
8837             this.overlay.hide();
8838         }
8839         var newSize;
8840         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8841             newSize = this.dragSpecs.startSize + 
8842                 (this.placement == Roo.SplitBar.LEFT ?
8843                     endPoint[0] - this.dragSpecs.startPoint[0] :
8844                     this.dragSpecs.startPoint[0] - endPoint[0]
8845                 );
8846         }else{
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.TOP ?
8849                     endPoint[1] - this.dragSpecs.startPoint[1] :
8850                     this.dragSpecs.startPoint[1] - endPoint[1]
8851                 );
8852         }
8853         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8854         if(newSize != this.dragSpecs.startSize){
8855             if(this.fireEvent('beforeapply', this, newSize) !== false){
8856                 this.adapter.setElementSize(this, newSize);
8857                 this.fireEvent("moved", this, newSize);
8858                 this.fireEvent("resize", this, newSize);
8859             }
8860         }
8861     },
8862     
8863     /**
8864      * Get the adapter this SplitBar uses
8865      * @return The adapter object
8866      */
8867     getAdapter : function(){
8868         return this.adapter;
8869     },
8870     
8871     /**
8872      * Set the adapter this SplitBar uses
8873      * @param {Object} adapter A SplitBar adapter object
8874      */
8875     setAdapter : function(adapter){
8876         this.adapter = adapter;
8877         this.adapter.init(this);
8878     },
8879     
8880     /**
8881      * Gets the minimum size for the resizing element
8882      * @return {Number} The minimum size
8883      */
8884     getMinimumSize : function(){
8885         return this.minSize;
8886     },
8887     
8888     /**
8889      * Sets the minimum size for the resizing element
8890      * @param {Number} minSize The minimum size
8891      */
8892     setMinimumSize : function(minSize){
8893         this.minSize = minSize;
8894     },
8895     
8896     /**
8897      * Gets the maximum size for the resizing element
8898      * @return {Number} The maximum size
8899      */
8900     getMaximumSize : function(){
8901         return this.maxSize;
8902     },
8903     
8904     /**
8905      * Sets the maximum size for the resizing element
8906      * @param {Number} maxSize The maximum size
8907      */
8908     setMaximumSize : function(maxSize){
8909         this.maxSize = maxSize;
8910     },
8911     
8912     /**
8913      * Sets the initialize size for the resizing element
8914      * @param {Number} size The initial size
8915      */
8916     setCurrentSize : function(size){
8917         var oldAnimate = this.animate;
8918         this.animate = false;
8919         this.adapter.setElementSize(this, size);
8920         this.animate = oldAnimate;
8921     },
8922     
8923     /**
8924      * Destroy this splitbar. 
8925      * @param {Boolean} removeEl True to remove the element
8926      */
8927     destroy : function(removeEl){
8928         if(this.shim){
8929             this.shim.remove();
8930         }
8931         this.dd.unreg();
8932         this.proxy.parentNode.removeChild(this.proxy);
8933         if(removeEl){
8934             this.el.remove();
8935         }
8936     }
8937 });
8938
8939 /**
8940  * @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.
8941  */
8942 Roo.SplitBar.createProxy = function(dir){
8943     var proxy = new Roo.Element(document.createElement("div"));
8944     proxy.unselectable();
8945     var cls = 'x-splitbar-proxy';
8946     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8947     document.body.appendChild(proxy.dom);
8948     return proxy.dom;
8949 };
8950
8951 /** 
8952  * @class Roo.SplitBar.BasicLayoutAdapter
8953  * Default Adapter. It assumes the splitter and resizing element are not positioned
8954  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8955  */
8956 Roo.SplitBar.BasicLayoutAdapter = function(){
8957 };
8958
8959 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8960     // do nothing for now
8961     init : function(s){
8962     
8963     },
8964     /**
8965      * Called before drag operations to get the current size of the resizing element. 
8966      * @param {Roo.SplitBar} s The SplitBar using this adapter
8967      */
8968      getElementSize : function(s){
8969         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8970             return s.resizingEl.getWidth();
8971         }else{
8972             return s.resizingEl.getHeight();
8973         }
8974     },
8975     
8976     /**
8977      * Called after drag operations to set the size of the resizing element.
8978      * @param {Roo.SplitBar} s The SplitBar using this adapter
8979      * @param {Number} newSize The new size to set
8980      * @param {Function} onComplete A function to be invoked when resizing is complete
8981      */
8982     setElementSize : function(s, newSize, onComplete){
8983         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8984             if(!s.animate){
8985                 s.resizingEl.setWidth(newSize);
8986                 if(onComplete){
8987                     onComplete(s, newSize);
8988                 }
8989             }else{
8990                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8991             }
8992         }else{
8993             
8994             if(!s.animate){
8995                 s.resizingEl.setHeight(newSize);
8996                 if(onComplete){
8997                     onComplete(s, newSize);
8998                 }
8999             }else{
9000                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9001             }
9002         }
9003     }
9004 };
9005
9006 /** 
9007  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9008  * @extends Roo.SplitBar.BasicLayoutAdapter
9009  * Adapter that  moves the splitter element to align with the resized sizing element. 
9010  * Used with an absolute positioned SplitBar.
9011  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9012  * document.body, make sure you assign an id to the body element.
9013  */
9014 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9015     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9016     this.container = Roo.get(container);
9017 };
9018
9019 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9020     init : function(s){
9021         this.basic.init(s);
9022     },
9023     
9024     getElementSize : function(s){
9025         return this.basic.getElementSize(s);
9026     },
9027     
9028     setElementSize : function(s, newSize, onComplete){
9029         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9030     },
9031     
9032     moveSplitter : function(s){
9033         var yes = Roo.SplitBar;
9034         switch(s.placement){
9035             case yes.LEFT:
9036                 s.el.setX(s.resizingEl.getRight());
9037                 break;
9038             case yes.RIGHT:
9039                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9040                 break;
9041             case yes.TOP:
9042                 s.el.setY(s.resizingEl.getBottom());
9043                 break;
9044             case yes.BOTTOM:
9045                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9046                 break;
9047         }
9048     }
9049 };
9050
9051 /**
9052  * Orientation constant - Create a vertical SplitBar
9053  * @static
9054  * @type Number
9055  */
9056 Roo.SplitBar.VERTICAL = 1;
9057
9058 /**
9059  * Orientation constant - Create a horizontal SplitBar
9060  * @static
9061  * @type Number
9062  */
9063 Roo.SplitBar.HORIZONTAL = 2;
9064
9065 /**
9066  * Placement constant - The resizing element is to the left of the splitter element
9067  * @static
9068  * @type Number
9069  */
9070 Roo.SplitBar.LEFT = 1;
9071
9072 /**
9073  * Placement constant - The resizing element is to the right of the splitter element
9074  * @static
9075  * @type Number
9076  */
9077 Roo.SplitBar.RIGHT = 2;
9078
9079 /**
9080  * Placement constant - The resizing element is positioned above the splitter element
9081  * @static
9082  * @type Number
9083  */
9084 Roo.SplitBar.TOP = 3;
9085
9086 /**
9087  * Placement constant - The resizing element is positioned under splitter element
9088  * @static
9089  * @type Number
9090  */
9091 Roo.SplitBar.BOTTOM = 4;
9092 /*
9093  * Based on:
9094  * Ext JS Library 1.1.1
9095  * Copyright(c) 2006-2007, Ext JS, LLC.
9096  *
9097  * Originally Released Under LGPL - original licence link has changed is not relivant.
9098  *
9099  * Fork - LGPL
9100  * <script type="text/javascript">
9101  */
9102
9103 /**
9104  * @class Roo.View
9105  * @extends Roo.util.Observable
9106  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9107  * This class also supports single and multi selection modes. <br>
9108  * Create a data model bound view:
9109  <pre><code>
9110  var store = new Roo.data.Store(...);
9111
9112  var view = new Roo.View({
9113     el : "my-element",
9114     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9115  
9116     singleSelect: true,
9117     selectedClass: "ydataview-selected",
9118     store: store
9119  });
9120
9121  // listen for node click?
9122  view.on("click", function(vw, index, node, e){
9123  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9124  });
9125
9126  // load XML data
9127  dataModel.load("foobar.xml");
9128  </code></pre>
9129  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9130  * <br><br>
9131  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9132  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9133  * 
9134  * Note: old style constructor is still suported (container, template, config)
9135  * 
9136  * @constructor
9137  * Create a new View
9138  * @param {Object} config The config object
9139  * 
9140  */
9141 Roo.View = function(config, depreciated_tpl, depreciated_config){
9142     
9143     if (typeof(depreciated_tpl) == 'undefined') {
9144         // new way.. - universal constructor.
9145         Roo.apply(this, config);
9146         this.el  = Roo.get(this.el);
9147     } else {
9148         // old format..
9149         this.el  = Roo.get(config);
9150         this.tpl = depreciated_tpl;
9151         Roo.apply(this, depreciated_config);
9152     }
9153      
9154     
9155     if(typeof(this.tpl) == "string"){
9156         this.tpl = new Roo.Template(this.tpl);
9157     } else {
9158         // support xtype ctors..
9159         this.tpl = new Roo.factory(this.tpl, Roo);
9160     }
9161     
9162     
9163     this.tpl.compile();
9164    
9165
9166      
9167     /** @private */
9168     this.addEvents({
9169         /**
9170          * @event beforeclick
9171          * Fires before a click is processed. Returns false to cancel the default action.
9172          * @param {Roo.View} this
9173          * @param {Number} index The index of the target node
9174          * @param {HTMLElement} node The target node
9175          * @param {Roo.EventObject} e The raw event object
9176          */
9177             "beforeclick" : true,
9178         /**
9179          * @event click
9180          * Fires when a template node is clicked.
9181          * @param {Roo.View} this
9182          * @param {Number} index The index of the target node
9183          * @param {HTMLElement} node The target node
9184          * @param {Roo.EventObject} e The raw event object
9185          */
9186             "click" : true,
9187         /**
9188          * @event dblclick
9189          * Fires when a template node is double clicked.
9190          * @param {Roo.View} this
9191          * @param {Number} index The index of the target node
9192          * @param {HTMLElement} node The target node
9193          * @param {Roo.EventObject} e The raw event object
9194          */
9195             "dblclick" : true,
9196         /**
9197          * @event contextmenu
9198          * Fires when a template node is right clicked.
9199          * @param {Roo.View} this
9200          * @param {Number} index The index of the target node
9201          * @param {HTMLElement} node The target node
9202          * @param {Roo.EventObject} e The raw event object
9203          */
9204             "contextmenu" : true,
9205         /**
9206          * @event selectionchange
9207          * Fires when the selected nodes change.
9208          * @param {Roo.View} this
9209          * @param {Array} selections Array of the selected nodes
9210          */
9211             "selectionchange" : true,
9212     
9213         /**
9214          * @event beforeselect
9215          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9216          * @param {Roo.View} this
9217          * @param {HTMLElement} node The node to be selected
9218          * @param {Array} selections Array of currently selected nodes
9219          */
9220             "beforeselect" : true,
9221         /**
9222          * @event preparedata
9223          * Fires on every row to render, to allow you to change the data.
9224          * @param {Roo.View} this
9225          * @param {Object} data to be rendered (change this)
9226          */
9227           "preparedata" : true
9228         });
9229
9230     this.el.on({
9231         "click": this.onClick,
9232         "dblclick": this.onDblClick,
9233         "contextmenu": this.onContextMenu,
9234         scope:this
9235     });
9236
9237     this.selections = [];
9238     this.nodes = [];
9239     this.cmp = new Roo.CompositeElementLite([]);
9240     if(this.store){
9241         this.store = Roo.factory(this.store, Roo.data);
9242         this.setStore(this.store, true);
9243     }
9244     Roo.View.superclass.constructor.call(this);
9245 };
9246
9247 Roo.extend(Roo.View, Roo.util.Observable, {
9248     
9249      /**
9250      * @cfg {Roo.data.Store} store Data store to load data from.
9251      */
9252     store : false,
9253     
9254     /**
9255      * @cfg {String|Roo.Element} el The container element.
9256      */
9257     el : '',
9258     
9259     /**
9260      * @cfg {String|Roo.Template} tpl The template used by this View 
9261      */
9262     tpl : false,
9263     /**
9264      * @cfg {String} dataName the named area of the template to use as the data area
9265      *                          Works with domtemplates roo-name="name"
9266      */
9267     dataName: false,
9268     /**
9269      * @cfg {String} selectedClass The css class to add to selected nodes
9270      */
9271     selectedClass : "x-view-selected",
9272      /**
9273      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9274      */
9275     emptyText : "",
9276     /**
9277      * @cfg {Boolean} multiSelect Allow multiple selection
9278      */
9279     multiSelect : false,
9280     /**
9281      * @cfg {Boolean} singleSelect Allow single selection
9282      */
9283     singleSelect:  false,
9284     
9285     /**
9286      * @cfg {Boolean} toggleSelect - selecting 
9287      */
9288     toggleSelect : false,
9289     
9290     /**
9291      * Returns the element this view is bound to.
9292      * @return {Roo.Element}
9293      */
9294     getEl : function(){
9295         return this.el;
9296     },
9297
9298     /**
9299      * Refreshes the view.
9300      */
9301     refresh : function(){
9302         var t = this.tpl;
9303         
9304         // if we are using something like 'domtemplate', then
9305         // the what gets used is:
9306         // t.applySubtemplate(NAME, data, wrapping data..)
9307         // the outer template then get' applied with
9308         //     the store 'extra data'
9309         // and the body get's added to the
9310         //      roo-name="data" node?
9311         //      <span class='roo-tpl-{name}'></span> ?????
9312         
9313         
9314         
9315         this.clearSelections();
9316         this.el.update("");
9317         var html = [];
9318         var records = this.store.getRange();
9319         if(records.length < 1) {
9320             
9321             // is this valid??  = should it render a template??
9322             
9323             this.el.update(this.emptyText);
9324             return;
9325         }
9326         var el = this.el;
9327         if (this.dataName) {
9328             this.el.update(t.apply(this.store.meta)); //????
9329             el = this.el.child('.roo-tpl-' + this.dataName);
9330         }
9331         
9332         for(var i = 0, len = records.length; i < len; i++){
9333             var data = this.prepareData(records[i].data, i, records[i]);
9334             this.fireEvent("preparedata", this, data, i, records[i]);
9335             html[html.length] = Roo.util.Format.trim(
9336                 this.dataName ?
9337                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9338                     t.apply(data)
9339             );
9340         }
9341         
9342         
9343         
9344         el.update(html.join(""));
9345         this.nodes = el.dom.childNodes;
9346         this.updateIndexes(0);
9347     },
9348
9349     /**
9350      * Function to override to reformat the data that is sent to
9351      * the template for each node.
9352      * DEPRICATED - use the preparedata event handler.
9353      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9354      * a JSON object for an UpdateManager bound view).
9355      */
9356     prepareData : function(data, index, record)
9357     {
9358         this.fireEvent("preparedata", this, data, index, record);
9359         return data;
9360     },
9361
9362     onUpdate : function(ds, record){
9363         this.clearSelections();
9364         var index = this.store.indexOf(record);
9365         var n = this.nodes[index];
9366         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9367         n.parentNode.removeChild(n);
9368         this.updateIndexes(index, index);
9369     },
9370
9371     
9372     
9373 // --------- FIXME     
9374     onAdd : function(ds, records, index)
9375     {
9376         this.clearSelections();
9377         if(this.nodes.length == 0){
9378             this.refresh();
9379             return;
9380         }
9381         var n = this.nodes[index];
9382         for(var i = 0, len = records.length; i < len; i++){
9383             var d = this.prepareData(records[i].data, i, records[i]);
9384             if(n){
9385                 this.tpl.insertBefore(n, d);
9386             }else{
9387                 
9388                 this.tpl.append(this.el, d);
9389             }
9390         }
9391         this.updateIndexes(index);
9392     },
9393
9394     onRemove : function(ds, record, index){
9395         this.clearSelections();
9396         var el = this.dataName  ?
9397             this.el.child('.roo-tpl-' + this.dataName) :
9398             this.el; 
9399         el.dom.removeChild(this.nodes[index]);
9400         this.updateIndexes(index);
9401     },
9402
9403     /**
9404      * Refresh an individual node.
9405      * @param {Number} index
9406      */
9407     refreshNode : function(index){
9408         this.onUpdate(this.store, this.store.getAt(index));
9409     },
9410
9411     updateIndexes : function(startIndex, endIndex){
9412         var ns = this.nodes;
9413         startIndex = startIndex || 0;
9414         endIndex = endIndex || ns.length - 1;
9415         for(var i = startIndex; i <= endIndex; i++){
9416             ns[i].nodeIndex = i;
9417         }
9418     },
9419
9420     /**
9421      * Changes the data store this view uses and refresh the view.
9422      * @param {Store} store
9423      */
9424     setStore : function(store, initial){
9425         if(!initial && this.store){
9426             this.store.un("datachanged", this.refresh);
9427             this.store.un("add", this.onAdd);
9428             this.store.un("remove", this.onRemove);
9429             this.store.un("update", this.onUpdate);
9430             this.store.un("clear", this.refresh);
9431         }
9432         if(store){
9433           
9434             store.on("datachanged", this.refresh, this);
9435             store.on("add", this.onAdd, this);
9436             store.on("remove", this.onRemove, this);
9437             store.on("update", this.onUpdate, this);
9438             store.on("clear", this.refresh, this);
9439         }
9440         
9441         if(store){
9442             this.refresh();
9443         }
9444     },
9445
9446     /**
9447      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9448      * @param {HTMLElement} node
9449      * @return {HTMLElement} The template node
9450      */
9451     findItemFromChild : function(node){
9452         var el = this.dataName  ?
9453             this.el.child('.roo-tpl-' + this.dataName,true) :
9454             this.el.dom; 
9455         
9456         if(!node || node.parentNode == el){
9457                     return node;
9458             }
9459             var p = node.parentNode;
9460             while(p && p != el){
9461             if(p.parentNode == el){
9462                 return p;
9463             }
9464             p = p.parentNode;
9465         }
9466             return null;
9467     },
9468
9469     /** @ignore */
9470     onClick : function(e){
9471         var item = this.findItemFromChild(e.getTarget());
9472         if(item){
9473             var index = this.indexOf(item);
9474             if(this.onItemClick(item, index, e) !== false){
9475                 this.fireEvent("click", this, index, item, e);
9476             }
9477         }else{
9478             this.clearSelections();
9479         }
9480     },
9481
9482     /** @ignore */
9483     onContextMenu : function(e){
9484         var item = this.findItemFromChild(e.getTarget());
9485         if(item){
9486             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9487         }
9488     },
9489
9490     /** @ignore */
9491     onDblClick : function(e){
9492         var item = this.findItemFromChild(e.getTarget());
9493         if(item){
9494             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9495         }
9496     },
9497
9498     onItemClick : function(item, index, e)
9499     {
9500         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9501             return false;
9502         }
9503         if (this.toggleSelect) {
9504             var m = this.isSelected(item) ? 'unselect' : 'select';
9505             Roo.log(m);
9506             var _t = this;
9507             _t[m](item, true, false);
9508             return true;
9509         }
9510         if(this.multiSelect || this.singleSelect){
9511             if(this.multiSelect && e.shiftKey && this.lastSelection){
9512                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9513             }else{
9514                 this.select(item, this.multiSelect && e.ctrlKey);
9515                 this.lastSelection = item;
9516             }
9517             e.preventDefault();
9518         }
9519         return true;
9520     },
9521
9522     /**
9523      * Get the number of selected nodes.
9524      * @return {Number}
9525      */
9526     getSelectionCount : function(){
9527         return this.selections.length;
9528     },
9529
9530     /**
9531      * Get the currently selected nodes.
9532      * @return {Array} An array of HTMLElements
9533      */
9534     getSelectedNodes : function(){
9535         return this.selections;
9536     },
9537
9538     /**
9539      * Get the indexes of the selected nodes.
9540      * @return {Array}
9541      */
9542     getSelectedIndexes : function(){
9543         var indexes = [], s = this.selections;
9544         for(var i = 0, len = s.length; i < len; i++){
9545             indexes.push(s[i].nodeIndex);
9546         }
9547         return indexes;
9548     },
9549
9550     /**
9551      * Clear all selections
9552      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9553      */
9554     clearSelections : function(suppressEvent){
9555         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9556             this.cmp.elements = this.selections;
9557             this.cmp.removeClass(this.selectedClass);
9558             this.selections = [];
9559             if(!suppressEvent){
9560                 this.fireEvent("selectionchange", this, this.selections);
9561             }
9562         }
9563     },
9564
9565     /**
9566      * Returns true if the passed node is selected
9567      * @param {HTMLElement/Number} node The node or node index
9568      * @return {Boolean}
9569      */
9570     isSelected : function(node){
9571         var s = this.selections;
9572         if(s.length < 1){
9573             return false;
9574         }
9575         node = this.getNode(node);
9576         return s.indexOf(node) !== -1;
9577     },
9578
9579     /**
9580      * Selects nodes.
9581      * @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
9582      * @param {Boolean} keepExisting (optional) true to keep existing selections
9583      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9584      */
9585     select : function(nodeInfo, keepExisting, suppressEvent){
9586         if(nodeInfo instanceof Array){
9587             if(!keepExisting){
9588                 this.clearSelections(true);
9589             }
9590             for(var i = 0, len = nodeInfo.length; i < len; i++){
9591                 this.select(nodeInfo[i], true, true);
9592             }
9593             return;
9594         } 
9595         var node = this.getNode(nodeInfo);
9596         if(!node || this.isSelected(node)){
9597             return; // already selected.
9598         }
9599         if(!keepExisting){
9600             this.clearSelections(true);
9601         }
9602         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9603             Roo.fly(node).addClass(this.selectedClass);
9604             this.selections.push(node);
9605             if(!suppressEvent){
9606                 this.fireEvent("selectionchange", this, this.selections);
9607             }
9608         }
9609         
9610         
9611     },
9612       /**
9613      * Unselects nodes.
9614      * @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
9615      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9616      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9617      */
9618     unselect : function(nodeInfo, keepExisting, suppressEvent)
9619     {
9620         if(nodeInfo instanceof Array){
9621             Roo.each(this.selections, function(s) {
9622                 this.unselect(s, nodeInfo);
9623             }, this);
9624             return;
9625         }
9626         var node = this.getNode(nodeInfo);
9627         if(!node || !this.isSelected(node)){
9628             Roo.log("not selected");
9629             return; // not selected.
9630         }
9631         // fireevent???
9632         var ns = [];
9633         Roo.each(this.selections, function(s) {
9634             if (s == node ) {
9635                 Roo.fly(node).removeClass(this.selectedClass);
9636
9637                 return;
9638             }
9639             ns.push(s);
9640         },this);
9641         
9642         this.selections= ns;
9643         this.fireEvent("selectionchange", this, this.selections);
9644     },
9645
9646     /**
9647      * Gets a template node.
9648      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9649      * @return {HTMLElement} The node or null if it wasn't found
9650      */
9651     getNode : function(nodeInfo){
9652         if(typeof nodeInfo == "string"){
9653             return document.getElementById(nodeInfo);
9654         }else if(typeof nodeInfo == "number"){
9655             return this.nodes[nodeInfo];
9656         }
9657         return nodeInfo;
9658     },
9659
9660     /**
9661      * Gets a range template nodes.
9662      * @param {Number} startIndex
9663      * @param {Number} endIndex
9664      * @return {Array} An array of nodes
9665      */
9666     getNodes : function(start, end){
9667         var ns = this.nodes;
9668         start = start || 0;
9669         end = typeof end == "undefined" ? ns.length - 1 : end;
9670         var nodes = [];
9671         if(start <= end){
9672             for(var i = start; i <= end; i++){
9673                 nodes.push(ns[i]);
9674             }
9675         } else{
9676             for(var i = start; i >= end; i--){
9677                 nodes.push(ns[i]);
9678             }
9679         }
9680         return nodes;
9681     },
9682
9683     /**
9684      * Finds the index of the passed node
9685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9686      * @return {Number} The index of the node or -1
9687      */
9688     indexOf : function(node){
9689         node = this.getNode(node);
9690         if(typeof node.nodeIndex == "number"){
9691             return node.nodeIndex;
9692         }
9693         var ns = this.nodes;
9694         for(var i = 0, len = ns.length; i < len; i++){
9695             if(ns[i] == node){
9696                 return i;
9697             }
9698         }
9699         return -1;
9700     }
9701 });
9702 /*
9703  * Based on:
9704  * Ext JS Library 1.1.1
9705  * Copyright(c) 2006-2007, Ext JS, LLC.
9706  *
9707  * Originally Released Under LGPL - original licence link has changed is not relivant.
9708  *
9709  * Fork - LGPL
9710  * <script type="text/javascript">
9711  */
9712
9713 /**
9714  * @class Roo.JsonView
9715  * @extends Roo.View
9716  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9717 <pre><code>
9718 var view = new Roo.JsonView({
9719     container: "my-element",
9720     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9721     multiSelect: true, 
9722     jsonRoot: "data" 
9723 });
9724
9725 // listen for node click?
9726 view.on("click", function(vw, index, node, e){
9727     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9728 });
9729
9730 // direct load of JSON data
9731 view.load("foobar.php");
9732
9733 // Example from my blog list
9734 var tpl = new Roo.Template(
9735     '&lt;div class="entry"&gt;' +
9736     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9737     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9738     "&lt;/div&gt;&lt;hr /&gt;"
9739 );
9740
9741 var moreView = new Roo.JsonView({
9742     container :  "entry-list", 
9743     template : tpl,
9744     jsonRoot: "posts"
9745 });
9746 moreView.on("beforerender", this.sortEntries, this);
9747 moreView.load({
9748     url: "/blog/get-posts.php",
9749     params: "allposts=true",
9750     text: "Loading Blog Entries..."
9751 });
9752 </code></pre>
9753
9754 * Note: old code is supported with arguments : (container, template, config)
9755
9756
9757  * @constructor
9758  * Create a new JsonView
9759  * 
9760  * @param {Object} config The config object
9761  * 
9762  */
9763 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9764     
9765     
9766     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9767
9768     var um = this.el.getUpdateManager();
9769     um.setRenderer(this);
9770     um.on("update", this.onLoad, this);
9771     um.on("failure", this.onLoadException, this);
9772
9773     /**
9774      * @event beforerender
9775      * Fires before rendering of the downloaded JSON data.
9776      * @param {Roo.JsonView} this
9777      * @param {Object} data The JSON data loaded
9778      */
9779     /**
9780      * @event load
9781      * Fires when data is loaded.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      * @param {Object} response The raw Connect response object
9785      */
9786     /**
9787      * @event loadexception
9788      * Fires when loading fails.
9789      * @param {Roo.JsonView} this
9790      * @param {Object} response The raw Connect response object
9791      */
9792     this.addEvents({
9793         'beforerender' : true,
9794         'load' : true,
9795         'loadexception' : true
9796     });
9797 };
9798 Roo.extend(Roo.JsonView, Roo.View, {
9799     /**
9800      * @type {String} The root property in the loaded JSON object that contains the data
9801      */
9802     jsonRoot : "",
9803
9804     /**
9805      * Refreshes the view.
9806      */
9807     refresh : function(){
9808         this.clearSelections();
9809         this.el.update("");
9810         var html = [];
9811         var o = this.jsonData;
9812         if(o && o.length > 0){
9813             for(var i = 0, len = o.length; i < len; i++){
9814                 var data = this.prepareData(o[i], i, o);
9815                 html[html.length] = this.tpl.apply(data);
9816             }
9817         }else{
9818             html.push(this.emptyText);
9819         }
9820         this.el.update(html.join(""));
9821         this.nodes = this.el.dom.childNodes;
9822         this.updateIndexes(0);
9823     },
9824
9825     /**
9826      * 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.
9827      * @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:
9828      <pre><code>
9829      view.load({
9830          url: "your-url.php",
9831          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9832          callback: yourFunction,
9833          scope: yourObject, //(optional scope)
9834          discardUrl: false,
9835          nocache: false,
9836          text: "Loading...",
9837          timeout: 30,
9838          scripts: false
9839      });
9840      </code></pre>
9841      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9842      * 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.
9843      * @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}
9844      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9845      * @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.
9846      */
9847     load : function(){
9848         var um = this.el.getUpdateManager();
9849         um.update.apply(um, arguments);
9850     },
9851
9852     render : function(el, response){
9853         this.clearSelections();
9854         this.el.update("");
9855         var o;
9856         try{
9857             o = Roo.util.JSON.decode(response.responseText);
9858             if(this.jsonRoot){
9859                 
9860                 o = o[this.jsonRoot];
9861             }
9862         } catch(e){
9863         }
9864         /**
9865          * The current JSON data or null
9866          */
9867         this.jsonData = o;
9868         this.beforeRender();
9869         this.refresh();
9870     },
9871
9872 /**
9873  * Get the number of records in the current JSON dataset
9874  * @return {Number}
9875  */
9876     getCount : function(){
9877         return this.jsonData ? this.jsonData.length : 0;
9878     },
9879
9880 /**
9881  * Returns the JSON object for the specified node(s)
9882  * @param {HTMLElement/Array} node The node or an array of nodes
9883  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9884  * you get the JSON object for the node
9885  */
9886     getNodeData : function(node){
9887         if(node instanceof Array){
9888             var data = [];
9889             for(var i = 0, len = node.length; i < len; i++){
9890                 data.push(this.getNodeData(node[i]));
9891             }
9892             return data;
9893         }
9894         return this.jsonData[this.indexOf(node)] || null;
9895     },
9896
9897     beforeRender : function(){
9898         this.snapshot = this.jsonData;
9899         if(this.sortInfo){
9900             this.sort.apply(this, this.sortInfo);
9901         }
9902         this.fireEvent("beforerender", this, this.jsonData);
9903     },
9904
9905     onLoad : function(el, o){
9906         this.fireEvent("load", this, this.jsonData, o);
9907     },
9908
9909     onLoadException : function(el, o){
9910         this.fireEvent("loadexception", this, o);
9911     },
9912
9913 /**
9914  * Filter the data by a specific property.
9915  * @param {String} property A property on your JSON objects
9916  * @param {String/RegExp} value Either string that the property values
9917  * should start with, or a RegExp to test against the property
9918  */
9919     filter : function(property, value){
9920         if(this.jsonData){
9921             var data = [];
9922             var ss = this.snapshot;
9923             if(typeof value == "string"){
9924                 var vlen = value.length;
9925                 if(vlen == 0){
9926                     this.clearFilter();
9927                     return;
9928                 }
9929                 value = value.toLowerCase();
9930                 for(var i = 0, len = ss.length; i < len; i++){
9931                     var o = ss[i];
9932                     if(o[property].substr(0, vlen).toLowerCase() == value){
9933                         data.push(o);
9934                     }
9935                 }
9936             } else if(value.exec){ // regex?
9937                 for(var i = 0, len = ss.length; i < len; i++){
9938                     var o = ss[i];
9939                     if(value.test(o[property])){
9940                         data.push(o);
9941                     }
9942                 }
9943             } else{
9944                 return;
9945             }
9946             this.jsonData = data;
9947             this.refresh();
9948         }
9949     },
9950
9951 /**
9952  * Filter by a function. The passed function will be called with each
9953  * object in the current dataset. If the function returns true the value is kept,
9954  * otherwise it is filtered.
9955  * @param {Function} fn
9956  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9957  */
9958     filterBy : function(fn, scope){
9959         if(this.jsonData){
9960             var data = [];
9961             var ss = this.snapshot;
9962             for(var i = 0, len = ss.length; i < len; i++){
9963                 var o = ss[i];
9964                 if(fn.call(scope || this, o)){
9965                     data.push(o);
9966                 }
9967             }
9968             this.jsonData = data;
9969             this.refresh();
9970         }
9971     },
9972
9973 /**
9974  * Clears the current filter.
9975  */
9976     clearFilter : function(){
9977         if(this.snapshot && this.jsonData != this.snapshot){
9978             this.jsonData = this.snapshot;
9979             this.refresh();
9980         }
9981     },
9982
9983
9984 /**
9985  * Sorts the data for this view and refreshes it.
9986  * @param {String} property A property on your JSON objects to sort on
9987  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9988  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9989  */
9990     sort : function(property, dir, sortType){
9991         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9992         if(this.jsonData){
9993             var p = property;
9994             var dsc = dir && dir.toLowerCase() == "desc";
9995             var f = function(o1, o2){
9996                 var v1 = sortType ? sortType(o1[p]) : o1[p];
9997                 var v2 = sortType ? sortType(o2[p]) : o2[p];
9998                 ;
9999                 if(v1 < v2){
10000                     return dsc ? +1 : -1;
10001                 } else if(v1 > v2){
10002                     return dsc ? -1 : +1;
10003                 } else{
10004                     return 0;
10005                 }
10006             };
10007             this.jsonData.sort(f);
10008             this.refresh();
10009             if(this.jsonData != this.snapshot){
10010                 this.snapshot.sort(f);
10011             }
10012         }
10013     }
10014 });/*
10015  * Based on:
10016  * Ext JS Library 1.1.1
10017  * Copyright(c) 2006-2007, Ext JS, LLC.
10018  *
10019  * Originally Released Under LGPL - original licence link has changed is not relivant.
10020  *
10021  * Fork - LGPL
10022  * <script type="text/javascript">
10023  */
10024  
10025
10026 /**
10027  * @class Roo.ColorPalette
10028  * @extends Roo.Component
10029  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10030  * Here's an example of typical usage:
10031  * <pre><code>
10032 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10033 cp.render('my-div');
10034
10035 cp.on('select', function(palette, selColor){
10036     // do something with selColor
10037 });
10038 </code></pre>
10039  * @constructor
10040  * Create a new ColorPalette
10041  * @param {Object} config The config object
10042  */
10043 Roo.ColorPalette = function(config){
10044     Roo.ColorPalette.superclass.constructor.call(this, config);
10045     this.addEvents({
10046         /**
10047              * @event select
10048              * Fires when a color is selected
10049              * @param {ColorPalette} this
10050              * @param {String} color The 6-digit color hex code (without the # symbol)
10051              */
10052         select: true
10053     });
10054
10055     if(this.handler){
10056         this.on("select", this.handler, this.scope, true);
10057     }
10058 };
10059 Roo.extend(Roo.ColorPalette, Roo.Component, {
10060     /**
10061      * @cfg {String} itemCls
10062      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10063      */
10064     itemCls : "x-color-palette",
10065     /**
10066      * @cfg {String} value
10067      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10068      * the hex codes are case-sensitive.
10069      */
10070     value : null,
10071     clickEvent:'click',
10072     // private
10073     ctype: "Roo.ColorPalette",
10074
10075     /**
10076      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10077      */
10078     allowReselect : false,
10079
10080     /**
10081      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10082      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10083      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10084      * of colors with the width setting until the box is symmetrical.</p>
10085      * <p>You can override individual colors if needed:</p>
10086      * <pre><code>
10087 var cp = new Roo.ColorPalette();
10088 cp.colors[0] = "FF0000";  // change the first box to red
10089 </code></pre>
10090
10091 Or you can provide a custom array of your own for complete control:
10092 <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors = ["000000", "993300", "333300"];
10095 </code></pre>
10096      * @type Array
10097      */
10098     colors : [
10099         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10100         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10101         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10102         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10103         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10104     ],
10105
10106     // private
10107     onRender : function(container, position){
10108         var t = new Roo.MasterTemplate(
10109             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10110         );
10111         var c = this.colors;
10112         for(var i = 0, len = c.length; i < len; i++){
10113             t.add([c[i]]);
10114         }
10115         var el = document.createElement("div");
10116         el.className = this.itemCls;
10117         t.overwrite(el);
10118         container.dom.insertBefore(el, position);
10119         this.el = Roo.get(el);
10120         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10121         if(this.clickEvent != 'click'){
10122             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10123         }
10124     },
10125
10126     // private
10127     afterRender : function(){
10128         Roo.ColorPalette.superclass.afterRender.call(this);
10129         if(this.value){
10130             var s = this.value;
10131             this.value = null;
10132             this.select(s);
10133         }
10134     },
10135
10136     // private
10137     handleClick : function(e, t){
10138         e.preventDefault();
10139         if(!this.disabled){
10140             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10141             this.select(c.toUpperCase());
10142         }
10143     },
10144
10145     /**
10146      * Selects the specified color in the palette (fires the select event)
10147      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10148      */
10149     select : function(color){
10150         color = color.replace("#", "");
10151         if(color != this.value || this.allowReselect){
10152             var el = this.el;
10153             if(this.value){
10154                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10155             }
10156             el.child("a.color-"+color).addClass("x-color-palette-sel");
10157             this.value = color;
10158             this.fireEvent("select", this, color);
10159         }
10160     }
10161 });/*
10162  * Based on:
10163  * Ext JS Library 1.1.1
10164  * Copyright(c) 2006-2007, Ext JS, LLC.
10165  *
10166  * Originally Released Under LGPL - original licence link has changed is not relivant.
10167  *
10168  * Fork - LGPL
10169  * <script type="text/javascript">
10170  */
10171  
10172 /**
10173  * @class Roo.DatePicker
10174  * @extends Roo.Component
10175  * Simple date picker class.
10176  * @constructor
10177  * Create a new DatePicker
10178  * @param {Object} config The config object
10179  */
10180 Roo.DatePicker = function(config){
10181     Roo.DatePicker.superclass.constructor.call(this, config);
10182
10183     this.value = config && config.value ?
10184                  config.value.clearTime() : new Date().clearTime();
10185
10186     this.addEvents({
10187         /**
10188              * @event select
10189              * Fires when a date is selected
10190              * @param {DatePicker} this
10191              * @param {Date} date The selected date
10192              */
10193         'select': true,
10194         /**
10195              * @event monthchange
10196              * Fires when the displayed month changes 
10197              * @param {DatePicker} this
10198              * @param {Date} date The selected month
10199              */
10200         'monthchange': true
10201     });
10202
10203     if(this.handler){
10204         this.on("select", this.handler,  this.scope || this);
10205     }
10206     // build the disabledDatesRE
10207     if(!this.disabledDatesRE && this.disabledDates){
10208         var dd = this.disabledDates;
10209         var re = "(?:";
10210         for(var i = 0; i < dd.length; i++){
10211             re += dd[i];
10212             if(i != dd.length-1) re += "|";
10213         }
10214         this.disabledDatesRE = new RegExp(re + ")");
10215     }
10216 };
10217
10218 Roo.extend(Roo.DatePicker, Roo.Component, {
10219     /**
10220      * @cfg {String} todayText
10221      * The text to display on the button that selects the current date (defaults to "Today")
10222      */
10223     todayText : "Today",
10224     /**
10225      * @cfg {String} okText
10226      * The text to display on the ok button
10227      */
10228     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10229     /**
10230      * @cfg {String} cancelText
10231      * The text to display on the cancel button
10232      */
10233     cancelText : "Cancel",
10234     /**
10235      * @cfg {String} todayTip
10236      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10237      */
10238     todayTip : "{0} (Spacebar)",
10239     /**
10240      * @cfg {Date} minDate
10241      * Minimum allowable date (JavaScript date object, defaults to null)
10242      */
10243     minDate : null,
10244     /**
10245      * @cfg {Date} maxDate
10246      * Maximum allowable date (JavaScript date object, defaults to null)
10247      */
10248     maxDate : null,
10249     /**
10250      * @cfg {String} minText
10251      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10252      */
10253     minText : "This date is before the minimum date",
10254     /**
10255      * @cfg {String} maxText
10256      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10257      */
10258     maxText : "This date is after the maximum date",
10259     /**
10260      * @cfg {String} format
10261      * The default date format string which can be overriden for localization support.  The format must be
10262      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10263      */
10264     format : "m/d/y",
10265     /**
10266      * @cfg {Array} disabledDays
10267      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10268      */
10269     disabledDays : null,
10270     /**
10271      * @cfg {String} disabledDaysText
10272      * The tooltip to display when the date falls on a disabled day (defaults to "")
10273      */
10274     disabledDaysText : "",
10275     /**
10276      * @cfg {RegExp} disabledDatesRE
10277      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10278      */
10279     disabledDatesRE : null,
10280     /**
10281      * @cfg {String} disabledDatesText
10282      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10283      */
10284     disabledDatesText : "",
10285     /**
10286      * @cfg {Boolean} constrainToViewport
10287      * True to constrain the date picker to the viewport (defaults to true)
10288      */
10289     constrainToViewport : true,
10290     /**
10291      * @cfg {Array} monthNames
10292      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10293      */
10294     monthNames : Date.monthNames,
10295     /**
10296      * @cfg {Array} dayNames
10297      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10298      */
10299     dayNames : Date.dayNames,
10300     /**
10301      * @cfg {String} nextText
10302      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10303      */
10304     nextText: 'Next Month (Control+Right)',
10305     /**
10306      * @cfg {String} prevText
10307      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10308      */
10309     prevText: 'Previous Month (Control+Left)',
10310     /**
10311      * @cfg {String} monthYearText
10312      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10313      */
10314     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10315     /**
10316      * @cfg {Number} startDay
10317      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10318      */
10319     startDay : 0,
10320     /**
10321      * @cfg {Bool} showClear
10322      * Show a clear button (usefull for date form elements that can be blank.)
10323      */
10324     
10325     showClear: false,
10326     
10327     /**
10328      * Sets the value of the date field
10329      * @param {Date} value The date to set
10330      */
10331     setValue : function(value){
10332         var old = this.value;
10333         this.value = value.clearTime(true);
10334         if(this.el){
10335             this.update(this.value);
10336         }
10337     },
10338
10339     /**
10340      * Gets the current selected value of the date field
10341      * @return {Date} The selected date
10342      */
10343     getValue : function(){
10344         return this.value;
10345     },
10346
10347     // private
10348     focus : function(){
10349         if(this.el){
10350             this.update(this.activeDate);
10351         }
10352     },
10353
10354     // private
10355     onRender : function(container, position){
10356         var m = [
10357              '<table cellspacing="0">',
10358                 '<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>',
10359                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10360         var dn = this.dayNames;
10361         for(var i = 0; i < 7; i++){
10362             var d = this.startDay+i;
10363             if(d > 6){
10364                 d = d-7;
10365             }
10366             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10367         }
10368         m[m.length] = "</tr></thead><tbody><tr>";
10369         for(var i = 0; i < 42; i++) {
10370             if(i % 7 == 0 && i != 0){
10371                 m[m.length] = "</tr><tr>";
10372             }
10373             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10374         }
10375         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10376             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10377
10378         var el = document.createElement("div");
10379         el.className = "x-date-picker";
10380         el.innerHTML = m.join("");
10381
10382         container.dom.insertBefore(el, position);
10383
10384         this.el = Roo.get(el);
10385         this.eventEl = Roo.get(el.firstChild);
10386
10387         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10388             handler: this.showPrevMonth,
10389             scope: this,
10390             preventDefault:true,
10391             stopDefault:true
10392         });
10393
10394         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10395             handler: this.showNextMonth,
10396             scope: this,
10397             preventDefault:true,
10398             stopDefault:true
10399         });
10400
10401         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10402
10403         this.monthPicker = this.el.down('div.x-date-mp');
10404         this.monthPicker.enableDisplayMode('block');
10405         
10406         var kn = new Roo.KeyNav(this.eventEl, {
10407             "left" : function(e){
10408                 e.ctrlKey ?
10409                     this.showPrevMonth() :
10410                     this.update(this.activeDate.add("d", -1));
10411             },
10412
10413             "right" : function(e){
10414                 e.ctrlKey ?
10415                     this.showNextMonth() :
10416                     this.update(this.activeDate.add("d", 1));
10417             },
10418
10419             "up" : function(e){
10420                 e.ctrlKey ?
10421                     this.showNextYear() :
10422                     this.update(this.activeDate.add("d", -7));
10423             },
10424
10425             "down" : function(e){
10426                 e.ctrlKey ?
10427                     this.showPrevYear() :
10428                     this.update(this.activeDate.add("d", 7));
10429             },
10430
10431             "pageUp" : function(e){
10432                 this.showNextMonth();
10433             },
10434
10435             "pageDown" : function(e){
10436                 this.showPrevMonth();
10437             },
10438
10439             "enter" : function(e){
10440                 e.stopPropagation();
10441                 return true;
10442             },
10443
10444             scope : this
10445         });
10446
10447         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10448
10449         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10450
10451         this.el.unselectable();
10452         
10453         this.cells = this.el.select("table.x-date-inner tbody td");
10454         this.textNodes = this.el.query("table.x-date-inner tbody span");
10455
10456         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10457             text: "&#160;",
10458             tooltip: this.monthYearText
10459         });
10460
10461         this.mbtn.on('click', this.showMonthPicker, this);
10462         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10463
10464
10465         var today = (new Date()).dateFormat(this.format);
10466         
10467         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10468         if (this.showClear) {
10469             baseTb.add( new Roo.Toolbar.Fill());
10470         }
10471         baseTb.add({
10472             text: String.format(this.todayText, today),
10473             tooltip: String.format(this.todayTip, today),
10474             handler: this.selectToday,
10475             scope: this
10476         });
10477         
10478         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10479             
10480         //});
10481         if (this.showClear) {
10482             
10483             baseTb.add( new Roo.Toolbar.Fill());
10484             baseTb.add({
10485                 text: '&#160;',
10486                 cls: 'x-btn-icon x-btn-clear',
10487                 handler: function() {
10488                     //this.value = '';
10489                     this.fireEvent("select", this, '');
10490                 },
10491                 scope: this
10492             });
10493         }
10494         
10495         
10496         if(Roo.isIE){
10497             this.el.repaint();
10498         }
10499         this.update(this.value);
10500     },
10501
10502     createMonthPicker : function(){
10503         if(!this.monthPicker.dom.firstChild){
10504             var buf = ['<table border="0" cellspacing="0">'];
10505             for(var i = 0; i < 6; i++){
10506                 buf.push(
10507                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10508                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10509                     i == 0 ?
10510                     '<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>' :
10511                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10512                 );
10513             }
10514             buf.push(
10515                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10516                     this.okText,
10517                     '</button><button type="button" class="x-date-mp-cancel">',
10518                     this.cancelText,
10519                     '</button></td></tr>',
10520                 '</table>'
10521             );
10522             this.monthPicker.update(buf.join(''));
10523             this.monthPicker.on('click', this.onMonthClick, this);
10524             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10525
10526             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10527             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10528
10529             this.mpMonths.each(function(m, a, i){
10530                 i += 1;
10531                 if((i%2) == 0){
10532                     m.dom.xmonth = 5 + Math.round(i * .5);
10533                 }else{
10534                     m.dom.xmonth = Math.round((i-1) * .5);
10535                 }
10536             });
10537         }
10538     },
10539
10540     showMonthPicker : function(){
10541         this.createMonthPicker();
10542         var size = this.el.getSize();
10543         this.monthPicker.setSize(size);
10544         this.monthPicker.child('table').setSize(size);
10545
10546         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10547         this.updateMPMonth(this.mpSelMonth);
10548         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10549         this.updateMPYear(this.mpSelYear);
10550
10551         this.monthPicker.slideIn('t', {duration:.2});
10552     },
10553
10554     updateMPYear : function(y){
10555         this.mpyear = y;
10556         var ys = this.mpYears.elements;
10557         for(var i = 1; i <= 10; i++){
10558             var td = ys[i-1], y2;
10559             if((i%2) == 0){
10560                 y2 = y + Math.round(i * .5);
10561                 td.firstChild.innerHTML = y2;
10562                 td.xyear = y2;
10563             }else{
10564                 y2 = y - (5-Math.round(i * .5));
10565                 td.firstChild.innerHTML = y2;
10566                 td.xyear = y2;
10567             }
10568             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10569         }
10570     },
10571
10572     updateMPMonth : function(sm){
10573         this.mpMonths.each(function(m, a, i){
10574             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10575         });
10576     },
10577
10578     selectMPMonth: function(m){
10579         
10580     },
10581
10582     onMonthClick : function(e, t){
10583         e.stopEvent();
10584         var el = new Roo.Element(t), pn;
10585         if(el.is('button.x-date-mp-cancel')){
10586             this.hideMonthPicker();
10587         }
10588         else if(el.is('button.x-date-mp-ok')){
10589             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10590             this.hideMonthPicker();
10591         }
10592         else if(pn = el.up('td.x-date-mp-month', 2)){
10593             this.mpMonths.removeClass('x-date-mp-sel');
10594             pn.addClass('x-date-mp-sel');
10595             this.mpSelMonth = pn.dom.xmonth;
10596         }
10597         else if(pn = el.up('td.x-date-mp-year', 2)){
10598             this.mpYears.removeClass('x-date-mp-sel');
10599             pn.addClass('x-date-mp-sel');
10600             this.mpSelYear = pn.dom.xyear;
10601         }
10602         else if(el.is('a.x-date-mp-prev')){
10603             this.updateMPYear(this.mpyear-10);
10604         }
10605         else if(el.is('a.x-date-mp-next')){
10606             this.updateMPYear(this.mpyear+10);
10607         }
10608     },
10609
10610     onMonthDblClick : function(e, t){
10611         e.stopEvent();
10612         var el = new Roo.Element(t), pn;
10613         if(pn = el.up('td.x-date-mp-month', 2)){
10614             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10615             this.hideMonthPicker();
10616         }
10617         else if(pn = el.up('td.x-date-mp-year', 2)){
10618             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10619             this.hideMonthPicker();
10620         }
10621     },
10622
10623     hideMonthPicker : function(disableAnim){
10624         if(this.monthPicker){
10625             if(disableAnim === true){
10626                 this.monthPicker.hide();
10627             }else{
10628                 this.monthPicker.slideOut('t', {duration:.2});
10629             }
10630         }
10631     },
10632
10633     // private
10634     showPrevMonth : function(e){
10635         this.update(this.activeDate.add("mo", -1));
10636     },
10637
10638     // private
10639     showNextMonth : function(e){
10640         this.update(this.activeDate.add("mo", 1));
10641     },
10642
10643     // private
10644     showPrevYear : function(){
10645         this.update(this.activeDate.add("y", -1));
10646     },
10647
10648     // private
10649     showNextYear : function(){
10650         this.update(this.activeDate.add("y", 1));
10651     },
10652
10653     // private
10654     handleMouseWheel : function(e){
10655         var delta = e.getWheelDelta();
10656         if(delta > 0){
10657             this.showPrevMonth();
10658             e.stopEvent();
10659         } else if(delta < 0){
10660             this.showNextMonth();
10661             e.stopEvent();
10662         }
10663     },
10664
10665     // private
10666     handleDateClick : function(e, t){
10667         e.stopEvent();
10668         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10669             this.setValue(new Date(t.dateValue));
10670             this.fireEvent("select", this, this.value);
10671         }
10672     },
10673
10674     // private
10675     selectToday : function(){
10676         this.setValue(new Date().clearTime());
10677         this.fireEvent("select", this, this.value);
10678     },
10679
10680     // private
10681     update : function(date)
10682     {
10683         var vd = this.activeDate;
10684         this.activeDate = date;
10685         if(vd && this.el){
10686             var t = date.getTime();
10687             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10688                 this.cells.removeClass("x-date-selected");
10689                 this.cells.each(function(c){
10690                    if(c.dom.firstChild.dateValue == t){
10691                        c.addClass("x-date-selected");
10692                        setTimeout(function(){
10693                             try{c.dom.firstChild.focus();}catch(e){}
10694                        }, 50);
10695                        return false;
10696                    }
10697                 });
10698                 return;
10699             }
10700         }
10701         
10702         var days = date.getDaysInMonth();
10703         var firstOfMonth = date.getFirstDateOfMonth();
10704         var startingPos = firstOfMonth.getDay()-this.startDay;
10705
10706         if(startingPos <= this.startDay){
10707             startingPos += 7;
10708         }
10709
10710         var pm = date.add("mo", -1);
10711         var prevStart = pm.getDaysInMonth()-startingPos;
10712
10713         var cells = this.cells.elements;
10714         var textEls = this.textNodes;
10715         days += startingPos;
10716
10717         // convert everything to numbers so it's fast
10718         var day = 86400000;
10719         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10720         var today = new Date().clearTime().getTime();
10721         var sel = date.clearTime().getTime();
10722         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10723         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10724         var ddMatch = this.disabledDatesRE;
10725         var ddText = this.disabledDatesText;
10726         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10727         var ddaysText = this.disabledDaysText;
10728         var format = this.format;
10729
10730         var setCellClass = function(cal, cell){
10731             cell.title = "";
10732             var t = d.getTime();
10733             cell.firstChild.dateValue = t;
10734             if(t == today){
10735                 cell.className += " x-date-today";
10736                 cell.title = cal.todayText;
10737             }
10738             if(t == sel){
10739                 cell.className += " x-date-selected";
10740                 setTimeout(function(){
10741                     try{cell.firstChild.focus();}catch(e){}
10742                 }, 50);
10743             }
10744             // disabling
10745             if(t < min) {
10746                 cell.className = " x-date-disabled";
10747                 cell.title = cal.minText;
10748                 return;
10749             }
10750             if(t > max) {
10751                 cell.className = " x-date-disabled";
10752                 cell.title = cal.maxText;
10753                 return;
10754             }
10755             if(ddays){
10756                 if(ddays.indexOf(d.getDay()) != -1){
10757                     cell.title = ddaysText;
10758                     cell.className = " x-date-disabled";
10759                 }
10760             }
10761             if(ddMatch && format){
10762                 var fvalue = d.dateFormat(format);
10763                 if(ddMatch.test(fvalue)){
10764                     cell.title = ddText.replace("%0", fvalue);
10765                     cell.className = " x-date-disabled";
10766                 }
10767             }
10768         };
10769
10770         var i = 0;
10771         for(; i < startingPos; i++) {
10772             textEls[i].innerHTML = (++prevStart);
10773             d.setDate(d.getDate()+1);
10774             cells[i].className = "x-date-prevday";
10775             setCellClass(this, cells[i]);
10776         }
10777         for(; i < days; i++){
10778             intDay = i - startingPos + 1;
10779             textEls[i].innerHTML = (intDay);
10780             d.setDate(d.getDate()+1);
10781             cells[i].className = "x-date-active";
10782             setCellClass(this, cells[i]);
10783         }
10784         var extraDays = 0;
10785         for(; i < 42; i++) {
10786              textEls[i].innerHTML = (++extraDays);
10787              d.setDate(d.getDate()+1);
10788              cells[i].className = "x-date-nextday";
10789              setCellClass(this, cells[i]);
10790         }
10791
10792         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10793         this.fireEvent('monthchange', this, date);
10794         
10795         if(!this.internalRender){
10796             var main = this.el.dom.firstChild;
10797             var w = main.offsetWidth;
10798             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10799             Roo.fly(main).setWidth(w);
10800             this.internalRender = true;
10801             // opera does not respect the auto grow header center column
10802             // then, after it gets a width opera refuses to recalculate
10803             // without a second pass
10804             if(Roo.isOpera && !this.secondPass){
10805                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10806                 this.secondPass = true;
10807                 this.update.defer(10, this, [date]);
10808             }
10809         }
10810         
10811         
10812     }
10813 });        /*
10814  * Based on:
10815  * Ext JS Library 1.1.1
10816  * Copyright(c) 2006-2007, Ext JS, LLC.
10817  *
10818  * Originally Released Under LGPL - original licence link has changed is not relivant.
10819  *
10820  * Fork - LGPL
10821  * <script type="text/javascript">
10822  */
10823 /**
10824  * @class Roo.TabPanel
10825  * @extends Roo.util.Observable
10826  * A lightweight tab container.
10827  * <br><br>
10828  * Usage:
10829  * <pre><code>
10830 // basic tabs 1, built from existing content
10831 var tabs = new Roo.TabPanel("tabs1");
10832 tabs.addTab("script", "View Script");
10833 tabs.addTab("markup", "View Markup");
10834 tabs.activate("script");
10835
10836 // more advanced tabs, built from javascript
10837 var jtabs = new Roo.TabPanel("jtabs");
10838 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10839
10840 // set up the UpdateManager
10841 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10842 var updater = tab2.getUpdateManager();
10843 updater.setDefaultUrl("ajax1.htm");
10844 tab2.on('activate', updater.refresh, updater, true);
10845
10846 // Use setUrl for Ajax loading
10847 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10848 tab3.setUrl("ajax2.htm", null, true);
10849
10850 // Disabled tab
10851 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10852 tab4.disable();
10853
10854 jtabs.activate("jtabs-1");
10855  * </code></pre>
10856  * @constructor
10857  * Create a new TabPanel.
10858  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10859  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10860  */
10861 Roo.TabPanel = function(container, config){
10862     /**
10863     * The container element for this TabPanel.
10864     * @type Roo.Element
10865     */
10866     this.el = Roo.get(container, true);
10867     if(config){
10868         if(typeof config == "boolean"){
10869             this.tabPosition = config ? "bottom" : "top";
10870         }else{
10871             Roo.apply(this, config);
10872         }
10873     }
10874     if(this.tabPosition == "bottom"){
10875         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10876         this.el.addClass("x-tabs-bottom");
10877     }
10878     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10879     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10880     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10881     if(Roo.isIE){
10882         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10883     }
10884     if(this.tabPosition != "bottom"){
10885         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10886          * @type Roo.Element
10887          */
10888         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10889         this.el.addClass("x-tabs-top");
10890     }
10891     this.items = [];
10892
10893     this.bodyEl.setStyle("position", "relative");
10894
10895     this.active = null;
10896     this.activateDelegate = this.activate.createDelegate(this);
10897
10898     this.addEvents({
10899         /**
10900          * @event tabchange
10901          * Fires when the active tab changes
10902          * @param {Roo.TabPanel} this
10903          * @param {Roo.TabPanelItem} activePanel The new active tab
10904          */
10905         "tabchange": true,
10906         /**
10907          * @event beforetabchange
10908          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10909          * @param {Roo.TabPanel} this
10910          * @param {Object} e Set cancel to true on this object to cancel the tab change
10911          * @param {Roo.TabPanelItem} tab The tab being changed to
10912          */
10913         "beforetabchange" : true
10914     });
10915
10916     Roo.EventManager.onWindowResize(this.onResize, this);
10917     this.cpad = this.el.getPadding("lr");
10918     this.hiddenCount = 0;
10919
10920
10921     // toolbar on the tabbar support...
10922     if (this.toolbar) {
10923         var tcfg = this.toolbar;
10924         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10925         this.toolbar = new Roo.Toolbar(tcfg);
10926         if (Roo.isSafari) {
10927             var tbl = tcfg.container.child('table', true);
10928             tbl.setAttribute('width', '100%');
10929         }
10930         
10931     }
10932    
10933
10934
10935     Roo.TabPanel.superclass.constructor.call(this);
10936 };
10937
10938 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10939     /*
10940      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10941      */
10942     tabPosition : "top",
10943     /*
10944      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10945      */
10946     currentTabWidth : 0,
10947     /*
10948      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10949      */
10950     minTabWidth : 40,
10951     /*
10952      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10953      */
10954     maxTabWidth : 250,
10955     /*
10956      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10957      */
10958     preferredTabWidth : 175,
10959     /*
10960      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10961      */
10962     resizeTabs : false,
10963     /*
10964      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10965      */
10966     monitorResize : true,
10967     /*
10968      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10969      */
10970     toolbar : false,
10971
10972     /**
10973      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10974      * @param {String} id The id of the div to use <b>or create</b>
10975      * @param {String} text The text for the tab
10976      * @param {String} content (optional) Content to put in the TabPanelItem body
10977      * @param {Boolean} closable (optional) True to create a close icon on the tab
10978      * @return {Roo.TabPanelItem} The created TabPanelItem
10979      */
10980     addTab : function(id, text, content, closable){
10981         var item = new Roo.TabPanelItem(this, id, text, closable);
10982         this.addTabItem(item);
10983         if(content){
10984             item.setContent(content);
10985         }
10986         return item;
10987     },
10988
10989     /**
10990      * Returns the {@link Roo.TabPanelItem} with the specified id/index
10991      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
10992      * @return {Roo.TabPanelItem}
10993      */
10994     getTab : function(id){
10995         return this.items[id];
10996     },
10997
10998     /**
10999      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11000      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11001      */
11002     hideTab : function(id){
11003         var t = this.items[id];
11004         if(!t.isHidden()){
11005            t.setHidden(true);
11006            this.hiddenCount++;
11007            this.autoSizeTabs();
11008         }
11009     },
11010
11011     /**
11012      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11013      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11014      */
11015     unhideTab : function(id){
11016         var t = this.items[id];
11017         if(t.isHidden()){
11018            t.setHidden(false);
11019            this.hiddenCount--;
11020            this.autoSizeTabs();
11021         }
11022     },
11023
11024     /**
11025      * Adds an existing {@link Roo.TabPanelItem}.
11026      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11027      */
11028     addTabItem : function(item){
11029         this.items[item.id] = item;
11030         this.items.push(item);
11031         if(this.resizeTabs){
11032            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11033            this.autoSizeTabs();
11034         }else{
11035             item.autoSize();
11036         }
11037     },
11038
11039     /**
11040      * Removes a {@link Roo.TabPanelItem}.
11041      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11042      */
11043     removeTab : function(id){
11044         var items = this.items;
11045         var tab = items[id];
11046         if(!tab) { return; }
11047         var index = items.indexOf(tab);
11048         if(this.active == tab && items.length > 1){
11049             var newTab = this.getNextAvailable(index);
11050             if(newTab) {
11051                 newTab.activate();
11052             }
11053         }
11054         this.stripEl.dom.removeChild(tab.pnode.dom);
11055         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11056             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11057         }
11058         items.splice(index, 1);
11059         delete this.items[tab.id];
11060         tab.fireEvent("close", tab);
11061         tab.purgeListeners();
11062         this.autoSizeTabs();
11063     },
11064
11065     getNextAvailable : function(start){
11066         var items = this.items;
11067         var index = start;
11068         // look for a next tab that will slide over to
11069         // replace the one being removed
11070         while(index < items.length){
11071             var item = items[++index];
11072             if(item && !item.isHidden()){
11073                 return item;
11074             }
11075         }
11076         // if one isn't found select the previous tab (on the left)
11077         index = start;
11078         while(index >= 0){
11079             var item = items[--index];
11080             if(item && !item.isHidden()){
11081                 return item;
11082             }
11083         }
11084         return null;
11085     },
11086
11087     /**
11088      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11089      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11090      */
11091     disableTab : function(id){
11092         var tab = this.items[id];
11093         if(tab && this.active != tab){
11094             tab.disable();
11095         }
11096     },
11097
11098     /**
11099      * Enables a {@link Roo.TabPanelItem} that is disabled.
11100      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11101      */
11102     enableTab : function(id){
11103         var tab = this.items[id];
11104         tab.enable();
11105     },
11106
11107     /**
11108      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11109      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11110      * @return {Roo.TabPanelItem} The TabPanelItem.
11111      */
11112     activate : function(id){
11113         var tab = this.items[id];
11114         if(!tab){
11115             return null;
11116         }
11117         if(tab == this.active || tab.disabled){
11118             return tab;
11119         }
11120         var e = {};
11121         this.fireEvent("beforetabchange", this, e, tab);
11122         if(e.cancel !== true && !tab.disabled){
11123             if(this.active){
11124                 this.active.hide();
11125             }
11126             this.active = this.items[id];
11127             this.active.show();
11128             this.fireEvent("tabchange", this, this.active);
11129         }
11130         return tab;
11131     },
11132
11133     /**
11134      * Gets the active {@link Roo.TabPanelItem}.
11135      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11136      */
11137     getActiveTab : function(){
11138         return this.active;
11139     },
11140
11141     /**
11142      * Updates the tab body element to fit the height of the container element
11143      * for overflow scrolling
11144      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11145      */
11146     syncHeight : function(targetHeight){
11147         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11148         var bm = this.bodyEl.getMargins();
11149         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11150         this.bodyEl.setHeight(newHeight);
11151         return newHeight;
11152     },
11153
11154     onResize : function(){
11155         if(this.monitorResize){
11156             this.autoSizeTabs();
11157         }
11158     },
11159
11160     /**
11161      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11162      */
11163     beginUpdate : function(){
11164         this.updating = true;
11165     },
11166
11167     /**
11168      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11169      */
11170     endUpdate : function(){
11171         this.updating = false;
11172         this.autoSizeTabs();
11173     },
11174
11175     /**
11176      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11177      */
11178     autoSizeTabs : function(){
11179         var count = this.items.length;
11180         var vcount = count - this.hiddenCount;
11181         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11182         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11183         var availWidth = Math.floor(w / vcount);
11184         var b = this.stripBody;
11185         if(b.getWidth() > w){
11186             var tabs = this.items;
11187             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11188             if(availWidth < this.minTabWidth){
11189                 /*if(!this.sleft){    // incomplete scrolling code
11190                     this.createScrollButtons();
11191                 }
11192                 this.showScroll();
11193                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11194             }
11195         }else{
11196             if(this.currentTabWidth < this.preferredTabWidth){
11197                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11198             }
11199         }
11200     },
11201
11202     /**
11203      * Returns the number of tabs in this TabPanel.
11204      * @return {Number}
11205      */
11206      getCount : function(){
11207          return this.items.length;
11208      },
11209
11210     /**
11211      * Resizes all the tabs to the passed width
11212      * @param {Number} The new width
11213      */
11214     setTabWidth : function(width){
11215         this.currentTabWidth = width;
11216         for(var i = 0, len = this.items.length; i < len; i++) {
11217                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11218         }
11219     },
11220
11221     /**
11222      * Destroys this TabPanel
11223      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11224      */
11225     destroy : function(removeEl){
11226         Roo.EventManager.removeResizeListener(this.onResize, this);
11227         for(var i = 0, len = this.items.length; i < len; i++){
11228             this.items[i].purgeListeners();
11229         }
11230         if(removeEl === true){
11231             this.el.update("");
11232             this.el.remove();
11233         }
11234     }
11235 });
11236
11237 /**
11238  * @class Roo.TabPanelItem
11239  * @extends Roo.util.Observable
11240  * Represents an individual item (tab plus body) in a TabPanel.
11241  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11242  * @param {String} id The id of this TabPanelItem
11243  * @param {String} text The text for the tab of this TabPanelItem
11244  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11245  */
11246 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11247     /**
11248      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11249      * @type Roo.TabPanel
11250      */
11251     this.tabPanel = tabPanel;
11252     /**
11253      * The id for this TabPanelItem
11254      * @type String
11255      */
11256     this.id = id;
11257     /** @private */
11258     this.disabled = false;
11259     /** @private */
11260     this.text = text;
11261     /** @private */
11262     this.loaded = false;
11263     this.closable = closable;
11264
11265     /**
11266      * The body element for this TabPanelItem.
11267      * @type Roo.Element
11268      */
11269     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11270     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11271     this.bodyEl.setStyle("display", "block");
11272     this.bodyEl.setStyle("zoom", "1");
11273     this.hideAction();
11274
11275     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11276     /** @private */
11277     this.el = Roo.get(els.el, true);
11278     this.inner = Roo.get(els.inner, true);
11279     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11280     this.pnode = Roo.get(els.el.parentNode, true);
11281     this.el.on("mousedown", this.onTabMouseDown, this);
11282     this.el.on("click", this.onTabClick, this);
11283     /** @private */
11284     if(closable){
11285         var c = Roo.get(els.close, true);
11286         c.dom.title = this.closeText;
11287         c.addClassOnOver("close-over");
11288         c.on("click", this.closeClick, this);
11289      }
11290
11291     this.addEvents({
11292          /**
11293          * @event activate
11294          * Fires when this tab becomes the active tab.
11295          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11296          * @param {Roo.TabPanelItem} this
11297          */
11298         "activate": true,
11299         /**
11300          * @event beforeclose
11301          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11302          * @param {Roo.TabPanelItem} this
11303          * @param {Object} e Set cancel to true on this object to cancel the close.
11304          */
11305         "beforeclose": true,
11306         /**
11307          * @event close
11308          * Fires when this tab is closed.
11309          * @param {Roo.TabPanelItem} this
11310          */
11311          "close": true,
11312         /**
11313          * @event deactivate
11314          * Fires when this tab is no longer the active tab.
11315          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11316          * @param {Roo.TabPanelItem} this
11317          */
11318          "deactivate" : true
11319     });
11320     this.hidden = false;
11321
11322     Roo.TabPanelItem.superclass.constructor.call(this);
11323 };
11324
11325 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11326     purgeListeners : function(){
11327        Roo.util.Observable.prototype.purgeListeners.call(this);
11328        this.el.removeAllListeners();
11329     },
11330     /**
11331      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11332      */
11333     show : function(){
11334         this.pnode.addClass("on");
11335         this.showAction();
11336         if(Roo.isOpera){
11337             this.tabPanel.stripWrap.repaint();
11338         }
11339         this.fireEvent("activate", this.tabPanel, this);
11340     },
11341
11342     /**
11343      * Returns true if this tab is the active tab.
11344      * @return {Boolean}
11345      */
11346     isActive : function(){
11347         return this.tabPanel.getActiveTab() == this;
11348     },
11349
11350     /**
11351      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11352      */
11353     hide : function(){
11354         this.pnode.removeClass("on");
11355         this.hideAction();
11356         this.fireEvent("deactivate", this.tabPanel, this);
11357     },
11358
11359     hideAction : function(){
11360         this.bodyEl.hide();
11361         this.bodyEl.setStyle("position", "absolute");
11362         this.bodyEl.setLeft("-20000px");
11363         this.bodyEl.setTop("-20000px");
11364     },
11365
11366     showAction : function(){
11367         this.bodyEl.setStyle("position", "relative");
11368         this.bodyEl.setTop("");
11369         this.bodyEl.setLeft("");
11370         this.bodyEl.show();
11371     },
11372
11373     /**
11374      * Set the tooltip for the tab.
11375      * @param {String} tooltip The tab's tooltip
11376      */
11377     setTooltip : function(text){
11378         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11379             this.textEl.dom.qtip = text;
11380             this.textEl.dom.removeAttribute('title');
11381         }else{
11382             this.textEl.dom.title = text;
11383         }
11384     },
11385
11386     onTabClick : function(e){
11387         e.preventDefault();
11388         this.tabPanel.activate(this.id);
11389     },
11390
11391     onTabMouseDown : function(e){
11392         e.preventDefault();
11393         this.tabPanel.activate(this.id);
11394     },
11395
11396     getWidth : function(){
11397         return this.inner.getWidth();
11398     },
11399
11400     setWidth : function(width){
11401         var iwidth = width - this.pnode.getPadding("lr");
11402         this.inner.setWidth(iwidth);
11403         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11404         this.pnode.setWidth(width);
11405     },
11406
11407     /**
11408      * Show or hide the tab
11409      * @param {Boolean} hidden True to hide or false to show.
11410      */
11411     setHidden : function(hidden){
11412         this.hidden = hidden;
11413         this.pnode.setStyle("display", hidden ? "none" : "");
11414     },
11415
11416     /**
11417      * Returns true if this tab is "hidden"
11418      * @return {Boolean}
11419      */
11420     isHidden : function(){
11421         return this.hidden;
11422     },
11423
11424     /**
11425      * Returns the text for this tab
11426      * @return {String}
11427      */
11428     getText : function(){
11429         return this.text;
11430     },
11431
11432     autoSize : function(){
11433         //this.el.beginMeasure();
11434         this.textEl.setWidth(1);
11435         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11436         //this.el.endMeasure();
11437     },
11438
11439     /**
11440      * Sets the text for the tab (Note: this also sets the tooltip text)
11441      * @param {String} text The tab's text and tooltip
11442      */
11443     setText : function(text){
11444         this.text = text;
11445         this.textEl.update(text);
11446         this.setTooltip(text);
11447         if(!this.tabPanel.resizeTabs){
11448             this.autoSize();
11449         }
11450     },
11451     /**
11452      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11453      */
11454     activate : function(){
11455         this.tabPanel.activate(this.id);
11456     },
11457
11458     /**
11459      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11460      */
11461     disable : function(){
11462         if(this.tabPanel.active != this){
11463             this.disabled = true;
11464             this.pnode.addClass("disabled");
11465         }
11466     },
11467
11468     /**
11469      * Enables this TabPanelItem if it was previously disabled.
11470      */
11471     enable : function(){
11472         this.disabled = false;
11473         this.pnode.removeClass("disabled");
11474     },
11475
11476     /**
11477      * Sets the content for this TabPanelItem.
11478      * @param {String} content The content
11479      * @param {Boolean} loadScripts true to look for and load scripts
11480      */
11481     setContent : function(content, loadScripts){
11482         this.bodyEl.update(content, loadScripts);
11483     },
11484
11485     /**
11486      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11487      * @return {Roo.UpdateManager} The UpdateManager
11488      */
11489     getUpdateManager : function(){
11490         return this.bodyEl.getUpdateManager();
11491     },
11492
11493     /**
11494      * Set a URL to be used to load the content for this TabPanelItem.
11495      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11496      * @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)
11497      * @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)
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     setUrl : function(url, params, loadOnce){
11501         if(this.refreshDelegate){
11502             this.un('activate', this.refreshDelegate);
11503         }
11504         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11505         this.on("activate", this.refreshDelegate);
11506         return this.bodyEl.getUpdateManager();
11507     },
11508
11509     /** @private */
11510     _handleRefresh : function(url, params, loadOnce){
11511         if(!loadOnce || !this.loaded){
11512             var updater = this.bodyEl.getUpdateManager();
11513             updater.update(url, params, this._setLoaded.createDelegate(this));
11514         }
11515     },
11516
11517     /**
11518      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11519      *   Will fail silently if the setUrl method has not been called.
11520      *   This does not activate the panel, just updates its content.
11521      */
11522     refresh : function(){
11523         if(this.refreshDelegate){
11524            this.loaded = false;
11525            this.refreshDelegate();
11526         }
11527     },
11528
11529     /** @private */
11530     _setLoaded : function(){
11531         this.loaded = true;
11532     },
11533
11534     /** @private */
11535     closeClick : function(e){
11536         var o = {};
11537         e.stopEvent();
11538         this.fireEvent("beforeclose", this, o);
11539         if(o.cancel !== true){
11540             this.tabPanel.removeTab(this.id);
11541         }
11542     },
11543     /**
11544      * The text displayed in the tooltip for the close icon.
11545      * @type String
11546      */
11547     closeText : "Close this tab"
11548 });
11549
11550 /** @private */
11551 Roo.TabPanel.prototype.createStrip = function(container){
11552     var strip = document.createElement("div");
11553     strip.className = "x-tabs-wrap";
11554     container.appendChild(strip);
11555     return strip;
11556 };
11557 /** @private */
11558 Roo.TabPanel.prototype.createStripList = function(strip){
11559     // div wrapper for retard IE
11560     // returns the "tr" element.
11561     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11562         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11563         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11564     return strip.firstChild.firstChild.firstChild.firstChild;
11565 };
11566 /** @private */
11567 Roo.TabPanel.prototype.createBody = function(container){
11568     var body = document.createElement("div");
11569     Roo.id(body, "tab-body");
11570     Roo.fly(body).addClass("x-tabs-body");
11571     container.appendChild(body);
11572     return body;
11573 };
11574 /** @private */
11575 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11576     var body = Roo.getDom(id);
11577     if(!body){
11578         body = document.createElement("div");
11579         body.id = id;
11580     }
11581     Roo.fly(body).addClass("x-tabs-item-body");
11582     bodyEl.insertBefore(body, bodyEl.firstChild);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11587     var td = document.createElement("td");
11588     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11589     //stripEl.appendChild(td);
11590     if(closable){
11591         td.className = "x-tabs-closable";
11592         if(!this.closeTpl){
11593             this.closeTpl = new Roo.Template(
11594                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11595                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11596                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11597             );
11598         }
11599         var el = this.closeTpl.overwrite(td, {"text": text});
11600         var close = el.getElementsByTagName("div")[0];
11601         var inner = el.getElementsByTagName("em")[0];
11602         return {"el": el, "close": close, "inner": inner};
11603     } else {
11604         if(!this.tabTpl){
11605             this.tabTpl = new Roo.Template(
11606                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11607                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11608             );
11609         }
11610         var el = this.tabTpl.overwrite(td, {"text": text});
11611         var inner = el.getElementsByTagName("em")[0];
11612         return {"el": el, "inner": inner};
11613     }
11614 };/*
11615  * Based on:
11616  * Ext JS Library 1.1.1
11617  * Copyright(c) 2006-2007, Ext JS, LLC.
11618  *
11619  * Originally Released Under LGPL - original licence link has changed is not relivant.
11620  *
11621  * Fork - LGPL
11622  * <script type="text/javascript">
11623  */
11624
11625 /**
11626  * @class Roo.Button
11627  * @extends Roo.util.Observable
11628  * Simple Button class
11629  * @cfg {String} text The button text
11630  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11631  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11632  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11633  * @cfg {Object} scope The scope of the handler
11634  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11635  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11636  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11637  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11638  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11639  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11640    applies if enableToggle = true)
11641  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11642  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11643   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11644  * @constructor
11645  * Create a new button
11646  * @param {Object} config The config object
11647  */
11648 Roo.Button = function(renderTo, config)
11649 {
11650     if (!config) {
11651         config = renderTo;
11652         renderTo = config.renderTo || false;
11653     }
11654     
11655     Roo.apply(this, config);
11656     this.addEvents({
11657         /**
11658              * @event click
11659              * Fires when this button is clicked
11660              * @param {Button} this
11661              * @param {EventObject} e The click event
11662              */
11663             "click" : true,
11664         /**
11665              * @event toggle
11666              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11667              * @param {Button} this
11668              * @param {Boolean} pressed
11669              */
11670             "toggle" : true,
11671         /**
11672              * @event mouseover
11673              * Fires when the mouse hovers over the button
11674              * @param {Button} this
11675              * @param {Event} e The event object
11676              */
11677         'mouseover' : true,
11678         /**
11679              * @event mouseout
11680              * Fires when the mouse exits the button
11681              * @param {Button} this
11682              * @param {Event} e The event object
11683              */
11684         'mouseout': true,
11685          /**
11686              * @event render
11687              * Fires when the button is rendered
11688              * @param {Button} this
11689              */
11690         'render': true
11691     });
11692     if(this.menu){
11693         this.menu = Roo.menu.MenuMgr.get(this.menu);
11694     }
11695     // register listeners first!!  - so render can be captured..
11696     Roo.util.Observable.call(this);
11697     if(renderTo){
11698         this.render(renderTo);
11699     }
11700     
11701   
11702 };
11703
11704 Roo.extend(Roo.Button, Roo.util.Observable, {
11705     /**
11706      * 
11707      */
11708     
11709     /**
11710      * Read-only. True if this button is hidden
11711      * @type Boolean
11712      */
11713     hidden : false,
11714     /**
11715      * Read-only. True if this button is disabled
11716      * @type Boolean
11717      */
11718     disabled : false,
11719     /**
11720      * Read-only. True if this button is pressed (only if enableToggle = true)
11721      * @type Boolean
11722      */
11723     pressed : false,
11724
11725     /**
11726      * @cfg {Number} tabIndex 
11727      * The DOM tabIndex for this button (defaults to undefined)
11728      */
11729     tabIndex : undefined,
11730
11731     /**
11732      * @cfg {Boolean} enableToggle
11733      * True to enable pressed/not pressed toggling (defaults to false)
11734      */
11735     enableToggle: false,
11736     /**
11737      * @cfg {Mixed} menu
11738      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11739      */
11740     menu : undefined,
11741     /**
11742      * @cfg {String} menuAlign
11743      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11744      */
11745     menuAlign : "tl-bl?",
11746
11747     /**
11748      * @cfg {String} iconCls
11749      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11750      */
11751     iconCls : undefined,
11752     /**
11753      * @cfg {String} type
11754      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11755      */
11756     type : 'button',
11757
11758     // private
11759     menuClassTarget: 'tr',
11760
11761     /**
11762      * @cfg {String} clickEvent
11763      * The type of event to map to the button's event handler (defaults to 'click')
11764      */
11765     clickEvent : 'click',
11766
11767     /**
11768      * @cfg {Boolean} handleMouseEvents
11769      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11770      */
11771     handleMouseEvents : true,
11772
11773     /**
11774      * @cfg {String} tooltipType
11775      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11776      */
11777     tooltipType : 'qtip',
11778
11779     /**
11780      * @cfg {String} cls
11781      * A CSS class to apply to the button's main element.
11782      */
11783     
11784     /**
11785      * @cfg {Roo.Template} template (Optional)
11786      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11787      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11788      * require code modifications if required elements (e.g. a button) aren't present.
11789      */
11790
11791     // private
11792     render : function(renderTo){
11793         var btn;
11794         if(this.hideParent){
11795             this.parentEl = Roo.get(renderTo);
11796         }
11797         if(!this.dhconfig){
11798             if(!this.template){
11799                 if(!Roo.Button.buttonTemplate){
11800                     // hideous table template
11801                     Roo.Button.buttonTemplate = new Roo.Template(
11802                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11803                         '<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>',
11804                         "</tr></tbody></table>");
11805                 }
11806                 this.template = Roo.Button.buttonTemplate;
11807             }
11808             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11809             var btnEl = btn.child("button:first");
11810             btnEl.on('focus', this.onFocus, this);
11811             btnEl.on('blur', this.onBlur, this);
11812             if(this.cls){
11813                 btn.addClass(this.cls);
11814             }
11815             if(this.icon){
11816                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11817             }
11818             if(this.iconCls){
11819                 btnEl.addClass(this.iconCls);
11820                 if(!this.cls){
11821                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11822                 }
11823             }
11824             if(this.tabIndex !== undefined){
11825                 btnEl.dom.tabIndex = this.tabIndex;
11826             }
11827             if(this.tooltip){
11828                 if(typeof this.tooltip == 'object'){
11829                     Roo.QuickTips.tips(Roo.apply({
11830                           target: btnEl.id
11831                     }, this.tooltip));
11832                 } else {
11833                     btnEl.dom[this.tooltipType] = this.tooltip;
11834                 }
11835             }
11836         }else{
11837             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11838         }
11839         this.el = btn;
11840         if(this.id){
11841             this.el.dom.id = this.el.id = this.id;
11842         }
11843         if(this.menu){
11844             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11845             this.menu.on("show", this.onMenuShow, this);
11846             this.menu.on("hide", this.onMenuHide, this);
11847         }
11848         btn.addClass("x-btn");
11849         if(Roo.isIE && !Roo.isIE7){
11850             this.autoWidth.defer(1, this);
11851         }else{
11852             this.autoWidth();
11853         }
11854         if(this.handleMouseEvents){
11855             btn.on("mouseover", this.onMouseOver, this);
11856             btn.on("mouseout", this.onMouseOut, this);
11857             btn.on("mousedown", this.onMouseDown, this);
11858         }
11859         btn.on(this.clickEvent, this.onClick, this);
11860         //btn.on("mouseup", this.onMouseUp, this);
11861         if(this.hidden){
11862             this.hide();
11863         }
11864         if(this.disabled){
11865             this.disable();
11866         }
11867         Roo.ButtonToggleMgr.register(this);
11868         if(this.pressed){
11869             this.el.addClass("x-btn-pressed");
11870         }
11871         if(this.repeat){
11872             var repeater = new Roo.util.ClickRepeater(btn,
11873                 typeof this.repeat == "object" ? this.repeat : {}
11874             );
11875             repeater.on("click", this.onClick,  this);
11876         }
11877         
11878         this.fireEvent('render', this);
11879         
11880     },
11881     /**
11882      * Returns the button's underlying element
11883      * @return {Roo.Element} The element
11884      */
11885     getEl : function(){
11886         return this.el;  
11887     },
11888     
11889     /**
11890      * Destroys this Button and removes any listeners.
11891      */
11892     destroy : function(){
11893         Roo.ButtonToggleMgr.unregister(this);
11894         this.el.removeAllListeners();
11895         this.purgeListeners();
11896         this.el.remove();
11897     },
11898
11899     // private
11900     autoWidth : function(){
11901         if(this.el){
11902             this.el.setWidth("auto");
11903             if(Roo.isIE7 && Roo.isStrict){
11904                 var ib = this.el.child('button');
11905                 if(ib && ib.getWidth() > 20){
11906                     ib.clip();
11907                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11908                 }
11909             }
11910             if(this.minWidth){
11911                 if(this.hidden){
11912                     this.el.beginMeasure();
11913                 }
11914                 if(this.el.getWidth() < this.minWidth){
11915                     this.el.setWidth(this.minWidth);
11916                 }
11917                 if(this.hidden){
11918                     this.el.endMeasure();
11919                 }
11920             }
11921         }
11922     },
11923
11924     /**
11925      * Assigns this button's click handler
11926      * @param {Function} handler The function to call when the button is clicked
11927      * @param {Object} scope (optional) Scope for the function passed in
11928      */
11929     setHandler : function(handler, scope){
11930         this.handler = handler;
11931         this.scope = scope;  
11932     },
11933     
11934     /**
11935      * Sets this button's text
11936      * @param {String} text The button text
11937      */
11938     setText : function(text){
11939         this.text = text;
11940         if(this.el){
11941             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11942         }
11943         this.autoWidth();
11944     },
11945     
11946     /**
11947      * Gets the text for this button
11948      * @return {String} The button text
11949      */
11950     getText : function(){
11951         return this.text;  
11952     },
11953     
11954     /**
11955      * Show this button
11956      */
11957     show: function(){
11958         this.hidden = false;
11959         if(this.el){
11960             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11961         }
11962     },
11963     
11964     /**
11965      * Hide this button
11966      */
11967     hide: function(){
11968         this.hidden = true;
11969         if(this.el){
11970             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11971         }
11972     },
11973     
11974     /**
11975      * Convenience function for boolean show/hide
11976      * @param {Boolean} visible True to show, false to hide
11977      */
11978     setVisible: function(visible){
11979         if(visible) {
11980             this.show();
11981         }else{
11982             this.hide();
11983         }
11984     },
11985     
11986     /**
11987      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11988      * @param {Boolean} state (optional) Force a particular state
11989      */
11990     toggle : function(state){
11991         state = state === undefined ? !this.pressed : state;
11992         if(state != this.pressed){
11993             if(state){
11994                 this.el.addClass("x-btn-pressed");
11995                 this.pressed = true;
11996                 this.fireEvent("toggle", this, true);
11997             }else{
11998                 this.el.removeClass("x-btn-pressed");
11999                 this.pressed = false;
12000                 this.fireEvent("toggle", this, false);
12001             }
12002             if(this.toggleHandler){
12003                 this.toggleHandler.call(this.scope || this, this, state);
12004             }
12005         }
12006     },
12007     
12008     /**
12009      * Focus the button
12010      */
12011     focus : function(){
12012         this.el.child('button:first').focus();
12013     },
12014     
12015     /**
12016      * Disable this button
12017      */
12018     disable : function(){
12019         if(this.el){
12020             this.el.addClass("x-btn-disabled");
12021         }
12022         this.disabled = true;
12023     },
12024     
12025     /**
12026      * Enable this button
12027      */
12028     enable : function(){
12029         if(this.el){
12030             this.el.removeClass("x-btn-disabled");
12031         }
12032         this.disabled = false;
12033     },
12034
12035     /**
12036      * Convenience function for boolean enable/disable
12037      * @param {Boolean} enabled True to enable, false to disable
12038      */
12039     setDisabled : function(v){
12040         this[v !== true ? "enable" : "disable"]();
12041     },
12042
12043     // private
12044     onClick : function(e){
12045         if(e){
12046             e.preventDefault();
12047         }
12048         if(e.button != 0){
12049             return;
12050         }
12051         if(!this.disabled){
12052             if(this.enableToggle){
12053                 this.toggle();
12054             }
12055             if(this.menu && !this.menu.isVisible()){
12056                 this.menu.show(this.el, this.menuAlign);
12057             }
12058             this.fireEvent("click", this, e);
12059             if(this.handler){
12060                 this.el.removeClass("x-btn-over");
12061                 this.handler.call(this.scope || this, this, e);
12062             }
12063         }
12064     },
12065     // private
12066     onMouseOver : function(e){
12067         if(!this.disabled){
12068             this.el.addClass("x-btn-over");
12069             this.fireEvent('mouseover', this, e);
12070         }
12071     },
12072     // private
12073     onMouseOut : function(e){
12074         if(!e.within(this.el,  true)){
12075             this.el.removeClass("x-btn-over");
12076             this.fireEvent('mouseout', this, e);
12077         }
12078     },
12079     // private
12080     onFocus : function(e){
12081         if(!this.disabled){
12082             this.el.addClass("x-btn-focus");
12083         }
12084     },
12085     // private
12086     onBlur : function(e){
12087         this.el.removeClass("x-btn-focus");
12088     },
12089     // private
12090     onMouseDown : function(e){
12091         if(!this.disabled && e.button == 0){
12092             this.el.addClass("x-btn-click");
12093             Roo.get(document).on('mouseup', this.onMouseUp, this);
12094         }
12095     },
12096     // private
12097     onMouseUp : function(e){
12098         if(e.button == 0){
12099             this.el.removeClass("x-btn-click");
12100             Roo.get(document).un('mouseup', this.onMouseUp, this);
12101         }
12102     },
12103     // private
12104     onMenuShow : function(e){
12105         this.el.addClass("x-btn-menu-active");
12106     },
12107     // private
12108     onMenuHide : function(e){
12109         this.el.removeClass("x-btn-menu-active");
12110     }   
12111 });
12112
12113 // Private utility class used by Button
12114 Roo.ButtonToggleMgr = function(){
12115    var groups = {};
12116    
12117    function toggleGroup(btn, state){
12118        if(state){
12119            var g = groups[btn.toggleGroup];
12120            for(var i = 0, l = g.length; i < l; i++){
12121                if(g[i] != btn){
12122                    g[i].toggle(false);
12123                }
12124            }
12125        }
12126    }
12127    
12128    return {
12129        register : function(btn){
12130            if(!btn.toggleGroup){
12131                return;
12132            }
12133            var g = groups[btn.toggleGroup];
12134            if(!g){
12135                g = groups[btn.toggleGroup] = [];
12136            }
12137            g.push(btn);
12138            btn.on("toggle", toggleGroup);
12139        },
12140        
12141        unregister : function(btn){
12142            if(!btn.toggleGroup){
12143                return;
12144            }
12145            var g = groups[btn.toggleGroup];
12146            if(g){
12147                g.remove(btn);
12148                btn.un("toggle", toggleGroup);
12149            }
12150        }
12151    };
12152 }();/*
12153  * Based on:
12154  * Ext JS Library 1.1.1
12155  * Copyright(c) 2006-2007, Ext JS, LLC.
12156  *
12157  * Originally Released Under LGPL - original licence link has changed is not relivant.
12158  *
12159  * Fork - LGPL
12160  * <script type="text/javascript">
12161  */
12162  
12163 /**
12164  * @class Roo.SplitButton
12165  * @extends Roo.Button
12166  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12167  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12168  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12169  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12170  * @cfg {String} arrowTooltip The title attribute of the arrow
12171  * @constructor
12172  * Create a new menu button
12173  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12174  * @param {Object} config The config object
12175  */
12176 Roo.SplitButton = function(renderTo, config){
12177     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12178     /**
12179      * @event arrowclick
12180      * Fires when this button's arrow is clicked
12181      * @param {SplitButton} this
12182      * @param {EventObject} e The click event
12183      */
12184     this.addEvents({"arrowclick":true});
12185 };
12186
12187 Roo.extend(Roo.SplitButton, Roo.Button, {
12188     render : function(renderTo){
12189         // this is one sweet looking template!
12190         var tpl = new Roo.Template(
12191             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12192             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12193             '<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>',
12194             "</tbody></table></td><td>",
12195             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12196             '<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>',
12197             "</tbody></table></td></tr></table>"
12198         );
12199         var btn = tpl.append(renderTo, [this.text, this.type], true);
12200         var btnEl = btn.child("button");
12201         if(this.cls){
12202             btn.addClass(this.cls);
12203         }
12204         if(this.icon){
12205             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12206         }
12207         if(this.iconCls){
12208             btnEl.addClass(this.iconCls);
12209             if(!this.cls){
12210                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12211             }
12212         }
12213         this.el = btn;
12214         if(this.handleMouseEvents){
12215             btn.on("mouseover", this.onMouseOver, this);
12216             btn.on("mouseout", this.onMouseOut, this);
12217             btn.on("mousedown", this.onMouseDown, this);
12218             btn.on("mouseup", this.onMouseUp, this);
12219         }
12220         btn.on(this.clickEvent, this.onClick, this);
12221         if(this.tooltip){
12222             if(typeof this.tooltip == 'object'){
12223                 Roo.QuickTips.tips(Roo.apply({
12224                       target: btnEl.id
12225                 }, this.tooltip));
12226             } else {
12227                 btnEl.dom[this.tooltipType] = this.tooltip;
12228             }
12229         }
12230         if(this.arrowTooltip){
12231             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12232         }
12233         if(this.hidden){
12234             this.hide();
12235         }
12236         if(this.disabled){
12237             this.disable();
12238         }
12239         if(this.pressed){
12240             this.el.addClass("x-btn-pressed");
12241         }
12242         if(Roo.isIE && !Roo.isIE7){
12243             this.autoWidth.defer(1, this);
12244         }else{
12245             this.autoWidth();
12246         }
12247         if(this.menu){
12248             this.menu.on("show", this.onMenuShow, this);
12249             this.menu.on("hide", this.onMenuHide, this);
12250         }
12251         this.fireEvent('render', this);
12252     },
12253
12254     // private
12255     autoWidth : function(){
12256         if(this.el){
12257             var tbl = this.el.child("table:first");
12258             var tbl2 = this.el.child("table:last");
12259             this.el.setWidth("auto");
12260             tbl.setWidth("auto");
12261             if(Roo.isIE7 && Roo.isStrict){
12262                 var ib = this.el.child('button:first');
12263                 if(ib && ib.getWidth() > 20){
12264                     ib.clip();
12265                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12266                 }
12267             }
12268             if(this.minWidth){
12269                 if(this.hidden){
12270                     this.el.beginMeasure();
12271                 }
12272                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12273                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12274                 }
12275                 if(this.hidden){
12276                     this.el.endMeasure();
12277                 }
12278             }
12279             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12280         } 
12281     },
12282     /**
12283      * Sets this button's click handler
12284      * @param {Function} handler The function to call when the button is clicked
12285      * @param {Object} scope (optional) Scope for the function passed above
12286      */
12287     setHandler : function(handler, scope){
12288         this.handler = handler;
12289         this.scope = scope;  
12290     },
12291     
12292     /**
12293      * Sets this button's arrow click handler
12294      * @param {Function} handler The function to call when the arrow is clicked
12295      * @param {Object} scope (optional) Scope for the function passed above
12296      */
12297     setArrowHandler : function(handler, scope){
12298         this.arrowHandler = handler;
12299         this.scope = scope;  
12300     },
12301     
12302     /**
12303      * Focus the button
12304      */
12305     focus : function(){
12306         if(this.el){
12307             this.el.child("button:first").focus();
12308         }
12309     },
12310
12311     // private
12312     onClick : function(e){
12313         e.preventDefault();
12314         if(!this.disabled){
12315             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12316                 if(this.menu && !this.menu.isVisible()){
12317                     this.menu.show(this.el, this.menuAlign);
12318                 }
12319                 this.fireEvent("arrowclick", this, e);
12320                 if(this.arrowHandler){
12321                     this.arrowHandler.call(this.scope || this, this, e);
12322                 }
12323             }else{
12324                 this.fireEvent("click", this, e);
12325                 if(this.handler){
12326                     this.handler.call(this.scope || this, this, e);
12327                 }
12328             }
12329         }
12330     },
12331     // private
12332     onMouseDown : function(e){
12333         if(!this.disabled){
12334             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12335         }
12336     },
12337     // private
12338     onMouseUp : function(e){
12339         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12340     }   
12341 });
12342
12343
12344 // backwards compat
12345 Roo.MenuButton = Roo.SplitButton;/*
12346  * Based on:
12347  * Ext JS Library 1.1.1
12348  * Copyright(c) 2006-2007, Ext JS, LLC.
12349  *
12350  * Originally Released Under LGPL - original licence link has changed is not relivant.
12351  *
12352  * Fork - LGPL
12353  * <script type="text/javascript">
12354  */
12355
12356 /**
12357  * @class Roo.Toolbar
12358  * Basic Toolbar class.
12359  * @constructor
12360  * Creates a new Toolbar
12361  * @param {Object} container The config object
12362  */ 
12363 Roo.Toolbar = function(container, buttons, config)
12364 {
12365     /// old consturctor format still supported..
12366     if(container instanceof Array){ // omit the container for later rendering
12367         buttons = container;
12368         config = buttons;
12369         container = null;
12370     }
12371     if (typeof(container) == 'object' && container.xtype) {
12372         config = container;
12373         container = config.container;
12374         buttons = config.buttons || []; // not really - use items!!
12375     }
12376     var xitems = [];
12377     if (config && config.items) {
12378         xitems = config.items;
12379         delete config.items;
12380     }
12381     Roo.apply(this, config);
12382     this.buttons = buttons;
12383     
12384     if(container){
12385         this.render(container);
12386     }
12387     this.xitems = xitems;
12388     Roo.each(xitems, function(b) {
12389         this.add(b);
12390     }, this);
12391     
12392 };
12393
12394 Roo.Toolbar.prototype = {
12395     /**
12396      * @cfg {Array} items
12397      * array of button configs or elements to add (will be converted to a MixedCollection)
12398      */
12399     
12400     /**
12401      * @cfg {String/HTMLElement/Element} container
12402      * The id or element that will contain the toolbar
12403      */
12404     // private
12405     render : function(ct){
12406         this.el = Roo.get(ct);
12407         if(this.cls){
12408             this.el.addClass(this.cls);
12409         }
12410         // using a table allows for vertical alignment
12411         // 100% width is needed by Safari...
12412         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12413         this.tr = this.el.child("tr", true);
12414         var autoId = 0;
12415         this.items = new Roo.util.MixedCollection(false, function(o){
12416             return o.id || ("item" + (++autoId));
12417         });
12418         if(this.buttons){
12419             this.add.apply(this, this.buttons);
12420             delete this.buttons;
12421         }
12422     },
12423
12424     /**
12425      * Adds element(s) to the toolbar -- this function takes a variable number of 
12426      * arguments of mixed type and adds them to the toolbar.
12427      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12428      * <ul>
12429      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12430      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12431      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12432      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12433      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12434      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12435      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12436      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12437      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12438      * </ul>
12439      * @param {Mixed} arg2
12440      * @param {Mixed} etc.
12441      */
12442     add : function(){
12443         var a = arguments, l = a.length;
12444         for(var i = 0; i < l; i++){
12445             this._add(a[i]);
12446         }
12447     },
12448     // private..
12449     _add : function(el) {
12450         
12451         if (el.xtype) {
12452             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12453         }
12454         
12455         if (el.applyTo){ // some kind of form field
12456             return this.addField(el);
12457         } 
12458         if (el.render){ // some kind of Toolbar.Item
12459             return this.addItem(el);
12460         }
12461         if (typeof el == "string"){ // string
12462             if(el == "separator" || el == "-"){
12463                 return this.addSeparator();
12464             }
12465             if (el == " "){
12466                 return this.addSpacer();
12467             }
12468             if(el == "->"){
12469                 return this.addFill();
12470             }
12471             return this.addText(el);
12472             
12473         }
12474         if(el.tagName){ // element
12475             return this.addElement(el);
12476         }
12477         if(typeof el == "object"){ // must be button config?
12478             return this.addButton(el);
12479         }
12480         // and now what?!?!
12481         return false;
12482         
12483     },
12484     
12485     /**
12486      * Add an Xtype element
12487      * @param {Object} xtype Xtype Object
12488      * @return {Object} created Object
12489      */
12490     addxtype : function(e){
12491         return this.add(e);  
12492     },
12493     
12494     /**
12495      * Returns the Element for this toolbar.
12496      * @return {Roo.Element}
12497      */
12498     getEl : function(){
12499         return this.el;  
12500     },
12501     
12502     /**
12503      * Adds a separator
12504      * @return {Roo.Toolbar.Item} The separator item
12505      */
12506     addSeparator : function(){
12507         return this.addItem(new Roo.Toolbar.Separator());
12508     },
12509
12510     /**
12511      * Adds a spacer element
12512      * @return {Roo.Toolbar.Spacer} The spacer item
12513      */
12514     addSpacer : function(){
12515         return this.addItem(new Roo.Toolbar.Spacer());
12516     },
12517
12518     /**
12519      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12520      * @return {Roo.Toolbar.Fill} The fill item
12521      */
12522     addFill : function(){
12523         return this.addItem(new Roo.Toolbar.Fill());
12524     },
12525
12526     /**
12527      * Adds any standard HTML element to the toolbar
12528      * @param {String/HTMLElement/Element} el The element or id of the element to add
12529      * @return {Roo.Toolbar.Item} The element's item
12530      */
12531     addElement : function(el){
12532         return this.addItem(new Roo.Toolbar.Item(el));
12533     },
12534     /**
12535      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12536      * @type Roo.util.MixedCollection  
12537      */
12538     items : false,
12539      
12540     /**
12541      * Adds any Toolbar.Item or subclass
12542      * @param {Roo.Toolbar.Item} item
12543      * @return {Roo.Toolbar.Item} The item
12544      */
12545     addItem : function(item){
12546         var td = this.nextBlock();
12547         item.render(td);
12548         this.items.add(item);
12549         return item;
12550     },
12551     
12552     /**
12553      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12554      * @param {Object/Array} config A button config or array of configs
12555      * @return {Roo.Toolbar.Button/Array}
12556      */
12557     addButton : function(config){
12558         if(config instanceof Array){
12559             var buttons = [];
12560             for(var i = 0, len = config.length; i < len; i++) {
12561                 buttons.push(this.addButton(config[i]));
12562             }
12563             return buttons;
12564         }
12565         var b = config;
12566         if(!(config instanceof Roo.Toolbar.Button)){
12567             b = config.split ?
12568                 new Roo.Toolbar.SplitButton(config) :
12569                 new Roo.Toolbar.Button(config);
12570         }
12571         var td = this.nextBlock();
12572         b.render(td);
12573         this.items.add(b);
12574         return b;
12575     },
12576     
12577     /**
12578      * Adds text to the toolbar
12579      * @param {String} text The text to add
12580      * @return {Roo.Toolbar.Item} The element's item
12581      */
12582     addText : function(text){
12583         return this.addItem(new Roo.Toolbar.TextItem(text));
12584     },
12585     
12586     /**
12587      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12588      * @param {Number} index The index where the item is to be inserted
12589      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12590      * @return {Roo.Toolbar.Button/Item}
12591      */
12592     insertButton : function(index, item){
12593         if(item instanceof Array){
12594             var buttons = [];
12595             for(var i = 0, len = item.length; i < len; i++) {
12596                buttons.push(this.insertButton(index + i, item[i]));
12597             }
12598             return buttons;
12599         }
12600         if (!(item instanceof Roo.Toolbar.Button)){
12601            item = new Roo.Toolbar.Button(item);
12602         }
12603         var td = document.createElement("td");
12604         this.tr.insertBefore(td, this.tr.childNodes[index]);
12605         item.render(td);
12606         this.items.insert(index, item);
12607         return item;
12608     },
12609     
12610     /**
12611      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12612      * @param {Object} config
12613      * @return {Roo.Toolbar.Item} The element's item
12614      */
12615     addDom : function(config, returnEl){
12616         var td = this.nextBlock();
12617         Roo.DomHelper.overwrite(td, config);
12618         var ti = new Roo.Toolbar.Item(td.firstChild);
12619         ti.render(td);
12620         this.items.add(ti);
12621         return ti;
12622     },
12623
12624     /**
12625      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12626      * @type Roo.util.MixedCollection  
12627      */
12628     fields : false,
12629     
12630     /**
12631      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12632      * Note: the field should not have been rendered yet. For a field that has already been
12633      * rendered, use {@link #addElement}.
12634      * @param {Roo.form.Field} field
12635      * @return {Roo.ToolbarItem}
12636      */
12637      
12638       
12639     addField : function(field) {
12640         if (!this.fields) {
12641             var autoId = 0;
12642             this.fields = new Roo.util.MixedCollection(false, function(o){
12643                 return o.id || ("item" + (++autoId));
12644             });
12645
12646         }
12647         
12648         var td = this.nextBlock();
12649         field.render(td);
12650         var ti = new Roo.Toolbar.Item(td.firstChild);
12651         ti.render(td);
12652         this.items.add(ti);
12653         this.fields.add(field);
12654         return ti;
12655     },
12656     /**
12657      * Hide the toolbar
12658      * @method hide
12659      */
12660      
12661       
12662     hide : function()
12663     {
12664         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12665         this.el.child('div').hide();
12666     },
12667     /**
12668      * Show the toolbar
12669      * @method show
12670      */
12671     show : function()
12672     {
12673         this.el.child('div').show();
12674     },
12675       
12676     // private
12677     nextBlock : function(){
12678         var td = document.createElement("td");
12679         this.tr.appendChild(td);
12680         return td;
12681     },
12682
12683     // private
12684     destroy : function(){
12685         if(this.items){ // rendered?
12686             Roo.destroy.apply(Roo, this.items.items);
12687         }
12688         if(this.fields){ // rendered?
12689             Roo.destroy.apply(Roo, this.fields.items);
12690         }
12691         Roo.Element.uncache(this.el, this.tr);
12692     }
12693 };
12694
12695 /**
12696  * @class Roo.Toolbar.Item
12697  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12698  * @constructor
12699  * Creates a new Item
12700  * @param {HTMLElement} el 
12701  */
12702 Roo.Toolbar.Item = function(el){
12703     this.el = Roo.getDom(el);
12704     this.id = Roo.id(this.el);
12705     this.hidden = false;
12706 };
12707
12708 Roo.Toolbar.Item.prototype = {
12709     
12710     /**
12711      * Get this item's HTML Element
12712      * @return {HTMLElement}
12713      */
12714     getEl : function(){
12715        return this.el;  
12716     },
12717
12718     // private
12719     render : function(td){
12720         this.td = td;
12721         td.appendChild(this.el);
12722     },
12723     
12724     /**
12725      * Removes and destroys this item.
12726      */
12727     destroy : function(){
12728         this.td.parentNode.removeChild(this.td);
12729     },
12730     
12731     /**
12732      * Shows this item.
12733      */
12734     show: function(){
12735         this.hidden = false;
12736         this.td.style.display = "";
12737     },
12738     
12739     /**
12740      * Hides this item.
12741      */
12742     hide: function(){
12743         this.hidden = true;
12744         this.td.style.display = "none";
12745     },
12746     
12747     /**
12748      * Convenience function for boolean show/hide.
12749      * @param {Boolean} visible true to show/false to hide
12750      */
12751     setVisible: function(visible){
12752         if(visible) {
12753             this.show();
12754         }else{
12755             this.hide();
12756         }
12757     },
12758     
12759     /**
12760      * Try to focus this item.
12761      */
12762     focus : function(){
12763         Roo.fly(this.el).focus();
12764     },
12765     
12766     /**
12767      * Disables this item.
12768      */
12769     disable : function(){
12770         Roo.fly(this.td).addClass("x-item-disabled");
12771         this.disabled = true;
12772         this.el.disabled = true;
12773     },
12774     
12775     /**
12776      * Enables this item.
12777      */
12778     enable : function(){
12779         Roo.fly(this.td).removeClass("x-item-disabled");
12780         this.disabled = false;
12781         this.el.disabled = false;
12782     }
12783 };
12784
12785
12786 /**
12787  * @class Roo.Toolbar.Separator
12788  * @extends Roo.Toolbar.Item
12789  * A simple toolbar separator class
12790  * @constructor
12791  * Creates a new Separator
12792  */
12793 Roo.Toolbar.Separator = function(){
12794     var s = document.createElement("span");
12795     s.className = "ytb-sep";
12796     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12797 };
12798 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12799     enable:Roo.emptyFn,
12800     disable:Roo.emptyFn,
12801     focus:Roo.emptyFn
12802 });
12803
12804 /**
12805  * @class Roo.Toolbar.Spacer
12806  * @extends Roo.Toolbar.Item
12807  * A simple element that adds extra horizontal space to a toolbar.
12808  * @constructor
12809  * Creates a new Spacer
12810  */
12811 Roo.Toolbar.Spacer = function(){
12812     var s = document.createElement("div");
12813     s.className = "ytb-spacer";
12814     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12815 };
12816 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12817     enable:Roo.emptyFn,
12818     disable:Roo.emptyFn,
12819     focus:Roo.emptyFn
12820 });
12821
12822 /**
12823  * @class Roo.Toolbar.Fill
12824  * @extends Roo.Toolbar.Spacer
12825  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12826  * @constructor
12827  * Creates a new Spacer
12828  */
12829 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12830     // private
12831     render : function(td){
12832         td.style.width = '100%';
12833         Roo.Toolbar.Fill.superclass.render.call(this, td);
12834     }
12835 });
12836
12837 /**
12838  * @class Roo.Toolbar.TextItem
12839  * @extends Roo.Toolbar.Item
12840  * A simple class that renders text directly into a toolbar.
12841  * @constructor
12842  * Creates a new TextItem
12843  * @param {String} text
12844  */
12845 Roo.Toolbar.TextItem = function(text){
12846     if (typeof(text) == 'object') {
12847         text = text.text;
12848     }
12849     var s = document.createElement("span");
12850     s.className = "ytb-text";
12851     s.innerHTML = text;
12852     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12853 };
12854 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12855     enable:Roo.emptyFn,
12856     disable:Roo.emptyFn,
12857     focus:Roo.emptyFn
12858 });
12859
12860 /**
12861  * @class Roo.Toolbar.Button
12862  * @extends Roo.Button
12863  * A button that renders into a toolbar.
12864  * @constructor
12865  * Creates a new Button
12866  * @param {Object} config A standard {@link Roo.Button} config object
12867  */
12868 Roo.Toolbar.Button = function(config){
12869     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12870 };
12871 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12872     render : function(td){
12873         this.td = td;
12874         Roo.Toolbar.Button.superclass.render.call(this, td);
12875     },
12876     
12877     /**
12878      * Removes and destroys this button
12879      */
12880     destroy : function(){
12881         Roo.Toolbar.Button.superclass.destroy.call(this);
12882         this.td.parentNode.removeChild(this.td);
12883     },
12884     
12885     /**
12886      * Shows this button
12887      */
12888     show: function(){
12889         this.hidden = false;
12890         this.td.style.display = "";
12891     },
12892     
12893     /**
12894      * Hides this button
12895      */
12896     hide: function(){
12897         this.hidden = true;
12898         this.td.style.display = "none";
12899     },
12900
12901     /**
12902      * Disables this item
12903      */
12904     disable : function(){
12905         Roo.fly(this.td).addClass("x-item-disabled");
12906         this.disabled = true;
12907     },
12908
12909     /**
12910      * Enables this item
12911      */
12912     enable : function(){
12913         Roo.fly(this.td).removeClass("x-item-disabled");
12914         this.disabled = false;
12915     }
12916 });
12917 // backwards compat
12918 Roo.ToolbarButton = Roo.Toolbar.Button;
12919
12920 /**
12921  * @class Roo.Toolbar.SplitButton
12922  * @extends Roo.SplitButton
12923  * A menu button that renders into a toolbar.
12924  * @constructor
12925  * Creates a new SplitButton
12926  * @param {Object} config A standard {@link Roo.SplitButton} config object
12927  */
12928 Roo.Toolbar.SplitButton = function(config){
12929     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12930 };
12931 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12932     render : function(td){
12933         this.td = td;
12934         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12935     },
12936     
12937     /**
12938      * Removes and destroys this button
12939      */
12940     destroy : function(){
12941         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12942         this.td.parentNode.removeChild(this.td);
12943     },
12944     
12945     /**
12946      * Shows this button
12947      */
12948     show: function(){
12949         this.hidden = false;
12950         this.td.style.display = "";
12951     },
12952     
12953     /**
12954      * Hides this button
12955      */
12956     hide: function(){
12957         this.hidden = true;
12958         this.td.style.display = "none";
12959     }
12960 });
12961
12962 // backwards compat
12963 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12964  * Based on:
12965  * Ext JS Library 1.1.1
12966  * Copyright(c) 2006-2007, Ext JS, LLC.
12967  *
12968  * Originally Released Under LGPL - original licence link has changed is not relivant.
12969  *
12970  * Fork - LGPL
12971  * <script type="text/javascript">
12972  */
12973  
12974 /**
12975  * @class Roo.PagingToolbar
12976  * @extends Roo.Toolbar
12977  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12978  * @constructor
12979  * Create a new PagingToolbar
12980  * @param {Object} config The config object
12981  */
12982 Roo.PagingToolbar = function(el, ds, config)
12983 {
12984     // old args format still supported... - xtype is prefered..
12985     if (typeof(el) == 'object' && el.xtype) {
12986         // created from xtype...
12987         config = el;
12988         ds = el.dataSource;
12989         el = config.container;
12990     }
12991     var items = [];
12992     if (config.items) {
12993         items = config.items;
12994         config.items = [];
12995     }
12996     
12997     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
12998     this.ds = ds;
12999     this.cursor = 0;
13000     this.renderButtons(this.el);
13001     this.bind(ds);
13002     
13003     // supprot items array.
13004    
13005     Roo.each(items, function(e) {
13006         this.add(Roo.factory(e));
13007     },this);
13008     
13009 };
13010
13011 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13012     /**
13013      * @cfg {Roo.data.Store} dataSource
13014      * The underlying data store providing the paged data
13015      */
13016     /**
13017      * @cfg {String/HTMLElement/Element} container
13018      * container The id or element that will contain the toolbar
13019      */
13020     /**
13021      * @cfg {Boolean} displayInfo
13022      * True to display the displayMsg (defaults to false)
13023      */
13024     /**
13025      * @cfg {Number} pageSize
13026      * The number of records to display per page (defaults to 20)
13027      */
13028     pageSize: 20,
13029     /**
13030      * @cfg {String} displayMsg
13031      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13032      */
13033     displayMsg : 'Displaying {0} - {1} of {2}',
13034     /**
13035      * @cfg {String} emptyMsg
13036      * The message to display when no records are found (defaults to "No data to display")
13037      */
13038     emptyMsg : 'No data to display',
13039     /**
13040      * Customizable piece of the default paging text (defaults to "Page")
13041      * @type String
13042      */
13043     beforePageText : "Page",
13044     /**
13045      * Customizable piece of the default paging text (defaults to "of %0")
13046      * @type String
13047      */
13048     afterPageText : "of {0}",
13049     /**
13050      * Customizable piece of the default paging text (defaults to "First Page")
13051      * @type String
13052      */
13053     firstText : "First Page",
13054     /**
13055      * Customizable piece of the default paging text (defaults to "Previous Page")
13056      * @type String
13057      */
13058     prevText : "Previous Page",
13059     /**
13060      * Customizable piece of the default paging text (defaults to "Next Page")
13061      * @type String
13062      */
13063     nextText : "Next Page",
13064     /**
13065      * Customizable piece of the default paging text (defaults to "Last Page")
13066      * @type String
13067      */
13068     lastText : "Last Page",
13069     /**
13070      * Customizable piece of the default paging text (defaults to "Refresh")
13071      * @type String
13072      */
13073     refreshText : "Refresh",
13074
13075     // private
13076     renderButtons : function(el){
13077         Roo.PagingToolbar.superclass.render.call(this, el);
13078         this.first = this.addButton({
13079             tooltip: this.firstText,
13080             cls: "x-btn-icon x-grid-page-first",
13081             disabled: true,
13082             handler: this.onClick.createDelegate(this, ["first"])
13083         });
13084         this.prev = this.addButton({
13085             tooltip: this.prevText,
13086             cls: "x-btn-icon x-grid-page-prev",
13087             disabled: true,
13088             handler: this.onClick.createDelegate(this, ["prev"])
13089         });
13090         //this.addSeparator();
13091         this.add(this.beforePageText);
13092         this.field = Roo.get(this.addDom({
13093            tag: "input",
13094            type: "text",
13095            size: "3",
13096            value: "1",
13097            cls: "x-grid-page-number"
13098         }).el);
13099         this.field.on("keydown", this.onPagingKeydown, this);
13100         this.field.on("focus", function(){this.dom.select();});
13101         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13102         this.field.setHeight(18);
13103         //this.addSeparator();
13104         this.next = this.addButton({
13105             tooltip: this.nextText,
13106             cls: "x-btn-icon x-grid-page-next",
13107             disabled: true,
13108             handler: this.onClick.createDelegate(this, ["next"])
13109         });
13110         this.last = this.addButton({
13111             tooltip: this.lastText,
13112             cls: "x-btn-icon x-grid-page-last",
13113             disabled: true,
13114             handler: this.onClick.createDelegate(this, ["last"])
13115         });
13116         //this.addSeparator();
13117         this.loading = this.addButton({
13118             tooltip: this.refreshText,
13119             cls: "x-btn-icon x-grid-loading",
13120             handler: this.onClick.createDelegate(this, ["refresh"])
13121         });
13122
13123         if(this.displayInfo){
13124             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13125         }
13126     },
13127
13128     // private
13129     updateInfo : function(){
13130         if(this.displayEl){
13131             var count = this.ds.getCount();
13132             var msg = count == 0 ?
13133                 this.emptyMsg :
13134                 String.format(
13135                     this.displayMsg,
13136                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13137                 );
13138             this.displayEl.update(msg);
13139         }
13140     },
13141
13142     // private
13143     onLoad : function(ds, r, o){
13144        this.cursor = o.params ? o.params.start : 0;
13145        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13146
13147        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13148        this.field.dom.value = ap;
13149        this.first.setDisabled(ap == 1);
13150        this.prev.setDisabled(ap == 1);
13151        this.next.setDisabled(ap == ps);
13152        this.last.setDisabled(ap == ps);
13153        this.loading.enable();
13154        this.updateInfo();
13155     },
13156
13157     // private
13158     getPageData : function(){
13159         var total = this.ds.getTotalCount();
13160         return {
13161             total : total,
13162             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13163             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13164         };
13165     },
13166
13167     // private
13168     onLoadError : function(){
13169         this.loading.enable();
13170     },
13171
13172     // private
13173     onPagingKeydown : function(e){
13174         var k = e.getKey();
13175         var d = this.getPageData();
13176         if(k == e.RETURN){
13177             var v = this.field.dom.value, pageNum;
13178             if(!v || isNaN(pageNum = parseInt(v, 10))){
13179                 this.field.dom.value = d.activePage;
13180                 return;
13181             }
13182             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13183             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13184             e.stopEvent();
13185         }
13186         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))
13187         {
13188           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13189           this.field.dom.value = pageNum;
13190           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13191           e.stopEvent();
13192         }
13193         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13194         {
13195           var v = this.field.dom.value, pageNum; 
13196           var increment = (e.shiftKey) ? 10 : 1;
13197           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13198             increment *= -1;
13199           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13200             this.field.dom.value = d.activePage;
13201             return;
13202           }
13203           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13204           {
13205             this.field.dom.value = parseInt(v, 10) + increment;
13206             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13207             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13208           }
13209           e.stopEvent();
13210         }
13211     },
13212
13213     // private
13214     beforeLoad : function(){
13215         if(this.loading){
13216             this.loading.disable();
13217         }
13218     },
13219
13220     // private
13221     onClick : function(which){
13222         var ds = this.ds;
13223         switch(which){
13224             case "first":
13225                 ds.load({params:{start: 0, limit: this.pageSize}});
13226             break;
13227             case "prev":
13228                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13229             break;
13230             case "next":
13231                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13232             break;
13233             case "last":
13234                 var total = ds.getTotalCount();
13235                 var extra = total % this.pageSize;
13236                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13237                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13238             break;
13239             case "refresh":
13240                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13241             break;
13242         }
13243     },
13244
13245     /**
13246      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13247      * @param {Roo.data.Store} store The data store to unbind
13248      */
13249     unbind : function(ds){
13250         ds.un("beforeload", this.beforeLoad, this);
13251         ds.un("load", this.onLoad, this);
13252         ds.un("loadexception", this.onLoadError, this);
13253         ds.un("remove", this.updateInfo, this);
13254         ds.un("add", this.updateInfo, this);
13255         this.ds = undefined;
13256     },
13257
13258     /**
13259      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13260      * @param {Roo.data.Store} store The data store to bind
13261      */
13262     bind : function(ds){
13263         ds.on("beforeload", this.beforeLoad, this);
13264         ds.on("load", this.onLoad, this);
13265         ds.on("loadexception", this.onLoadError, this);
13266         ds.on("remove", this.updateInfo, this);
13267         ds.on("add", this.updateInfo, this);
13268         this.ds = ds;
13269     }
13270 });/*
13271  * Based on:
13272  * Ext JS Library 1.1.1
13273  * Copyright(c) 2006-2007, Ext JS, LLC.
13274  *
13275  * Originally Released Under LGPL - original licence link has changed is not relivant.
13276  *
13277  * Fork - LGPL
13278  * <script type="text/javascript">
13279  */
13280
13281 /**
13282  * @class Roo.Resizable
13283  * @extends Roo.util.Observable
13284  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13285  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13286  * 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
13287  * the element will be wrapped for you automatically.</p>
13288  * <p>Here is the list of valid resize handles:</p>
13289  * <pre>
13290 Value   Description
13291 ------  -------------------
13292  'n'     north
13293  's'     south
13294  'e'     east
13295  'w'     west
13296  'nw'    northwest
13297  'sw'    southwest
13298  'se'    southeast
13299  'ne'    northeast
13300  'hd'    horizontal drag
13301  'all'   all
13302 </pre>
13303  * <p>Here's an example showing the creation of a typical Resizable:</p>
13304  * <pre><code>
13305 var resizer = new Roo.Resizable("element-id", {
13306     handles: 'all',
13307     minWidth: 200,
13308     minHeight: 100,
13309     maxWidth: 500,
13310     maxHeight: 400,
13311     pinned: true
13312 });
13313 resizer.on("resize", myHandler);
13314 </code></pre>
13315  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13316  * resizer.east.setDisplayed(false);</p>
13317  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13318  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13319  * resize operation's new size (defaults to [0, 0])
13320  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13321  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13322  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13323  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13324  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13325  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13326  * @cfg {Number} width The width of the element in pixels (defaults to null)
13327  * @cfg {Number} height The height of the element in pixels (defaults to null)
13328  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13329  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13330  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13331  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13332  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13333  * in favor of the handles config option (defaults to false)
13334  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13335  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13336  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13337  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13338  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13339  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13340  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13341  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13342  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13343  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13344  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13345  * @constructor
13346  * Create a new resizable component
13347  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13348  * @param {Object} config configuration options
13349   */
13350 Roo.Resizable = function(el, config)
13351 {
13352     this.el = Roo.get(el);
13353
13354     if(config && config.wrap){
13355         config.resizeChild = this.el;
13356         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13357         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13358         this.el.setStyle("overflow", "hidden");
13359         this.el.setPositioning(config.resizeChild.getPositioning());
13360         config.resizeChild.clearPositioning();
13361         if(!config.width || !config.height){
13362             var csize = config.resizeChild.getSize();
13363             this.el.setSize(csize.width, csize.height);
13364         }
13365         if(config.pinned && !config.adjustments){
13366             config.adjustments = "auto";
13367         }
13368     }
13369
13370     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13371     this.proxy.unselectable();
13372     this.proxy.enableDisplayMode('block');
13373
13374     Roo.apply(this, config);
13375
13376     if(this.pinned){
13377         this.disableTrackOver = true;
13378         this.el.addClass("x-resizable-pinned");
13379     }
13380     // if the element isn't positioned, make it relative
13381     var position = this.el.getStyle("position");
13382     if(position != "absolute" && position != "fixed"){
13383         this.el.setStyle("position", "relative");
13384     }
13385     if(!this.handles){ // no handles passed, must be legacy style
13386         this.handles = 's,e,se';
13387         if(this.multiDirectional){
13388             this.handles += ',n,w';
13389         }
13390     }
13391     if(this.handles == "all"){
13392         this.handles = "n s e w ne nw se sw";
13393     }
13394     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13395     var ps = Roo.Resizable.positions;
13396     for(var i = 0, len = hs.length; i < len; i++){
13397         if(hs[i] && ps[hs[i]]){
13398             var pos = ps[hs[i]];
13399             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13400         }
13401     }
13402     // legacy
13403     this.corner = this.southeast;
13404     
13405     // updateBox = the box can move..
13406     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13407         this.updateBox = true;
13408     }
13409
13410     this.activeHandle = null;
13411
13412     if(this.resizeChild){
13413         if(typeof this.resizeChild == "boolean"){
13414             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13415         }else{
13416             this.resizeChild = Roo.get(this.resizeChild, true);
13417         }
13418     }
13419     
13420     if(this.adjustments == "auto"){
13421         var rc = this.resizeChild;
13422         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13423         if(rc && (hw || hn)){
13424             rc.position("relative");
13425             rc.setLeft(hw ? hw.el.getWidth() : 0);
13426             rc.setTop(hn ? hn.el.getHeight() : 0);
13427         }
13428         this.adjustments = [
13429             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13430             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13431         ];
13432     }
13433
13434     if(this.draggable){
13435         this.dd = this.dynamic ?
13436             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13437         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13438     }
13439
13440     // public events
13441     this.addEvents({
13442         /**
13443          * @event beforeresize
13444          * Fired before resize is allowed. Set enabled to false to cancel resize.
13445          * @param {Roo.Resizable} this
13446          * @param {Roo.EventObject} e The mousedown event
13447          */
13448         "beforeresize" : true,
13449         /**
13450          * @event resize
13451          * Fired after a resize.
13452          * @param {Roo.Resizable} this
13453          * @param {Number} width The new width
13454          * @param {Number} height The new height
13455          * @param {Roo.EventObject} e The mouseup event
13456          */
13457         "resize" : true
13458     });
13459
13460     if(this.width !== null && this.height !== null){
13461         this.resizeTo(this.width, this.height);
13462     }else{
13463         this.updateChildSize();
13464     }
13465     if(Roo.isIE){
13466         this.el.dom.style.zoom = 1;
13467     }
13468     Roo.Resizable.superclass.constructor.call(this);
13469 };
13470
13471 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13472         resizeChild : false,
13473         adjustments : [0, 0],
13474         minWidth : 5,
13475         minHeight : 5,
13476         maxWidth : 10000,
13477         maxHeight : 10000,
13478         enabled : true,
13479         animate : false,
13480         duration : .35,
13481         dynamic : false,
13482         handles : false,
13483         multiDirectional : false,
13484         disableTrackOver : false,
13485         easing : 'easeOutStrong',
13486         widthIncrement : 0,
13487         heightIncrement : 0,
13488         pinned : false,
13489         width : null,
13490         height : null,
13491         preserveRatio : false,
13492         transparent: false,
13493         minX: 0,
13494         minY: 0,
13495         draggable: false,
13496
13497         /**
13498          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13499          */
13500         constrainTo: undefined,
13501         /**
13502          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13503          */
13504         resizeRegion: undefined,
13505
13506
13507     /**
13508      * Perform a manual resize
13509      * @param {Number} width
13510      * @param {Number} height
13511      */
13512     resizeTo : function(width, height){
13513         this.el.setSize(width, height);
13514         this.updateChildSize();
13515         this.fireEvent("resize", this, width, height, null);
13516     },
13517
13518     // private
13519     startSizing : function(e, handle){
13520         this.fireEvent("beforeresize", this, e);
13521         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13522
13523             if(!this.overlay){
13524                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13525                 this.overlay.unselectable();
13526                 this.overlay.enableDisplayMode("block");
13527                 this.overlay.on("mousemove", this.onMouseMove, this);
13528                 this.overlay.on("mouseup", this.onMouseUp, this);
13529             }
13530             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13531
13532             this.resizing = true;
13533             this.startBox = this.el.getBox();
13534             this.startPoint = e.getXY();
13535             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13536                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13537
13538             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13539             this.overlay.show();
13540
13541             if(this.constrainTo) {
13542                 var ct = Roo.get(this.constrainTo);
13543                 this.resizeRegion = ct.getRegion().adjust(
13544                     ct.getFrameWidth('t'),
13545                     ct.getFrameWidth('l'),
13546                     -ct.getFrameWidth('b'),
13547                     -ct.getFrameWidth('r')
13548                 );
13549             }
13550
13551             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13552             this.proxy.show();
13553             this.proxy.setBox(this.startBox);
13554             if(!this.dynamic){
13555                 this.proxy.setStyle('visibility', 'visible');
13556             }
13557         }
13558     },
13559
13560     // private
13561     onMouseDown : function(handle, e){
13562         if(this.enabled){
13563             e.stopEvent();
13564             this.activeHandle = handle;
13565             this.startSizing(e, handle);
13566         }
13567     },
13568
13569     // private
13570     onMouseUp : function(e){
13571         var size = this.resizeElement();
13572         this.resizing = false;
13573         this.handleOut();
13574         this.overlay.hide();
13575         this.proxy.hide();
13576         this.fireEvent("resize", this, size.width, size.height, e);
13577     },
13578
13579     // private
13580     updateChildSize : function(){
13581         if(this.resizeChild){
13582             var el = this.el;
13583             var child = this.resizeChild;
13584             var adj = this.adjustments;
13585             if(el.dom.offsetWidth){
13586                 var b = el.getSize(true);
13587                 child.setSize(b.width+adj[0], b.height+adj[1]);
13588             }
13589             // Second call here for IE
13590             // The first call enables instant resizing and
13591             // the second call corrects scroll bars if they
13592             // exist
13593             if(Roo.isIE){
13594                 setTimeout(function(){
13595                     if(el.dom.offsetWidth){
13596                         var b = el.getSize(true);
13597                         child.setSize(b.width+adj[0], b.height+adj[1]);
13598                     }
13599                 }, 10);
13600             }
13601         }
13602     },
13603
13604     // private
13605     snap : function(value, inc, min){
13606         if(!inc || !value) return value;
13607         var newValue = value;
13608         var m = value % inc;
13609         if(m > 0){
13610             if(m > (inc/2)){
13611                 newValue = value + (inc-m);
13612             }else{
13613                 newValue = value - m;
13614             }
13615         }
13616         return Math.max(min, newValue);
13617     },
13618
13619     // private
13620     resizeElement : function(){
13621         var box = this.proxy.getBox();
13622         if(this.updateBox){
13623             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13624         }else{
13625             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13626         }
13627         this.updateChildSize();
13628         if(!this.dynamic){
13629             this.proxy.hide();
13630         }
13631         return box;
13632     },
13633
13634     // private
13635     constrain : function(v, diff, m, mx){
13636         if(v - diff < m){
13637             diff = v - m;
13638         }else if(v - diff > mx){
13639             diff = mx - v;
13640         }
13641         return diff;
13642     },
13643
13644     // private
13645     onMouseMove : function(e){
13646         if(this.enabled){
13647             try{// try catch so if something goes wrong the user doesn't get hung
13648
13649             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13650                 return;
13651             }
13652
13653             //var curXY = this.startPoint;
13654             var curSize = this.curSize || this.startBox;
13655             var x = this.startBox.x, y = this.startBox.y;
13656             var ox = x, oy = y;
13657             var w = curSize.width, h = curSize.height;
13658             var ow = w, oh = h;
13659             var mw = this.minWidth, mh = this.minHeight;
13660             var mxw = this.maxWidth, mxh = this.maxHeight;
13661             var wi = this.widthIncrement;
13662             var hi = this.heightIncrement;
13663
13664             var eventXY = e.getXY();
13665             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13666             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13667
13668             var pos = this.activeHandle.position;
13669
13670             switch(pos){
13671                 case "east":
13672                     w += diffX;
13673                     w = Math.min(Math.max(mw, w), mxw);
13674                     break;
13675              
13676                 case "south":
13677                     h += diffY;
13678                     h = Math.min(Math.max(mh, h), mxh);
13679                     break;
13680                 case "southeast":
13681                     w += diffX;
13682                     h += diffY;
13683                     w = Math.min(Math.max(mw, w), mxw);
13684                     h = Math.min(Math.max(mh, h), mxh);
13685                     break;
13686                 case "north":
13687                     diffY = this.constrain(h, diffY, mh, mxh);
13688                     y += diffY;
13689                     h -= diffY;
13690                     break;
13691                 case "hdrag":
13692                     
13693                     if (wi) {
13694                         var adiffX = Math.abs(diffX);
13695                         var sub = (adiffX % wi); // how much 
13696                         if (sub > (wi/2)) { // far enough to snap
13697                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13698                         } else {
13699                             // remove difference.. 
13700                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13701                         }
13702                     }
13703                     x += diffX;
13704                     x = Math.max(this.minX, x);
13705                     break;
13706                 case "west":
13707                     diffX = this.constrain(w, diffX, mw, mxw);
13708                     x += diffX;
13709                     w -= diffX;
13710                     break;
13711                 case "northeast":
13712                     w += diffX;
13713                     w = Math.min(Math.max(mw, w), mxw);
13714                     diffY = this.constrain(h, diffY, mh, mxh);
13715                     y += diffY;
13716                     h -= diffY;
13717                     break;
13718                 case "northwest":
13719                     diffX = this.constrain(w, diffX, mw, mxw);
13720                     diffY = this.constrain(h, diffY, mh, mxh);
13721                     y += diffY;
13722                     h -= diffY;
13723                     x += diffX;
13724                     w -= diffX;
13725                     break;
13726                case "southwest":
13727                     diffX = this.constrain(w, diffX, mw, mxw);
13728                     h += diffY;
13729                     h = Math.min(Math.max(mh, h), mxh);
13730                     x += diffX;
13731                     w -= diffX;
13732                     break;
13733             }
13734
13735             var sw = this.snap(w, wi, mw);
13736             var sh = this.snap(h, hi, mh);
13737             if(sw != w || sh != h){
13738                 switch(pos){
13739                     case "northeast":
13740                         y -= sh - h;
13741                     break;
13742                     case "north":
13743                         y -= sh - h;
13744                         break;
13745                     case "southwest":
13746                         x -= sw - w;
13747                     break;
13748                     case "west":
13749                         x -= sw - w;
13750                         break;
13751                     case "northwest":
13752                         x -= sw - w;
13753                         y -= sh - h;
13754                     break;
13755                 }
13756                 w = sw;
13757                 h = sh;
13758             }
13759
13760             if(this.preserveRatio){
13761                 switch(pos){
13762                     case "southeast":
13763                     case "east":
13764                         h = oh * (w/ow);
13765                         h = Math.min(Math.max(mh, h), mxh);
13766                         w = ow * (h/oh);
13767                        break;
13768                     case "south":
13769                         w = ow * (h/oh);
13770                         w = Math.min(Math.max(mw, w), mxw);
13771                         h = oh * (w/ow);
13772                         break;
13773                     case "northeast":
13774                         w = ow * (h/oh);
13775                         w = Math.min(Math.max(mw, w), mxw);
13776                         h = oh * (w/ow);
13777                     break;
13778                     case "north":
13779                         var tw = w;
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         x += (tw - w) / 2;
13784                         break;
13785                     case "southwest":
13786                         h = oh * (w/ow);
13787                         h = Math.min(Math.max(mh, h), mxh);
13788                         var tw = w;
13789                         w = ow * (h/oh);
13790                         x += tw - w;
13791                         break;
13792                     case "west":
13793                         var th = h;
13794                         h = oh * (w/ow);
13795                         h = Math.min(Math.max(mh, h), mxh);
13796                         y += (th - h) / 2;
13797                         var tw = w;
13798                         w = ow * (h/oh);
13799                         x += tw - w;
13800                        break;
13801                     case "northwest":
13802                         var tw = w;
13803                         var th = h;
13804                         h = oh * (w/ow);
13805                         h = Math.min(Math.max(mh, h), mxh);
13806                         w = ow * (h/oh);
13807                         y += th - h;
13808                         x += tw - w;
13809                        break;
13810
13811                 }
13812             }
13813             if (pos == 'hdrag') {
13814                 w = ow;
13815             }
13816             this.proxy.setBounds(x, y, w, h);
13817             if(this.dynamic){
13818                 this.resizeElement();
13819             }
13820             }catch(e){}
13821         }
13822     },
13823
13824     // private
13825     handleOver : function(){
13826         if(this.enabled){
13827             this.el.addClass("x-resizable-over");
13828         }
13829     },
13830
13831     // private
13832     handleOut : function(){
13833         if(!this.resizing){
13834             this.el.removeClass("x-resizable-over");
13835         }
13836     },
13837
13838     /**
13839      * Returns the element this component is bound to.
13840      * @return {Roo.Element}
13841      */
13842     getEl : function(){
13843         return this.el;
13844     },
13845
13846     /**
13847      * Returns the resizeChild element (or null).
13848      * @return {Roo.Element}
13849      */
13850     getResizeChild : function(){
13851         return this.resizeChild;
13852     },
13853
13854     /**
13855      * Destroys this resizable. If the element was wrapped and
13856      * removeEl is not true then the element remains.
13857      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13858      */
13859     destroy : function(removeEl){
13860         this.proxy.remove();
13861         if(this.overlay){
13862             this.overlay.removeAllListeners();
13863             this.overlay.remove();
13864         }
13865         var ps = Roo.Resizable.positions;
13866         for(var k in ps){
13867             if(typeof ps[k] != "function" && this[ps[k]]){
13868                 var h = this[ps[k]];
13869                 h.el.removeAllListeners();
13870                 h.el.remove();
13871             }
13872         }
13873         if(removeEl){
13874             this.el.update("");
13875             this.el.remove();
13876         }
13877     }
13878 });
13879
13880 // private
13881 // hash to map config positions to true positions
13882 Roo.Resizable.positions = {
13883     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13884     hd: "hdrag"
13885 };
13886
13887 // private
13888 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13889     if(!this.tpl){
13890         // only initialize the template if resizable is used
13891         var tpl = Roo.DomHelper.createTemplate(
13892             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13893         );
13894         tpl.compile();
13895         Roo.Resizable.Handle.prototype.tpl = tpl;
13896     }
13897     this.position = pos;
13898     this.rz = rz;
13899     // show north drag fro topdra
13900     var handlepos = pos == 'hdrag' ? 'north' : pos;
13901     
13902     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13903     if (pos == 'hdrag') {
13904         this.el.setStyle('cursor', 'pointer');
13905     }
13906     this.el.unselectable();
13907     if(transparent){
13908         this.el.setOpacity(0);
13909     }
13910     this.el.on("mousedown", this.onMouseDown, this);
13911     if(!disableTrackOver){
13912         this.el.on("mouseover", this.onMouseOver, this);
13913         this.el.on("mouseout", this.onMouseOut, this);
13914     }
13915 };
13916
13917 // private
13918 Roo.Resizable.Handle.prototype = {
13919     afterResize : function(rz){
13920         // do nothing
13921     },
13922     // private
13923     onMouseDown : function(e){
13924         this.rz.onMouseDown(this, e);
13925     },
13926     // private
13927     onMouseOver : function(e){
13928         this.rz.handleOver(this, e);
13929     },
13930     // private
13931     onMouseOut : function(e){
13932         this.rz.handleOut(this, e);
13933     }
13934 };/*
13935  * Based on:
13936  * Ext JS Library 1.1.1
13937  * Copyright(c) 2006-2007, Ext JS, LLC.
13938  *
13939  * Originally Released Under LGPL - original licence link has changed is not relivant.
13940  *
13941  * Fork - LGPL
13942  * <script type="text/javascript">
13943  */
13944
13945 /**
13946  * @class Roo.Editor
13947  * @extends Roo.Component
13948  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13949  * @constructor
13950  * Create a new Editor
13951  * @param {Roo.form.Field} field The Field object (or descendant)
13952  * @param {Object} config The config object
13953  */
13954 Roo.Editor = function(field, config){
13955     Roo.Editor.superclass.constructor.call(this, config);
13956     this.field = field;
13957     this.addEvents({
13958         /**
13959              * @event beforestartedit
13960              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13961              * false from the handler of this event.
13962              * @param {Editor} this
13963              * @param {Roo.Element} boundEl The underlying element bound to this editor
13964              * @param {Mixed} value The field value being set
13965              */
13966         "beforestartedit" : true,
13967         /**
13968              * @event startedit
13969              * Fires when this editor is displayed
13970              * @param {Roo.Element} boundEl The underlying element bound to this editor
13971              * @param {Mixed} value The starting field value
13972              */
13973         "startedit" : true,
13974         /**
13975              * @event beforecomplete
13976              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13977              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13978              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13979              * event will not fire since no edit actually occurred.
13980              * @param {Editor} this
13981              * @param {Mixed} value The current field value
13982              * @param {Mixed} startValue The original field value
13983              */
13984         "beforecomplete" : true,
13985         /**
13986              * @event complete
13987              * Fires after editing is complete and any changed value has been written to the underlying field.
13988              * @param {Editor} this
13989              * @param {Mixed} value The current field value
13990              * @param {Mixed} startValue The original field value
13991              */
13992         "complete" : true,
13993         /**
13994          * @event specialkey
13995          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
13996          * {@link Roo.EventObject#getKey} to determine which key was pressed.
13997          * @param {Roo.form.Field} this
13998          * @param {Roo.EventObject} e The event object
13999          */
14000         "specialkey" : true
14001     });
14002 };
14003
14004 Roo.extend(Roo.Editor, Roo.Component, {
14005     /**
14006      * @cfg {Boolean/String} autosize
14007      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14008      * or "height" to adopt the height only (defaults to false)
14009      */
14010     /**
14011      * @cfg {Boolean} revertInvalid
14012      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14013      * validation fails (defaults to true)
14014      */
14015     /**
14016      * @cfg {Boolean} ignoreNoChange
14017      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14018      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14019      * will never be ignored.
14020      */
14021     /**
14022      * @cfg {Boolean} hideEl
14023      * False to keep the bound element visible while the editor is displayed (defaults to true)
14024      */
14025     /**
14026      * @cfg {Mixed} value
14027      * The data value of the underlying field (defaults to "")
14028      */
14029     value : "",
14030     /**
14031      * @cfg {String} alignment
14032      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14033      */
14034     alignment: "c-c?",
14035     /**
14036      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14037      * for bottom-right shadow (defaults to "frame")
14038      */
14039     shadow : "frame",
14040     /**
14041      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14042      */
14043     constrain : false,
14044     /**
14045      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14046      */
14047     completeOnEnter : false,
14048     /**
14049      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14050      */
14051     cancelOnEsc : false,
14052     /**
14053      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14054      */
14055     updateEl : false,
14056
14057     // private
14058     onRender : function(ct, position){
14059         this.el = new Roo.Layer({
14060             shadow: this.shadow,
14061             cls: "x-editor",
14062             parentEl : ct,
14063             shim : this.shim,
14064             shadowOffset:4,
14065             id: this.id,
14066             constrain: this.constrain
14067         });
14068         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14069         if(this.field.msgTarget != 'title'){
14070             this.field.msgTarget = 'qtip';
14071         }
14072         this.field.render(this.el);
14073         if(Roo.isGecko){
14074             this.field.el.dom.setAttribute('autocomplete', 'off');
14075         }
14076         this.field.on("specialkey", this.onSpecialKey, this);
14077         if(this.swallowKeys){
14078             this.field.el.swallowEvent(['keydown','keypress']);
14079         }
14080         this.field.show();
14081         this.field.on("blur", this.onBlur, this);
14082         if(this.field.grow){
14083             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14084         }
14085     },
14086
14087     onSpecialKey : function(field, e)
14088     {
14089         //Roo.log('editor onSpecialKey');
14090         if(this.completeOnEnter && e.getKey() == e.ENTER){
14091             e.stopEvent();
14092             this.completeEdit();
14093             return;
14094         }
14095         // do not fire special key otherwise it might hide close the editor...
14096         if(e.getKey() == e.ENTER){    
14097             return;
14098         }
14099         if(this.cancelOnEsc && e.getKey() == e.ESC){
14100             this.cancelEdit();
14101             return;
14102         } 
14103         this.fireEvent('specialkey', field, e);
14104     
14105     },
14106
14107     /**
14108      * Starts the editing process and shows the editor.
14109      * @param {String/HTMLElement/Element} el The element to edit
14110      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14111       * to the innerHTML of el.
14112      */
14113     startEdit : function(el, value){
14114         if(this.editing){
14115             this.completeEdit();
14116         }
14117         this.boundEl = Roo.get(el);
14118         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14119         if(!this.rendered){
14120             this.render(this.parentEl || document.body);
14121         }
14122         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14123             return;
14124         }
14125         this.startValue = v;
14126         this.field.setValue(v);
14127         if(this.autoSize){
14128             var sz = this.boundEl.getSize();
14129             switch(this.autoSize){
14130                 case "width":
14131                 this.setSize(sz.width,  "");
14132                 break;
14133                 case "height":
14134                 this.setSize("",  sz.height);
14135                 break;
14136                 default:
14137                 this.setSize(sz.width,  sz.height);
14138             }
14139         }
14140         this.el.alignTo(this.boundEl, this.alignment);
14141         this.editing = true;
14142         if(Roo.QuickTips){
14143             Roo.QuickTips.disable();
14144         }
14145         this.show();
14146     },
14147
14148     /**
14149      * Sets the height and width of this editor.
14150      * @param {Number} width The new width
14151      * @param {Number} height The new height
14152      */
14153     setSize : function(w, h){
14154         this.field.setSize(w, h);
14155         if(this.el){
14156             this.el.sync();
14157         }
14158     },
14159
14160     /**
14161      * Realigns the editor to the bound field based on the current alignment config value.
14162      */
14163     realign : function(){
14164         this.el.alignTo(this.boundEl, this.alignment);
14165     },
14166
14167     /**
14168      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14169      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14170      */
14171     completeEdit : function(remainVisible){
14172         if(!this.editing){
14173             return;
14174         }
14175         var v = this.getValue();
14176         if(this.revertInvalid !== false && !this.field.isValid()){
14177             v = this.startValue;
14178             this.cancelEdit(true);
14179         }
14180         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14181             this.editing = false;
14182             this.hide();
14183             return;
14184         }
14185         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14186             this.editing = false;
14187             if(this.updateEl && this.boundEl){
14188                 this.boundEl.update(v);
14189             }
14190             if(remainVisible !== true){
14191                 this.hide();
14192             }
14193             this.fireEvent("complete", this, v, this.startValue);
14194         }
14195     },
14196
14197     // private
14198     onShow : function(){
14199         this.el.show();
14200         if(this.hideEl !== false){
14201             this.boundEl.hide();
14202         }
14203         this.field.show();
14204         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14205             this.fixIEFocus = true;
14206             this.deferredFocus.defer(50, this);
14207         }else{
14208             this.field.focus();
14209         }
14210         this.fireEvent("startedit", this.boundEl, this.startValue);
14211     },
14212
14213     deferredFocus : function(){
14214         if(this.editing){
14215             this.field.focus();
14216         }
14217     },
14218
14219     /**
14220      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14221      * reverted to the original starting value.
14222      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14223      * cancel (defaults to false)
14224      */
14225     cancelEdit : function(remainVisible){
14226         if(this.editing){
14227             this.setValue(this.startValue);
14228             if(remainVisible !== true){
14229                 this.hide();
14230             }
14231         }
14232     },
14233
14234     // private
14235     onBlur : function(){
14236         if(this.allowBlur !== true && this.editing){
14237             this.completeEdit();
14238         }
14239     },
14240
14241     // private
14242     onHide : function(){
14243         if(this.editing){
14244             this.completeEdit();
14245             return;
14246         }
14247         this.field.blur();
14248         if(this.field.collapse){
14249             this.field.collapse();
14250         }
14251         this.el.hide();
14252         if(this.hideEl !== false){
14253             this.boundEl.show();
14254         }
14255         if(Roo.QuickTips){
14256             Roo.QuickTips.enable();
14257         }
14258     },
14259
14260     /**
14261      * Sets the data value of the editor
14262      * @param {Mixed} value Any valid value supported by the underlying field
14263      */
14264     setValue : function(v){
14265         this.field.setValue(v);
14266     },
14267
14268     /**
14269      * Gets the data value of the editor
14270      * @return {Mixed} The data value
14271      */
14272     getValue : function(){
14273         return this.field.getValue();
14274     }
14275 });/*
14276  * Based on:
14277  * Ext JS Library 1.1.1
14278  * Copyright(c) 2006-2007, Ext JS, LLC.
14279  *
14280  * Originally Released Under LGPL - original licence link has changed is not relivant.
14281  *
14282  * Fork - LGPL
14283  * <script type="text/javascript">
14284  */
14285  
14286 /**
14287  * @class Roo.BasicDialog
14288  * @extends Roo.util.Observable
14289  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14290  * <pre><code>
14291 var dlg = new Roo.BasicDialog("my-dlg", {
14292     height: 200,
14293     width: 300,
14294     minHeight: 100,
14295     minWidth: 150,
14296     modal: true,
14297     proxyDrag: true,
14298     shadow: true
14299 });
14300 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14301 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14302 dlg.addButton('Cancel', dlg.hide, dlg);
14303 dlg.show();
14304 </code></pre>
14305   <b>A Dialog should always be a direct child of the body element.</b>
14306  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14307  * @cfg {String} title Default text to display in the title bar (defaults to null)
14308  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14309  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14310  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14311  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14312  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14313  * (defaults to null with no animation)
14314  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14315  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14316  * property for valid values (defaults to 'all')
14317  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14318  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14319  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14320  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14321  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14322  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14323  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14324  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14325  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14326  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14327  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14328  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14329  * draggable = true (defaults to false)
14330  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14331  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14332  * shadow (defaults to false)
14333  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14334  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14335  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14336  * @cfg {Array} buttons Array of buttons
14337  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14338  * @constructor
14339  * Create a new BasicDialog.
14340  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14341  * @param {Object} config Configuration options
14342  */
14343 Roo.BasicDialog = function(el, config){
14344     this.el = Roo.get(el);
14345     var dh = Roo.DomHelper;
14346     if(!this.el && config && config.autoCreate){
14347         if(typeof config.autoCreate == "object"){
14348             if(!config.autoCreate.id){
14349                 config.autoCreate.id = el;
14350             }
14351             this.el = dh.append(document.body,
14352                         config.autoCreate, true);
14353         }else{
14354             this.el = dh.append(document.body,
14355                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14356         }
14357     }
14358     el = this.el;
14359     el.setDisplayed(true);
14360     el.hide = this.hideAction;
14361     this.id = el.id;
14362     el.addClass("x-dlg");
14363
14364     Roo.apply(this, config);
14365
14366     this.proxy = el.createProxy("x-dlg-proxy");
14367     this.proxy.hide = this.hideAction;
14368     this.proxy.setOpacity(.5);
14369     this.proxy.hide();
14370
14371     if(config.width){
14372         el.setWidth(config.width);
14373     }
14374     if(config.height){
14375         el.setHeight(config.height);
14376     }
14377     this.size = el.getSize();
14378     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14379         this.xy = [config.x,config.y];
14380     }else{
14381         this.xy = el.getCenterXY(true);
14382     }
14383     /** The header element @type Roo.Element */
14384     this.header = el.child("> .x-dlg-hd");
14385     /** The body element @type Roo.Element */
14386     this.body = el.child("> .x-dlg-bd");
14387     /** The footer element @type Roo.Element */
14388     this.footer = el.child("> .x-dlg-ft");
14389
14390     if(!this.header){
14391         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14392     }
14393     if(!this.body){
14394         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14395     }
14396
14397     this.header.unselectable();
14398     if(this.title){
14399         this.header.update(this.title);
14400     }
14401     // this element allows the dialog to be focused for keyboard event
14402     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14403     this.focusEl.swallowEvent("click", true);
14404
14405     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14406
14407     // wrap the body and footer for special rendering
14408     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14409     if(this.footer){
14410         this.bwrap.dom.appendChild(this.footer.dom);
14411     }
14412
14413     this.bg = this.el.createChild({
14414         tag: "div", cls:"x-dlg-bg",
14415         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14416     });
14417     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14418
14419
14420     if(this.autoScroll !== false && !this.autoTabs){
14421         this.body.setStyle("overflow", "auto");
14422     }
14423
14424     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14425
14426     if(this.closable !== false){
14427         this.el.addClass("x-dlg-closable");
14428         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14429         this.close.on("click", this.closeClick, this);
14430         this.close.addClassOnOver("x-dlg-close-over");
14431     }
14432     if(this.collapsible !== false){
14433         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14434         this.collapseBtn.on("click", this.collapseClick, this);
14435         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14436         this.header.on("dblclick", this.collapseClick, this);
14437     }
14438     if(this.resizable !== false){
14439         this.el.addClass("x-dlg-resizable");
14440         this.resizer = new Roo.Resizable(el, {
14441             minWidth: this.minWidth || 80,
14442             minHeight:this.minHeight || 80,
14443             handles: this.resizeHandles || "all",
14444             pinned: true
14445         });
14446         this.resizer.on("beforeresize", this.beforeResize, this);
14447         this.resizer.on("resize", this.onResize, this);
14448     }
14449     if(this.draggable !== false){
14450         el.addClass("x-dlg-draggable");
14451         if (!this.proxyDrag) {
14452             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14453         }
14454         else {
14455             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14456         }
14457         dd.setHandleElId(this.header.id);
14458         dd.endDrag = this.endMove.createDelegate(this);
14459         dd.startDrag = this.startMove.createDelegate(this);
14460         dd.onDrag = this.onDrag.createDelegate(this);
14461         dd.scroll = false;
14462         this.dd = dd;
14463     }
14464     if(this.modal){
14465         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14466         this.mask.enableDisplayMode("block");
14467         this.mask.hide();
14468         this.el.addClass("x-dlg-modal");
14469     }
14470     if(this.shadow){
14471         this.shadow = new Roo.Shadow({
14472             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14473             offset : this.shadowOffset
14474         });
14475     }else{
14476         this.shadowOffset = 0;
14477     }
14478     if(Roo.useShims && this.shim !== false){
14479         this.shim = this.el.createShim();
14480         this.shim.hide = this.hideAction;
14481         this.shim.hide();
14482     }else{
14483         this.shim = false;
14484     }
14485     if(this.autoTabs){
14486         this.initTabs();
14487     }
14488     if (this.buttons) { 
14489         var bts= this.buttons;
14490         this.buttons = [];
14491         Roo.each(bts, function(b) {
14492             this.addButton(b);
14493         }, this);
14494     }
14495     
14496     
14497     this.addEvents({
14498         /**
14499          * @event keydown
14500          * Fires when a key is pressed
14501          * @param {Roo.BasicDialog} this
14502          * @param {Roo.EventObject} e
14503          */
14504         "keydown" : true,
14505         /**
14506          * @event move
14507          * Fires when this dialog is moved by the user.
14508          * @param {Roo.BasicDialog} this
14509          * @param {Number} x The new page X
14510          * @param {Number} y The new page Y
14511          */
14512         "move" : true,
14513         /**
14514          * @event resize
14515          * Fires when this dialog is resized by the user.
14516          * @param {Roo.BasicDialog} this
14517          * @param {Number} width The new width
14518          * @param {Number} height The new height
14519          */
14520         "resize" : true,
14521         /**
14522          * @event beforehide
14523          * Fires before this dialog is hidden.
14524          * @param {Roo.BasicDialog} this
14525          */
14526         "beforehide" : true,
14527         /**
14528          * @event hide
14529          * Fires when this dialog is hidden.
14530          * @param {Roo.BasicDialog} this
14531          */
14532         "hide" : true,
14533         /**
14534          * @event beforeshow
14535          * Fires before this dialog is shown.
14536          * @param {Roo.BasicDialog} this
14537          */
14538         "beforeshow" : true,
14539         /**
14540          * @event show
14541          * Fires when this dialog is shown.
14542          * @param {Roo.BasicDialog} this
14543          */
14544         "show" : true
14545     });
14546     el.on("keydown", this.onKeyDown, this);
14547     el.on("mousedown", this.toFront, this);
14548     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14549     this.el.hide();
14550     Roo.DialogManager.register(this);
14551     Roo.BasicDialog.superclass.constructor.call(this);
14552 };
14553
14554 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14555     shadowOffset: Roo.isIE ? 6 : 5,
14556     minHeight: 80,
14557     minWidth: 200,
14558     minButtonWidth: 75,
14559     defaultButton: null,
14560     buttonAlign: "right",
14561     tabTag: 'div',
14562     firstShow: true,
14563
14564     /**
14565      * Sets the dialog title text
14566      * @param {String} text The title text to display
14567      * @return {Roo.BasicDialog} this
14568      */
14569     setTitle : function(text){
14570         this.header.update(text);
14571         return this;
14572     },
14573
14574     // private
14575     closeClick : function(){
14576         this.hide();
14577     },
14578
14579     // private
14580     collapseClick : function(){
14581         this[this.collapsed ? "expand" : "collapse"]();
14582     },
14583
14584     /**
14585      * Collapses the dialog to its minimized state (only the title bar is visible).
14586      * Equivalent to the user clicking the collapse dialog button.
14587      */
14588     collapse : function(){
14589         if(!this.collapsed){
14590             this.collapsed = true;
14591             this.el.addClass("x-dlg-collapsed");
14592             this.restoreHeight = this.el.getHeight();
14593             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14594         }
14595     },
14596
14597     /**
14598      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14599      * clicking the expand dialog button.
14600      */
14601     expand : function(){
14602         if(this.collapsed){
14603             this.collapsed = false;
14604             this.el.removeClass("x-dlg-collapsed");
14605             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14606         }
14607     },
14608
14609     /**
14610      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14611      * @return {Roo.TabPanel} The tabs component
14612      */
14613     initTabs : function(){
14614         var tabs = this.getTabs();
14615         while(tabs.getTab(0)){
14616             tabs.removeTab(0);
14617         }
14618         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14619             var dom = el.dom;
14620             tabs.addTab(Roo.id(dom), dom.title);
14621             dom.title = "";
14622         });
14623         tabs.activate(0);
14624         return tabs;
14625     },
14626
14627     // private
14628     beforeResize : function(){
14629         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14630     },
14631
14632     // private
14633     onResize : function(){
14634         this.refreshSize();
14635         this.syncBodyHeight();
14636         this.adjustAssets();
14637         this.focus();
14638         this.fireEvent("resize", this, this.size.width, this.size.height);
14639     },
14640
14641     // private
14642     onKeyDown : function(e){
14643         if(this.isVisible()){
14644             this.fireEvent("keydown", this, e);
14645         }
14646     },
14647
14648     /**
14649      * Resizes the dialog.
14650      * @param {Number} width
14651      * @param {Number} height
14652      * @return {Roo.BasicDialog} this
14653      */
14654     resizeTo : function(width, height){
14655         this.el.setSize(width, height);
14656         this.size = {width: width, height: height};
14657         this.syncBodyHeight();
14658         if(this.fixedcenter){
14659             this.center();
14660         }
14661         if(this.isVisible()){
14662             this.constrainXY();
14663             this.adjustAssets();
14664         }
14665         this.fireEvent("resize", this, width, height);
14666         return this;
14667     },
14668
14669
14670     /**
14671      * Resizes the dialog to fit the specified content size.
14672      * @param {Number} width
14673      * @param {Number} height
14674      * @return {Roo.BasicDialog} this
14675      */
14676     setContentSize : function(w, h){
14677         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14678         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14679         //if(!this.el.isBorderBox()){
14680             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14681             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14682         //}
14683         if(this.tabs){
14684             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14685             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14686         }
14687         this.resizeTo(w, h);
14688         return this;
14689     },
14690
14691     /**
14692      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14693      * executed in response to a particular key being pressed while the dialog is active.
14694      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14695      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14696      * @param {Function} fn The function to call
14697      * @param {Object} scope (optional) The scope of the function
14698      * @return {Roo.BasicDialog} this
14699      */
14700     addKeyListener : function(key, fn, scope){
14701         var keyCode, shift, ctrl, alt;
14702         if(typeof key == "object" && !(key instanceof Array)){
14703             keyCode = key["key"];
14704             shift = key["shift"];
14705             ctrl = key["ctrl"];
14706             alt = key["alt"];
14707         }else{
14708             keyCode = key;
14709         }
14710         var handler = function(dlg, e){
14711             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14712                 var k = e.getKey();
14713                 if(keyCode instanceof Array){
14714                     for(var i = 0, len = keyCode.length; i < len; i++){
14715                         if(keyCode[i] == k){
14716                           fn.call(scope || window, dlg, k, e);
14717                           return;
14718                         }
14719                     }
14720                 }else{
14721                     if(k == keyCode){
14722                         fn.call(scope || window, dlg, k, e);
14723                     }
14724                 }
14725             }
14726         };
14727         this.on("keydown", handler);
14728         return this;
14729     },
14730
14731     /**
14732      * Returns the TabPanel component (creates it if it doesn't exist).
14733      * Note: If you wish to simply check for the existence of tabs without creating them,
14734      * check for a null 'tabs' property.
14735      * @return {Roo.TabPanel} The tabs component
14736      */
14737     getTabs : function(){
14738         if(!this.tabs){
14739             this.el.addClass("x-dlg-auto-tabs");
14740             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14741             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14742         }
14743         return this.tabs;
14744     },
14745
14746     /**
14747      * Adds a button to the footer section of the dialog.
14748      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14749      * object or a valid Roo.DomHelper element config
14750      * @param {Function} handler The function called when the button is clicked
14751      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14752      * @return {Roo.Button} The new button
14753      */
14754     addButton : function(config, handler, scope){
14755         var dh = Roo.DomHelper;
14756         if(!this.footer){
14757             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14758         }
14759         if(!this.btnContainer){
14760             var tb = this.footer.createChild({
14761
14762                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14763                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14764             }, null, true);
14765             this.btnContainer = tb.firstChild.firstChild.firstChild;
14766         }
14767         var bconfig = {
14768             handler: handler,
14769             scope: scope,
14770             minWidth: this.minButtonWidth,
14771             hideParent:true
14772         };
14773         if(typeof config == "string"){
14774             bconfig.text = config;
14775         }else{
14776             if(config.tag){
14777                 bconfig.dhconfig = config;
14778             }else{
14779                 Roo.apply(bconfig, config);
14780             }
14781         }
14782         var fc = false;
14783         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14784             bconfig.position = Math.max(0, bconfig.position);
14785             fc = this.btnContainer.childNodes[bconfig.position];
14786         }
14787          
14788         var btn = new Roo.Button(
14789             fc ? 
14790                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14791                 : this.btnContainer.appendChild(document.createElement("td")),
14792             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14793             bconfig
14794         );
14795         this.syncBodyHeight();
14796         if(!this.buttons){
14797             /**
14798              * Array of all the buttons that have been added to this dialog via addButton
14799              * @type Array
14800              */
14801             this.buttons = [];
14802         }
14803         this.buttons.push(btn);
14804         return btn;
14805     },
14806
14807     /**
14808      * Sets the default button to be focused when the dialog is displayed.
14809      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14810      * @return {Roo.BasicDialog} this
14811      */
14812     setDefaultButton : function(btn){
14813         this.defaultButton = btn;
14814         return this;
14815     },
14816
14817     // private
14818     getHeaderFooterHeight : function(safe){
14819         var height = 0;
14820         if(this.header){
14821            height += this.header.getHeight();
14822         }
14823         if(this.footer){
14824            var fm = this.footer.getMargins();
14825             height += (this.footer.getHeight()+fm.top+fm.bottom);
14826         }
14827         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14828         height += this.centerBg.getPadding("tb");
14829         return height;
14830     },
14831
14832     // private
14833     syncBodyHeight : function(){
14834         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14835         var height = this.size.height - this.getHeaderFooterHeight(false);
14836         bd.setHeight(height-bd.getMargins("tb"));
14837         var hh = this.header.getHeight();
14838         var h = this.size.height-hh;
14839         cb.setHeight(h);
14840         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14841         bw.setHeight(h-cb.getPadding("tb"));
14842         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14843         bd.setWidth(bw.getWidth(true));
14844         if(this.tabs){
14845             this.tabs.syncHeight();
14846             if(Roo.isIE){
14847                 this.tabs.el.repaint();
14848             }
14849         }
14850     },
14851
14852     /**
14853      * Restores the previous state of the dialog if Roo.state is configured.
14854      * @return {Roo.BasicDialog} this
14855      */
14856     restoreState : function(){
14857         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14858         if(box && box.width){
14859             this.xy = [box.x, box.y];
14860             this.resizeTo(box.width, box.height);
14861         }
14862         return this;
14863     },
14864
14865     // private
14866     beforeShow : function(){
14867         this.expand();
14868         if(this.fixedcenter){
14869             this.xy = this.el.getCenterXY(true);
14870         }
14871         if(this.modal){
14872             Roo.get(document.body).addClass("x-body-masked");
14873             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14874             this.mask.show();
14875         }
14876         this.constrainXY();
14877     },
14878
14879     // private
14880     animShow : function(){
14881         var b = Roo.get(this.animateTarget).getBox();
14882         this.proxy.setSize(b.width, b.height);
14883         this.proxy.setLocation(b.x, b.y);
14884         this.proxy.show();
14885         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14886                     true, .35, this.showEl.createDelegate(this));
14887     },
14888
14889     /**
14890      * Shows the dialog.
14891      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14892      * @return {Roo.BasicDialog} this
14893      */
14894     show : function(animateTarget){
14895         if (this.fireEvent("beforeshow", this) === false){
14896             return;
14897         }
14898         if(this.syncHeightBeforeShow){
14899             this.syncBodyHeight();
14900         }else if(this.firstShow){
14901             this.firstShow = false;
14902             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14903         }
14904         this.animateTarget = animateTarget || this.animateTarget;
14905         if(!this.el.isVisible()){
14906             this.beforeShow();
14907             if(this.animateTarget && Roo.get(this.animateTarget)){
14908                 this.animShow();
14909             }else{
14910                 this.showEl();
14911             }
14912         }
14913         return this;
14914     },
14915
14916     // private
14917     showEl : function(){
14918         this.proxy.hide();
14919         this.el.setXY(this.xy);
14920         this.el.show();
14921         this.adjustAssets(true);
14922         this.toFront();
14923         this.focus();
14924         // IE peekaboo bug - fix found by Dave Fenwick
14925         if(Roo.isIE){
14926             this.el.repaint();
14927         }
14928         this.fireEvent("show", this);
14929     },
14930
14931     /**
14932      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14933      * dialog itself will receive focus.
14934      */
14935     focus : function(){
14936         if(this.defaultButton){
14937             this.defaultButton.focus();
14938         }else{
14939             this.focusEl.focus();
14940         }
14941     },
14942
14943     // private
14944     constrainXY : function(){
14945         if(this.constraintoviewport !== false){
14946             if(!this.viewSize){
14947                 if(this.container){
14948                     var s = this.container.getSize();
14949                     this.viewSize = [s.width, s.height];
14950                 }else{
14951                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14952                 }
14953             }
14954             var s = Roo.get(this.container||document).getScroll();
14955
14956             var x = this.xy[0], y = this.xy[1];
14957             var w = this.size.width, h = this.size.height;
14958             var vw = this.viewSize[0], vh = this.viewSize[1];
14959             // only move it if it needs it
14960             var moved = false;
14961             // first validate right/bottom
14962             if(x + w > vw+s.left){
14963                 x = vw - w;
14964                 moved = true;
14965             }
14966             if(y + h > vh+s.top){
14967                 y = vh - h;
14968                 moved = true;
14969             }
14970             // then make sure top/left isn't negative
14971             if(x < s.left){
14972                 x = s.left;
14973                 moved = true;
14974             }
14975             if(y < s.top){
14976                 y = s.top;
14977                 moved = true;
14978             }
14979             if(moved){
14980                 // cache xy
14981                 this.xy = [x, y];
14982                 if(this.isVisible()){
14983                     this.el.setLocation(x, y);
14984                     this.adjustAssets();
14985                 }
14986             }
14987         }
14988     },
14989
14990     // private
14991     onDrag : function(){
14992         if(!this.proxyDrag){
14993             this.xy = this.el.getXY();
14994             this.adjustAssets();
14995         }
14996     },
14997
14998     // private
14999     adjustAssets : function(doShow){
15000         var x = this.xy[0], y = this.xy[1];
15001         var w = this.size.width, h = this.size.height;
15002         if(doShow === true){
15003             if(this.shadow){
15004                 this.shadow.show(this.el);
15005             }
15006             if(this.shim){
15007                 this.shim.show();
15008             }
15009         }
15010         if(this.shadow && this.shadow.isVisible()){
15011             this.shadow.show(this.el);
15012         }
15013         if(this.shim && this.shim.isVisible()){
15014             this.shim.setBounds(x, y, w, h);
15015         }
15016     },
15017
15018     // private
15019     adjustViewport : function(w, h){
15020         if(!w || !h){
15021             w = Roo.lib.Dom.getViewWidth();
15022             h = Roo.lib.Dom.getViewHeight();
15023         }
15024         // cache the size
15025         this.viewSize = [w, h];
15026         if(this.modal && this.mask.isVisible()){
15027             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15028             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15029         }
15030         if(this.isVisible()){
15031             this.constrainXY();
15032         }
15033     },
15034
15035     /**
15036      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15037      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15038      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15039      */
15040     destroy : function(removeEl){
15041         if(this.isVisible()){
15042             this.animateTarget = null;
15043             this.hide();
15044         }
15045         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15046         if(this.tabs){
15047             this.tabs.destroy(removeEl);
15048         }
15049         Roo.destroy(
15050              this.shim,
15051              this.proxy,
15052              this.resizer,
15053              this.close,
15054              this.mask
15055         );
15056         if(this.dd){
15057             this.dd.unreg();
15058         }
15059         if(this.buttons){
15060            for(var i = 0, len = this.buttons.length; i < len; i++){
15061                this.buttons[i].destroy();
15062            }
15063         }
15064         this.el.removeAllListeners();
15065         if(removeEl === true){
15066             this.el.update("");
15067             this.el.remove();
15068         }
15069         Roo.DialogManager.unregister(this);
15070     },
15071
15072     // private
15073     startMove : function(){
15074         if(this.proxyDrag){
15075             this.proxy.show();
15076         }
15077         if(this.constraintoviewport !== false){
15078             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15079         }
15080     },
15081
15082     // private
15083     endMove : function(){
15084         if(!this.proxyDrag){
15085             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15086         }else{
15087             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15088             this.proxy.hide();
15089         }
15090         this.refreshSize();
15091         this.adjustAssets();
15092         this.focus();
15093         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15094     },
15095
15096     /**
15097      * Brings this dialog to the front of any other visible dialogs
15098      * @return {Roo.BasicDialog} this
15099      */
15100     toFront : function(){
15101         Roo.DialogManager.bringToFront(this);
15102         return this;
15103     },
15104
15105     /**
15106      * Sends this dialog to the back (under) of any other visible dialogs
15107      * @return {Roo.BasicDialog} this
15108      */
15109     toBack : function(){
15110         Roo.DialogManager.sendToBack(this);
15111         return this;
15112     },
15113
15114     /**
15115      * Centers this dialog in the viewport
15116      * @return {Roo.BasicDialog} this
15117      */
15118     center : function(){
15119         var xy = this.el.getCenterXY(true);
15120         this.moveTo(xy[0], xy[1]);
15121         return this;
15122     },
15123
15124     /**
15125      * Moves the dialog's top-left corner to the specified point
15126      * @param {Number} x
15127      * @param {Number} y
15128      * @return {Roo.BasicDialog} this
15129      */
15130     moveTo : function(x, y){
15131         this.xy = [x,y];
15132         if(this.isVisible()){
15133             this.el.setXY(this.xy);
15134             this.adjustAssets();
15135         }
15136         return this;
15137     },
15138
15139     /**
15140      * Aligns the dialog to the specified element
15141      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15142      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15143      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15144      * @return {Roo.BasicDialog} this
15145      */
15146     alignTo : function(element, position, offsets){
15147         this.xy = this.el.getAlignToXY(element, position, offsets);
15148         if(this.isVisible()){
15149             this.el.setXY(this.xy);
15150             this.adjustAssets();
15151         }
15152         return this;
15153     },
15154
15155     /**
15156      * Anchors an element to another element and realigns it when the window is resized.
15157      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15158      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15159      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15160      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15161      * is a number, it is used as the buffer delay (defaults to 50ms).
15162      * @return {Roo.BasicDialog} this
15163      */
15164     anchorTo : function(el, alignment, offsets, monitorScroll){
15165         var action = function(){
15166             this.alignTo(el, alignment, offsets);
15167         };
15168         Roo.EventManager.onWindowResize(action, this);
15169         var tm = typeof monitorScroll;
15170         if(tm != 'undefined'){
15171             Roo.EventManager.on(window, 'scroll', action, this,
15172                 {buffer: tm == 'number' ? monitorScroll : 50});
15173         }
15174         action.call(this);
15175         return this;
15176     },
15177
15178     /**
15179      * Returns true if the dialog is visible
15180      * @return {Boolean}
15181      */
15182     isVisible : function(){
15183         return this.el.isVisible();
15184     },
15185
15186     // private
15187     animHide : function(callback){
15188         var b = Roo.get(this.animateTarget).getBox();
15189         this.proxy.show();
15190         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15191         this.el.hide();
15192         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15193                     this.hideEl.createDelegate(this, [callback]));
15194     },
15195
15196     /**
15197      * Hides the dialog.
15198      * @param {Function} callback (optional) Function to call when the dialog is hidden
15199      * @return {Roo.BasicDialog} this
15200      */
15201     hide : function(callback){
15202         if (this.fireEvent("beforehide", this) === false){
15203             return;
15204         }
15205         if(this.shadow){
15206             this.shadow.hide();
15207         }
15208         if(this.shim) {
15209           this.shim.hide();
15210         }
15211         // sometimes animateTarget seems to get set.. causing problems...
15212         // this just double checks..
15213         if(this.animateTarget && Roo.get(this.animateTarget)) {
15214            this.animHide(callback);
15215         }else{
15216             this.el.hide();
15217             this.hideEl(callback);
15218         }
15219         return this;
15220     },
15221
15222     // private
15223     hideEl : function(callback){
15224         this.proxy.hide();
15225         if(this.modal){
15226             this.mask.hide();
15227             Roo.get(document.body).removeClass("x-body-masked");
15228         }
15229         this.fireEvent("hide", this);
15230         if(typeof callback == "function"){
15231             callback();
15232         }
15233     },
15234
15235     // private
15236     hideAction : function(){
15237         this.setLeft("-10000px");
15238         this.setTop("-10000px");
15239         this.setStyle("visibility", "hidden");
15240     },
15241
15242     // private
15243     refreshSize : function(){
15244         this.size = this.el.getSize();
15245         this.xy = this.el.getXY();
15246         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15247     },
15248
15249     // private
15250     // z-index is managed by the DialogManager and may be overwritten at any time
15251     setZIndex : function(index){
15252         if(this.modal){
15253             this.mask.setStyle("z-index", index);
15254         }
15255         if(this.shim){
15256             this.shim.setStyle("z-index", ++index);
15257         }
15258         if(this.shadow){
15259             this.shadow.setZIndex(++index);
15260         }
15261         this.el.setStyle("z-index", ++index);
15262         if(this.proxy){
15263             this.proxy.setStyle("z-index", ++index);
15264         }
15265         if(this.resizer){
15266             this.resizer.proxy.setStyle("z-index", ++index);
15267         }
15268
15269         this.lastZIndex = index;
15270     },
15271
15272     /**
15273      * Returns the element for this dialog
15274      * @return {Roo.Element} The underlying dialog Element
15275      */
15276     getEl : function(){
15277         return this.el;
15278     }
15279 });
15280
15281 /**
15282  * @class Roo.DialogManager
15283  * Provides global access to BasicDialogs that have been created and
15284  * support for z-indexing (layering) multiple open dialogs.
15285  */
15286 Roo.DialogManager = function(){
15287     var list = {};
15288     var accessList = [];
15289     var front = null;
15290
15291     // private
15292     var sortDialogs = function(d1, d2){
15293         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15294     };
15295
15296     // private
15297     var orderDialogs = function(){
15298         accessList.sort(sortDialogs);
15299         var seed = Roo.DialogManager.zseed;
15300         for(var i = 0, len = accessList.length; i < len; i++){
15301             var dlg = accessList[i];
15302             if(dlg){
15303                 dlg.setZIndex(seed + (i*10));
15304             }
15305         }
15306     };
15307
15308     return {
15309         /**
15310          * The starting z-index for BasicDialogs (defaults to 9000)
15311          * @type Number The z-index value
15312          */
15313         zseed : 9000,
15314
15315         // private
15316         register : function(dlg){
15317             list[dlg.id] = dlg;
15318             accessList.push(dlg);
15319         },
15320
15321         // private
15322         unregister : function(dlg){
15323             delete list[dlg.id];
15324             var i=0;
15325             var len=0;
15326             if(!accessList.indexOf){
15327                 for(  i = 0, len = accessList.length; i < len; i++){
15328                     if(accessList[i] == dlg){
15329                         accessList.splice(i, 1);
15330                         return;
15331                     }
15332                 }
15333             }else{
15334                  i = accessList.indexOf(dlg);
15335                 if(i != -1){
15336                     accessList.splice(i, 1);
15337                 }
15338             }
15339         },
15340
15341         /**
15342          * Gets a registered dialog by id
15343          * @param {String/Object} id The id of the dialog or a dialog
15344          * @return {Roo.BasicDialog} this
15345          */
15346         get : function(id){
15347             return typeof id == "object" ? id : list[id];
15348         },
15349
15350         /**
15351          * Brings the specified dialog to the front
15352          * @param {String/Object} dlg The id of the dialog or a dialog
15353          * @return {Roo.BasicDialog} this
15354          */
15355         bringToFront : function(dlg){
15356             dlg = this.get(dlg);
15357             if(dlg != front){
15358                 front = dlg;
15359                 dlg._lastAccess = new Date().getTime();
15360                 orderDialogs();
15361             }
15362             return dlg;
15363         },
15364
15365         /**
15366          * Sends the specified dialog to the back
15367          * @param {String/Object} dlg The id of the dialog or a dialog
15368          * @return {Roo.BasicDialog} this
15369          */
15370         sendToBack : function(dlg){
15371             dlg = this.get(dlg);
15372             dlg._lastAccess = -(new Date().getTime());
15373             orderDialogs();
15374             return dlg;
15375         },
15376
15377         /**
15378          * Hides all dialogs
15379          */
15380         hideAll : function(){
15381             for(var id in list){
15382                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15383                     list[id].hide();
15384                 }
15385             }
15386         }
15387     };
15388 }();
15389
15390 /**
15391  * @class Roo.LayoutDialog
15392  * @extends Roo.BasicDialog
15393  * Dialog which provides adjustments for working with a layout in a Dialog.
15394  * Add your necessary layout config options to the dialog's config.<br>
15395  * Example usage (including a nested layout):
15396  * <pre><code>
15397 if(!dialog){
15398     dialog = new Roo.LayoutDialog("download-dlg", {
15399         modal: true,
15400         width:600,
15401         height:450,
15402         shadow:true,
15403         minWidth:500,
15404         minHeight:350,
15405         autoTabs:true,
15406         proxyDrag:true,
15407         // layout config merges with the dialog config
15408         center:{
15409             tabPosition: "top",
15410             alwaysShowTabs: true
15411         }
15412     });
15413     dialog.addKeyListener(27, dialog.hide, dialog);
15414     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15415     dialog.addButton("Build It!", this.getDownload, this);
15416
15417     // we can even add nested layouts
15418     var innerLayout = new Roo.BorderLayout("dl-inner", {
15419         east: {
15420             initialSize: 200,
15421             autoScroll:true,
15422             split:true
15423         },
15424         center: {
15425             autoScroll:true
15426         }
15427     });
15428     innerLayout.beginUpdate();
15429     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15430     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15431     innerLayout.endUpdate(true);
15432
15433     var layout = dialog.getLayout();
15434     layout.beginUpdate();
15435     layout.add("center", new Roo.ContentPanel("standard-panel",
15436                         {title: "Download the Source", fitToFrame:true}));
15437     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15438                {title: "Build your own roo.js"}));
15439     layout.getRegion("center").showPanel(sp);
15440     layout.endUpdate();
15441 }
15442 </code></pre>
15443     * @constructor
15444     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15445     * @param {Object} config configuration options
15446   */
15447 Roo.LayoutDialog = function(el, cfg){
15448     
15449     var config=  cfg;
15450     if (typeof(cfg) == 'undefined') {
15451         config = Roo.apply({}, el);
15452         // not sure why we use documentElement here.. - it should always be body.
15453         // IE7 borks horribly if we use documentElement.
15454         // webkit also does not like documentElement - it creates a body element...
15455         el = Roo.get( document.body || document.documentElement ).createChild();
15456         //config.autoCreate = true;
15457     }
15458     
15459     
15460     config.autoTabs = false;
15461     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15462     this.body.setStyle({overflow:"hidden", position:"relative"});
15463     this.layout = new Roo.BorderLayout(this.body.dom, config);
15464     this.layout.monitorWindowResize = false;
15465     this.el.addClass("x-dlg-auto-layout");
15466     // fix case when center region overwrites center function
15467     this.center = Roo.BasicDialog.prototype.center;
15468     this.on("show", this.layout.layout, this.layout, true);
15469     if (config.items) {
15470         var xitems = config.items;
15471         delete config.items;
15472         Roo.each(xitems, this.addxtype, this);
15473     }
15474     
15475     
15476 };
15477 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15478     /**
15479      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15480      * @deprecated
15481      */
15482     endUpdate : function(){
15483         this.layout.endUpdate();
15484     },
15485
15486     /**
15487      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15488      *  @deprecated
15489      */
15490     beginUpdate : function(){
15491         this.layout.beginUpdate();
15492     },
15493
15494     /**
15495      * Get the BorderLayout for this dialog
15496      * @return {Roo.BorderLayout}
15497      */
15498     getLayout : function(){
15499         return this.layout;
15500     },
15501
15502     showEl : function(){
15503         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15504         if(Roo.isIE7){
15505             this.layout.layout();
15506         }
15507     },
15508
15509     // private
15510     // Use the syncHeightBeforeShow config option to control this automatically
15511     syncBodyHeight : function(){
15512         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15513         if(this.layout){this.layout.layout();}
15514     },
15515     
15516       /**
15517      * Add an xtype element (actually adds to the layout.)
15518      * @return {Object} xdata xtype object data.
15519      */
15520     
15521     addxtype : function(c) {
15522         return this.layout.addxtype(c);
15523     }
15524 });/*
15525  * Based on:
15526  * Ext JS Library 1.1.1
15527  * Copyright(c) 2006-2007, Ext JS, LLC.
15528  *
15529  * Originally Released Under LGPL - original licence link has changed is not relivant.
15530  *
15531  * Fork - LGPL
15532  * <script type="text/javascript">
15533  */
15534  
15535 /**
15536  * @class Roo.MessageBox
15537  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15538  * Example usage:
15539  *<pre><code>
15540 // Basic alert:
15541 Roo.Msg.alert('Status', 'Changes saved successfully.');
15542
15543 // Prompt for user data:
15544 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15545     if (btn == 'ok'){
15546         // process text value...
15547     }
15548 });
15549
15550 // Show a dialog using config options:
15551 Roo.Msg.show({
15552    title:'Save Changes?',
15553    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15554    buttons: Roo.Msg.YESNOCANCEL,
15555    fn: processResult,
15556    animEl: 'elId'
15557 });
15558 </code></pre>
15559  * @singleton
15560  */
15561 Roo.MessageBox = function(){
15562     var dlg, opt, mask, waitTimer;
15563     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15564     var buttons, activeTextEl, bwidth;
15565
15566     // private
15567     var handleButton = function(button){
15568         dlg.hide();
15569         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15570     };
15571
15572     // private
15573     var handleHide = function(){
15574         if(opt && opt.cls){
15575             dlg.el.removeClass(opt.cls);
15576         }
15577         if(waitTimer){
15578             Roo.TaskMgr.stop(waitTimer);
15579             waitTimer = null;
15580         }
15581     };
15582
15583     // private
15584     var updateButtons = function(b){
15585         var width = 0;
15586         if(!b){
15587             buttons["ok"].hide();
15588             buttons["cancel"].hide();
15589             buttons["yes"].hide();
15590             buttons["no"].hide();
15591             dlg.footer.dom.style.display = 'none';
15592             return width;
15593         }
15594         dlg.footer.dom.style.display = '';
15595         for(var k in buttons){
15596             if(typeof buttons[k] != "function"){
15597                 if(b[k]){
15598                     buttons[k].show();
15599                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15600                     width += buttons[k].el.getWidth()+15;
15601                 }else{
15602                     buttons[k].hide();
15603                 }
15604             }
15605         }
15606         return width;
15607     };
15608
15609     // private
15610     var handleEsc = function(d, k, e){
15611         if(opt && opt.closable !== false){
15612             dlg.hide();
15613         }
15614         if(e){
15615             e.stopEvent();
15616         }
15617     };
15618
15619     return {
15620         /**
15621          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15622          * @return {Roo.BasicDialog} The BasicDialog element
15623          */
15624         getDialog : function(){
15625            if(!dlg){
15626                 dlg = new Roo.BasicDialog("x-msg-box", {
15627                     autoCreate : true,
15628                     shadow: true,
15629                     draggable: true,
15630                     resizable:false,
15631                     constraintoviewport:false,
15632                     fixedcenter:true,
15633                     collapsible : false,
15634                     shim:true,
15635                     modal: true,
15636                     width:400, height:100,
15637                     buttonAlign:"center",
15638                     closeClick : function(){
15639                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15640                             handleButton("no");
15641                         }else{
15642                             handleButton("cancel");
15643                         }
15644                     }
15645                 });
15646                 dlg.on("hide", handleHide);
15647                 mask = dlg.mask;
15648                 dlg.addKeyListener(27, handleEsc);
15649                 buttons = {};
15650                 var bt = this.buttonText;
15651                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15652                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15653                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15654                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15655                 bodyEl = dlg.body.createChild({
15656
15657                     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>'
15658                 });
15659                 msgEl = bodyEl.dom.firstChild;
15660                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15661                 textboxEl.enableDisplayMode();
15662                 textboxEl.addKeyListener([10,13], function(){
15663                     if(dlg.isVisible() && opt && opt.buttons){
15664                         if(opt.buttons.ok){
15665                             handleButton("ok");
15666                         }else if(opt.buttons.yes){
15667                             handleButton("yes");
15668                         }
15669                     }
15670                 });
15671                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15672                 textareaEl.enableDisplayMode();
15673                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15674                 progressEl.enableDisplayMode();
15675                 var pf = progressEl.dom.firstChild;
15676                 if (pf) {
15677                     pp = Roo.get(pf.firstChild);
15678                     pp.setHeight(pf.offsetHeight);
15679                 }
15680                 
15681             }
15682             return dlg;
15683         },
15684
15685         /**
15686          * Updates the message box body text
15687          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15688          * the XHTML-compliant non-breaking space character '&amp;#160;')
15689          * @return {Roo.MessageBox} This message box
15690          */
15691         updateText : function(text){
15692             if(!dlg.isVisible() && !opt.width){
15693                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15694             }
15695             msgEl.innerHTML = text || '&#160;';
15696       
15697             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15698             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15699             var w = Math.max(
15700                     Math.min(opt.width || cw , this.maxWidth), 
15701                     Math.max(opt.minWidth || this.minWidth, bwidth)
15702             );
15703             if(opt.prompt){
15704                 activeTextEl.setWidth(w);
15705             }
15706             if(dlg.isVisible()){
15707                 dlg.fixedcenter = false;
15708             }
15709             // to big, make it scroll. = But as usual stupid IE does not support
15710             // !important..
15711             
15712             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15713                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15714                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15715             } else {
15716                 bodyEl.dom.style.height = '';
15717                 bodyEl.dom.style.overflowY = '';
15718             }
15719             if (cw > w) {
15720                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15721             } else {
15722                 bodyEl.dom.style.overflowX = '';
15723             }
15724             
15725             dlg.setContentSize(w, bodyEl.getHeight());
15726             if(dlg.isVisible()){
15727                 dlg.fixedcenter = true;
15728             }
15729             return this;
15730         },
15731
15732         /**
15733          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15734          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15735          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15736          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15737          * @return {Roo.MessageBox} This message box
15738          */
15739         updateProgress : function(value, text){
15740             if(text){
15741                 this.updateText(text);
15742             }
15743             if (pp) { // weird bug on my firefox - for some reason this is not defined
15744                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15745             }
15746             return this;
15747         },        
15748
15749         /**
15750          * Returns true if the message box is currently displayed
15751          * @return {Boolean} True if the message box is visible, else false
15752          */
15753         isVisible : function(){
15754             return dlg && dlg.isVisible();  
15755         },
15756
15757         /**
15758          * Hides the message box if it is displayed
15759          */
15760         hide : function(){
15761             if(this.isVisible()){
15762                 dlg.hide();
15763             }  
15764         },
15765
15766         /**
15767          * Displays a new message box, or reinitializes an existing message box, based on the config options
15768          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15769          * The following config object properties are supported:
15770          * <pre>
15771 Property    Type             Description
15772 ----------  ---------------  ------------------------------------------------------------------------------------
15773 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15774                                    closes (defaults to undefined)
15775 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15776                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15777 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15778                                    progress and wait dialogs will ignore this property and always hide the
15779                                    close button as they can only be closed programmatically.
15780 cls               String           A custom CSS class to apply to the message box element
15781 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15782                                    displayed (defaults to 75)
15783 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15784                                    function will be btn (the name of the button that was clicked, if applicable,
15785                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15786                                    Progress and wait dialogs will ignore this option since they do not respond to
15787                                    user actions and can only be closed programmatically, so any required function
15788                                    should be called by the same code after it closes the dialog.
15789 icon              String           A CSS class that provides a background image to be used as an icon for
15790                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15791 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15792 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15793 modal             Boolean          False to allow user interaction with the page while the message box is
15794                                    displayed (defaults to true)
15795 msg               String           A string that will replace the existing message box body text (defaults
15796                                    to the XHTML-compliant non-breaking space character '&#160;')
15797 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15798 progress          Boolean          True to display a progress bar (defaults to false)
15799 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15800 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15801 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15802 title             String           The title text
15803 value             String           The string value to set into the active textbox element if displayed
15804 wait              Boolean          True to display a progress bar (defaults to false)
15805 width             Number           The width of the dialog in pixels
15806 </pre>
15807          *
15808          * Example usage:
15809          * <pre><code>
15810 Roo.Msg.show({
15811    title: 'Address',
15812    msg: 'Please enter your address:',
15813    width: 300,
15814    buttons: Roo.MessageBox.OKCANCEL,
15815    multiline: true,
15816    fn: saveAddress,
15817    animEl: 'addAddressBtn'
15818 });
15819 </code></pre>
15820          * @param {Object} config Configuration options
15821          * @return {Roo.MessageBox} This message box
15822          */
15823         show : function(options)
15824         {
15825             
15826             // this causes nightmares if you show one dialog after another
15827             // especially on callbacks..
15828              
15829             if(this.isVisible()){
15830                 
15831                 this.hide();
15832                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15833                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15834                 Roo.log("New Dialog Message:" +  options.msg )
15835                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15836                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15837                 
15838             }
15839             var d = this.getDialog();
15840             opt = options;
15841             d.setTitle(opt.title || "&#160;");
15842             d.close.setDisplayed(opt.closable !== false);
15843             activeTextEl = textboxEl;
15844             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15845             if(opt.prompt){
15846                 if(opt.multiline){
15847                     textboxEl.hide();
15848                     textareaEl.show();
15849                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15850                         opt.multiline : this.defaultTextHeight);
15851                     activeTextEl = textareaEl;
15852                 }else{
15853                     textboxEl.show();
15854                     textareaEl.hide();
15855                 }
15856             }else{
15857                 textboxEl.hide();
15858                 textareaEl.hide();
15859             }
15860             progressEl.setDisplayed(opt.progress === true);
15861             this.updateProgress(0);
15862             activeTextEl.dom.value = opt.value || "";
15863             if(opt.prompt){
15864                 dlg.setDefaultButton(activeTextEl);
15865             }else{
15866                 var bs = opt.buttons;
15867                 var db = null;
15868                 if(bs && bs.ok){
15869                     db = buttons["ok"];
15870                 }else if(bs && bs.yes){
15871                     db = buttons["yes"];
15872                 }
15873                 dlg.setDefaultButton(db);
15874             }
15875             bwidth = updateButtons(opt.buttons);
15876             this.updateText(opt.msg);
15877             if(opt.cls){
15878                 d.el.addClass(opt.cls);
15879             }
15880             d.proxyDrag = opt.proxyDrag === true;
15881             d.modal = opt.modal !== false;
15882             d.mask = opt.modal !== false ? mask : false;
15883             if(!d.isVisible()){
15884                 // force it to the end of the z-index stack so it gets a cursor in FF
15885                 document.body.appendChild(dlg.el.dom);
15886                 d.animateTarget = null;
15887                 d.show(options.animEl);
15888             }
15889             return this;
15890         },
15891
15892         /**
15893          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15894          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15895          * and closing the message box when the process is complete.
15896          * @param {String} title The title bar text
15897          * @param {String} msg The message box body text
15898          * @return {Roo.MessageBox} This message box
15899          */
15900         progress : function(title, msg){
15901             this.show({
15902                 title : title,
15903                 msg : msg,
15904                 buttons: false,
15905                 progress:true,
15906                 closable:false,
15907                 minWidth: this.minProgressWidth,
15908                 modal : true
15909             });
15910             return this;
15911         },
15912
15913         /**
15914          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15915          * If a callback function is passed it will be called after the user clicks the button, and the
15916          * id of the button that was clicked will be passed as the only parameter to the callback
15917          * (could also be the top-right close button).
15918          * @param {String} title The title bar text
15919          * @param {String} msg The message box body text
15920          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15921          * @param {Object} scope (optional) The scope of the callback function
15922          * @return {Roo.MessageBox} This message box
15923          */
15924         alert : function(title, msg, fn, scope){
15925             this.show({
15926                 title : title,
15927                 msg : msg,
15928                 buttons: this.OK,
15929                 fn: fn,
15930                 scope : scope,
15931                 modal : true
15932             });
15933             return this;
15934         },
15935
15936         /**
15937          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15938          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15939          * You are responsible for closing the message box when the process is complete.
15940          * @param {String} msg The message box body text
15941          * @param {String} title (optional) The title bar text
15942          * @return {Roo.MessageBox} This message box
15943          */
15944         wait : function(msg, title){
15945             this.show({
15946                 title : title,
15947                 msg : msg,
15948                 buttons: false,
15949                 closable:false,
15950                 progress:true,
15951                 modal:true,
15952                 width:300,
15953                 wait:true
15954             });
15955             waitTimer = Roo.TaskMgr.start({
15956                 run: function(i){
15957                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15958                 },
15959                 interval: 1000
15960             });
15961             return this;
15962         },
15963
15964         /**
15965          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15966          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15967          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15968          * @param {String} title The title bar text
15969          * @param {String} msg The message box body text
15970          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15971          * @param {Object} scope (optional) The scope of the callback function
15972          * @return {Roo.MessageBox} This message box
15973          */
15974         confirm : function(title, msg, fn, scope){
15975             this.show({
15976                 title : title,
15977                 msg : msg,
15978                 buttons: this.YESNO,
15979                 fn: fn,
15980                 scope : scope,
15981                 modal : true
15982             });
15983             return this;
15984         },
15985
15986         /**
15987          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15988          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
15989          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
15990          * (could also be the top-right close button) and the text that was entered will be passed as the two
15991          * parameters to the callback.
15992          * @param {String} title The title bar text
15993          * @param {String} msg The message box body text
15994          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15995          * @param {Object} scope (optional) The scope of the callback function
15996          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
15997          * property, or the height in pixels to create the textbox (defaults to false / single-line)
15998          * @return {Roo.MessageBox} This message box
15999          */
16000         prompt : function(title, msg, fn, scope, multiline){
16001             this.show({
16002                 title : title,
16003                 msg : msg,
16004                 buttons: this.OKCANCEL,
16005                 fn: fn,
16006                 minWidth:250,
16007                 scope : scope,
16008                 prompt:true,
16009                 multiline: multiline,
16010                 modal : true
16011             });
16012             return this;
16013         },
16014
16015         /**
16016          * Button config that displays a single OK button
16017          * @type Object
16018          */
16019         OK : {ok:true},
16020         /**
16021          * Button config that displays Yes and No buttons
16022          * @type Object
16023          */
16024         YESNO : {yes:true, no:true},
16025         /**
16026          * Button config that displays OK and Cancel buttons
16027          * @type Object
16028          */
16029         OKCANCEL : {ok:true, cancel:true},
16030         /**
16031          * Button config that displays Yes, No and Cancel buttons
16032          * @type Object
16033          */
16034         YESNOCANCEL : {yes:true, no:true, cancel:true},
16035
16036         /**
16037          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16038          * @type Number
16039          */
16040         defaultTextHeight : 75,
16041         /**
16042          * The maximum width in pixels of the message box (defaults to 600)
16043          * @type Number
16044          */
16045         maxWidth : 600,
16046         /**
16047          * The minimum width in pixels of the message box (defaults to 100)
16048          * @type Number
16049          */
16050         minWidth : 100,
16051         /**
16052          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16053          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16054          * @type Number
16055          */
16056         minProgressWidth : 250,
16057         /**
16058          * An object containing the default button text strings that can be overriden for localized language support.
16059          * Supported properties are: ok, cancel, yes and no.
16060          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16061          * @type Object
16062          */
16063         buttonText : {
16064             ok : "OK",
16065             cancel : "Cancel",
16066             yes : "Yes",
16067             no : "No"
16068         }
16069     };
16070 }();
16071
16072 /**
16073  * Shorthand for {@link Roo.MessageBox}
16074  */
16075 Roo.Msg = Roo.MessageBox;/*
16076  * Based on:
16077  * Ext JS Library 1.1.1
16078  * Copyright(c) 2006-2007, Ext JS, LLC.
16079  *
16080  * Originally Released Under LGPL - original licence link has changed is not relivant.
16081  *
16082  * Fork - LGPL
16083  * <script type="text/javascript">
16084  */
16085 /**
16086  * @class Roo.QuickTips
16087  * Provides attractive and customizable tooltips for any element.
16088  * @singleton
16089  */
16090 Roo.QuickTips = function(){
16091     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16092     var ce, bd, xy, dd;
16093     var visible = false, disabled = true, inited = false;
16094     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16095     
16096     var onOver = function(e){
16097         if(disabled){
16098             return;
16099         }
16100         var t = e.getTarget();
16101         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16102             return;
16103         }
16104         if(ce && t == ce.el){
16105             clearTimeout(hideProc);
16106             return;
16107         }
16108         if(t && tagEls[t.id]){
16109             tagEls[t.id].el = t;
16110             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16111             return;
16112         }
16113         var ttp, et = Roo.fly(t);
16114         var ns = cfg.namespace;
16115         if(tm.interceptTitles && t.title){
16116             ttp = t.title;
16117             t.qtip = ttp;
16118             t.removeAttribute("title");
16119             e.preventDefault();
16120         }else{
16121             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16122         }
16123         if(ttp){
16124             showProc = show.defer(tm.showDelay, tm, [{
16125                 el: t, 
16126                 text: ttp, 
16127                 width: et.getAttributeNS(ns, cfg.width),
16128                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16129                 title: et.getAttributeNS(ns, cfg.title),
16130                     cls: et.getAttributeNS(ns, cfg.cls)
16131             }]);
16132         }
16133     };
16134     
16135     var onOut = function(e){
16136         clearTimeout(showProc);
16137         var t = e.getTarget();
16138         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16139             hideProc = setTimeout(hide, tm.hideDelay);
16140         }
16141     };
16142     
16143     var onMove = function(e){
16144         if(disabled){
16145             return;
16146         }
16147         xy = e.getXY();
16148         xy[1] += 18;
16149         if(tm.trackMouse && ce){
16150             el.setXY(xy);
16151         }
16152     };
16153     
16154     var onDown = function(e){
16155         clearTimeout(showProc);
16156         clearTimeout(hideProc);
16157         if(!e.within(el)){
16158             if(tm.hideOnClick){
16159                 hide();
16160                 tm.disable();
16161                 tm.enable.defer(100, tm);
16162             }
16163         }
16164     };
16165     
16166     var getPad = function(){
16167         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16168     };
16169
16170     var show = function(o){
16171         if(disabled){
16172             return;
16173         }
16174         clearTimeout(dismissProc);
16175         ce = o;
16176         if(removeCls){ // in case manually hidden
16177             el.removeClass(removeCls);
16178             removeCls = null;
16179         }
16180         if(ce.cls){
16181             el.addClass(ce.cls);
16182             removeCls = ce.cls;
16183         }
16184         if(ce.title){
16185             tipTitle.update(ce.title);
16186             tipTitle.show();
16187         }else{
16188             tipTitle.update('');
16189             tipTitle.hide();
16190         }
16191         el.dom.style.width  = tm.maxWidth+'px';
16192         //tipBody.dom.style.width = '';
16193         tipBodyText.update(o.text);
16194         var p = getPad(), w = ce.width;
16195         if(!w){
16196             var td = tipBodyText.dom;
16197             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16198             if(aw > tm.maxWidth){
16199                 w = tm.maxWidth;
16200             }else if(aw < tm.minWidth){
16201                 w = tm.minWidth;
16202             }else{
16203                 w = aw;
16204             }
16205         }
16206         //tipBody.setWidth(w);
16207         el.setWidth(parseInt(w, 10) + p);
16208         if(ce.autoHide === false){
16209             close.setDisplayed(true);
16210             if(dd){
16211                 dd.unlock();
16212             }
16213         }else{
16214             close.setDisplayed(false);
16215             if(dd){
16216                 dd.lock();
16217             }
16218         }
16219         if(xy){
16220             el.avoidY = xy[1]-18;
16221             el.setXY(xy);
16222         }
16223         if(tm.animate){
16224             el.setOpacity(.1);
16225             el.setStyle("visibility", "visible");
16226             el.fadeIn({callback: afterShow});
16227         }else{
16228             afterShow();
16229         }
16230     };
16231     
16232     var afterShow = function(){
16233         if(ce){
16234             el.show();
16235             esc.enable();
16236             if(tm.autoDismiss && ce.autoHide !== false){
16237                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16238             }
16239         }
16240     };
16241     
16242     var hide = function(noanim){
16243         clearTimeout(dismissProc);
16244         clearTimeout(hideProc);
16245         ce = null;
16246         if(el.isVisible()){
16247             esc.disable();
16248             if(noanim !== true && tm.animate){
16249                 el.fadeOut({callback: afterHide});
16250             }else{
16251                 afterHide();
16252             } 
16253         }
16254     };
16255     
16256     var afterHide = function(){
16257         el.hide();
16258         if(removeCls){
16259             el.removeClass(removeCls);
16260             removeCls = null;
16261         }
16262     };
16263     
16264     return {
16265         /**
16266         * @cfg {Number} minWidth
16267         * The minimum width of the quick tip (defaults to 40)
16268         */
16269        minWidth : 40,
16270         /**
16271         * @cfg {Number} maxWidth
16272         * The maximum width of the quick tip (defaults to 300)
16273         */
16274        maxWidth : 300,
16275         /**
16276         * @cfg {Boolean} interceptTitles
16277         * True to automatically use the element's DOM title value if available (defaults to false)
16278         */
16279        interceptTitles : false,
16280         /**
16281         * @cfg {Boolean} trackMouse
16282         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16283         */
16284        trackMouse : false,
16285         /**
16286         * @cfg {Boolean} hideOnClick
16287         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16288         */
16289        hideOnClick : true,
16290         /**
16291         * @cfg {Number} showDelay
16292         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16293         */
16294        showDelay : 500,
16295         /**
16296         * @cfg {Number} hideDelay
16297         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16298         */
16299        hideDelay : 200,
16300         /**
16301         * @cfg {Boolean} autoHide
16302         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16303         * Used in conjunction with hideDelay.
16304         */
16305        autoHide : true,
16306         /**
16307         * @cfg {Boolean}
16308         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16309         * (defaults to true).  Used in conjunction with autoDismissDelay.
16310         */
16311        autoDismiss : true,
16312         /**
16313         * @cfg {Number}
16314         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16315         */
16316        autoDismissDelay : 5000,
16317        /**
16318         * @cfg {Boolean} animate
16319         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16320         */
16321        animate : false,
16322
16323        /**
16324         * @cfg {String} title
16325         * Title text to display (defaults to '').  This can be any valid HTML markup.
16326         */
16327         title: '',
16328        /**
16329         * @cfg {String} text
16330         * Body text to display (defaults to '').  This can be any valid HTML markup.
16331         */
16332         text : '',
16333        /**
16334         * @cfg {String} cls
16335         * A CSS class to apply to the base quick tip element (defaults to '').
16336         */
16337         cls : '',
16338        /**
16339         * @cfg {Number} width
16340         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16341         * minWidth or maxWidth.
16342         */
16343         width : null,
16344
16345     /**
16346      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16347      * or display QuickTips in a page.
16348      */
16349        init : function(){
16350           tm = Roo.QuickTips;
16351           cfg = tm.tagConfig;
16352           if(!inited){
16353               if(!Roo.isReady){ // allow calling of init() before onReady
16354                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16355                   return;
16356               }
16357               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16358               el.fxDefaults = {stopFx: true};
16359               // maximum custom styling
16360               //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>');
16361               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>');              
16362               tipTitle = el.child('h3');
16363               tipTitle.enableDisplayMode("block");
16364               tipBody = el.child('div.x-tip-bd');
16365               tipBodyText = el.child('div.x-tip-bd-inner');
16366               //bdLeft = el.child('div.x-tip-bd-left');
16367               //bdRight = el.child('div.x-tip-bd-right');
16368               close = el.child('div.x-tip-close');
16369               close.enableDisplayMode("block");
16370               close.on("click", hide);
16371               var d = Roo.get(document);
16372               d.on("mousedown", onDown);
16373               d.on("mouseover", onOver);
16374               d.on("mouseout", onOut);
16375               d.on("mousemove", onMove);
16376               esc = d.addKeyListener(27, hide);
16377               esc.disable();
16378               if(Roo.dd.DD){
16379                   dd = el.initDD("default", null, {
16380                       onDrag : function(){
16381                           el.sync();  
16382                       }
16383                   });
16384                   dd.setHandleElId(tipTitle.id);
16385                   dd.lock();
16386               }
16387               inited = true;
16388           }
16389           this.enable(); 
16390        },
16391
16392     /**
16393      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16394      * are supported:
16395      * <pre>
16396 Property    Type                   Description
16397 ----------  ---------------------  ------------------------------------------------------------------------
16398 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16399      * </ul>
16400      * @param {Object} config The config object
16401      */
16402        register : function(config){
16403            var cs = config instanceof Array ? config : arguments;
16404            for(var i = 0, len = cs.length; i < len; i++) {
16405                var c = cs[i];
16406                var target = c.target;
16407                if(target){
16408                    if(target instanceof Array){
16409                        for(var j = 0, jlen = target.length; j < jlen; j++){
16410                            tagEls[target[j]] = c;
16411                        }
16412                    }else{
16413                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16414                    }
16415                }
16416            }
16417        },
16418
16419     /**
16420      * Removes this quick tip from its element and destroys it.
16421      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16422      */
16423        unregister : function(el){
16424            delete tagEls[Roo.id(el)];
16425        },
16426
16427     /**
16428      * Enable this quick tip.
16429      */
16430        enable : function(){
16431            if(inited && disabled){
16432                locks.pop();
16433                if(locks.length < 1){
16434                    disabled = false;
16435                }
16436            }
16437        },
16438
16439     /**
16440      * Disable this quick tip.
16441      */
16442        disable : function(){
16443           disabled = true;
16444           clearTimeout(showProc);
16445           clearTimeout(hideProc);
16446           clearTimeout(dismissProc);
16447           if(ce){
16448               hide(true);
16449           }
16450           locks.push(1);
16451        },
16452
16453     /**
16454      * Returns true if the quick tip is enabled, else false.
16455      */
16456        isEnabled : function(){
16457             return !disabled;
16458        },
16459
16460         // private
16461        tagConfig : {
16462            namespace : "ext",
16463            attribute : "qtip",
16464            width : "width",
16465            target : "target",
16466            title : "qtitle",
16467            hide : "hide",
16468            cls : "qclass"
16469        }
16470    };
16471 }();
16472
16473 // backwards compat
16474 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16475  * Based on:
16476  * Ext JS Library 1.1.1
16477  * Copyright(c) 2006-2007, Ext JS, LLC.
16478  *
16479  * Originally Released Under LGPL - original licence link has changed is not relivant.
16480  *
16481  * Fork - LGPL
16482  * <script type="text/javascript">
16483  */
16484  
16485
16486 /**
16487  * @class Roo.tree.TreePanel
16488  * @extends Roo.data.Tree
16489
16490  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16491  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16492  * @cfg {Boolean} enableDD true to enable drag and drop
16493  * @cfg {Boolean} enableDrag true to enable just drag
16494  * @cfg {Boolean} enableDrop true to enable just drop
16495  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16496  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16497  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16498  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16499  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16500  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16501  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16502  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16503  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16504  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16505  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16506  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16507  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16508  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16509  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / 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>
16510  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / 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>
16511  * 
16512  * @constructor
16513  * @param {String/HTMLElement/Element} el The container element
16514  * @param {Object} config
16515  */
16516 Roo.tree.TreePanel = function(el, config){
16517     var root = false;
16518     var loader = false;
16519     if (config.root) {
16520         root = config.root;
16521         delete config.root;
16522     }
16523     if (config.loader) {
16524         loader = config.loader;
16525         delete config.loader;
16526     }
16527     
16528     Roo.apply(this, config);
16529     Roo.tree.TreePanel.superclass.constructor.call(this);
16530     this.el = Roo.get(el);
16531     this.el.addClass('x-tree');
16532     //console.log(root);
16533     if (root) {
16534         this.setRootNode( Roo.factory(root, Roo.tree));
16535     }
16536     if (loader) {
16537         this.loader = Roo.factory(loader, Roo.tree);
16538     }
16539    /**
16540     * Read-only. The id of the container element becomes this TreePanel's id.
16541     */
16542     this.id = this.el.id;
16543     this.addEvents({
16544         /**
16545         * @event beforeload
16546         * Fires before a node is loaded, return false to cancel
16547         * @param {Node} node The node being loaded
16548         */
16549         "beforeload" : true,
16550         /**
16551         * @event load
16552         * Fires when a node is loaded
16553         * @param {Node} node The node that was loaded
16554         */
16555         "load" : true,
16556         /**
16557         * @event textchange
16558         * Fires when the text for a node is changed
16559         * @param {Node} node The node
16560         * @param {String} text The new text
16561         * @param {String} oldText The old text
16562         */
16563         "textchange" : true,
16564         /**
16565         * @event beforeexpand
16566         * Fires before a node is expanded, return false to cancel.
16567         * @param {Node} node The node
16568         * @param {Boolean} deep
16569         * @param {Boolean} anim
16570         */
16571         "beforeexpand" : true,
16572         /**
16573         * @event beforecollapse
16574         * Fires before a node is collapsed, return false to cancel.
16575         * @param {Node} node The node
16576         * @param {Boolean} deep
16577         * @param {Boolean} anim
16578         */
16579         "beforecollapse" : true,
16580         /**
16581         * @event expand
16582         * Fires when a node is expanded
16583         * @param {Node} node The node
16584         */
16585         "expand" : true,
16586         /**
16587         * @event disabledchange
16588         * Fires when the disabled status of a node changes
16589         * @param {Node} node The node
16590         * @param {Boolean} disabled
16591         */
16592         "disabledchange" : true,
16593         /**
16594         * @event collapse
16595         * Fires when a node is collapsed
16596         * @param {Node} node The node
16597         */
16598         "collapse" : true,
16599         /**
16600         * @event beforeclick
16601         * Fires before click processing on a node. Return false to cancel the default action.
16602         * @param {Node} node The node
16603         * @param {Roo.EventObject} e The event object
16604         */
16605         "beforeclick":true,
16606         /**
16607         * @event checkchange
16608         * Fires when a node with a checkbox's checked property changes
16609         * @param {Node} this This node
16610         * @param {Boolean} checked
16611         */
16612         "checkchange":true,
16613         /**
16614         * @event click
16615         * Fires when a node is clicked
16616         * @param {Node} node The node
16617         * @param {Roo.EventObject} e The event object
16618         */
16619         "click":true,
16620         /**
16621         * @event dblclick
16622         * Fires when a node is double clicked
16623         * @param {Node} node The node
16624         * @param {Roo.EventObject} e The event object
16625         */
16626         "dblclick":true,
16627         /**
16628         * @event contextmenu
16629         * Fires when a node is right clicked
16630         * @param {Node} node The node
16631         * @param {Roo.EventObject} e The event object
16632         */
16633         "contextmenu":true,
16634         /**
16635         * @event beforechildrenrendered
16636         * Fires right before the child nodes for a node are rendered
16637         * @param {Node} node The node
16638         */
16639         "beforechildrenrendered":true,
16640         /**
16641         * @event startdrag
16642         * Fires when a node starts being dragged
16643         * @param {Roo.tree.TreePanel} this
16644         * @param {Roo.tree.TreeNode} node
16645         * @param {event} e The raw browser event
16646         */ 
16647        "startdrag" : true,
16648        /**
16649         * @event enddrag
16650         * Fires when a drag operation is complete
16651         * @param {Roo.tree.TreePanel} this
16652         * @param {Roo.tree.TreeNode} node
16653         * @param {event} e The raw browser event
16654         */
16655        "enddrag" : true,
16656        /**
16657         * @event dragdrop
16658         * Fires when a dragged node is dropped on a valid DD target
16659         * @param {Roo.tree.TreePanel} this
16660         * @param {Roo.tree.TreeNode} node
16661         * @param {DD} dd The dd it was dropped on
16662         * @param {event} e The raw browser event
16663         */
16664        "dragdrop" : true,
16665        /**
16666         * @event beforenodedrop
16667         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16668         * passed to handlers has the following properties:<br />
16669         * <ul style="padding:5px;padding-left:16px;">
16670         * <li>tree - The TreePanel</li>
16671         * <li>target - The node being targeted for the drop</li>
16672         * <li>data - The drag data from the drag source</li>
16673         * <li>point - The point of the drop - append, above or below</li>
16674         * <li>source - The drag source</li>
16675         * <li>rawEvent - Raw mouse event</li>
16676         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16677         * to be inserted by setting them on this object.</li>
16678         * <li>cancel - Set this to true to cancel the drop.</li>
16679         * </ul>
16680         * @param {Object} dropEvent
16681         */
16682        "beforenodedrop" : true,
16683        /**
16684         * @event nodedrop
16685         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16686         * passed to handlers has the following properties:<br />
16687         * <ul style="padding:5px;padding-left:16px;">
16688         * <li>tree - The TreePanel</li>
16689         * <li>target - The node being targeted for the drop</li>
16690         * <li>data - The drag data from the drag source</li>
16691         * <li>point - The point of the drop - append, above or below</li>
16692         * <li>source - The drag source</li>
16693         * <li>rawEvent - Raw mouse event</li>
16694         * <li>dropNode - Dropped node(s).</li>
16695         * </ul>
16696         * @param {Object} dropEvent
16697         */
16698        "nodedrop" : true,
16699         /**
16700         * @event nodedragover
16701         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16702         * passed to handlers has the following properties:<br />
16703         * <ul style="padding:5px;padding-left:16px;">
16704         * <li>tree - The TreePanel</li>
16705         * <li>target - The node being targeted for the drop</li>
16706         * <li>data - The drag data from the drag source</li>
16707         * <li>point - The point of the drop - append, above or below</li>
16708         * <li>source - The drag source</li>
16709         * <li>rawEvent - Raw mouse event</li>
16710         * <li>dropNode - Drop node(s) provided by the source.</li>
16711         * <li>cancel - Set this to true to signal drop not allowed.</li>
16712         * </ul>
16713         * @param {Object} dragOverEvent
16714         */
16715        "nodedragover" : true
16716         
16717     });
16718     if(this.singleExpand){
16719        this.on("beforeexpand", this.restrictExpand, this);
16720     }
16721     if (this.editor) {
16722         this.editor.tree = this;
16723         this.editor = Roo.factory(this.editor, Roo.tree);
16724     }
16725     
16726     if (this.selModel) {
16727         this.selModel = Roo.factory(this.selModel, Roo.tree);
16728     }
16729    
16730 };
16731 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16732     rootVisible : true,
16733     animate: Roo.enableFx,
16734     lines : true,
16735     enableDD : false,
16736     hlDrop : Roo.enableFx,
16737   
16738     renderer: false,
16739     
16740     rendererTip: false,
16741     // private
16742     restrictExpand : function(node){
16743         var p = node.parentNode;
16744         if(p){
16745             if(p.expandedChild && p.expandedChild.parentNode == p){
16746                 p.expandedChild.collapse();
16747             }
16748             p.expandedChild = node;
16749         }
16750     },
16751
16752     // private override
16753     setRootNode : function(node){
16754         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16755         if(!this.rootVisible){
16756             node.ui = new Roo.tree.RootTreeNodeUI(node);
16757         }
16758         return node;
16759     },
16760
16761     /**
16762      * Returns the container element for this TreePanel
16763      */
16764     getEl : function(){
16765         return this.el;
16766     },
16767
16768     /**
16769      * Returns the default TreeLoader for this TreePanel
16770      */
16771     getLoader : function(){
16772         return this.loader;
16773     },
16774
16775     /**
16776      * Expand all nodes
16777      */
16778     expandAll : function(){
16779         this.root.expand(true);
16780     },
16781
16782     /**
16783      * Collapse all nodes
16784      */
16785     collapseAll : function(){
16786         this.root.collapse(true);
16787     },
16788
16789     /**
16790      * Returns the selection model used by this TreePanel
16791      */
16792     getSelectionModel : function(){
16793         if(!this.selModel){
16794             this.selModel = new Roo.tree.DefaultSelectionModel();
16795         }
16796         return this.selModel;
16797     },
16798
16799     /**
16800      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16801      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16802      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16803      * @return {Array}
16804      */
16805     getChecked : function(a, startNode){
16806         startNode = startNode || this.root;
16807         var r = [];
16808         var f = function(){
16809             if(this.attributes.checked){
16810                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16811             }
16812         }
16813         startNode.cascade(f);
16814         return r;
16815     },
16816
16817     /**
16818      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16819      * @param {String} path
16820      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16821      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16822      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16823      */
16824     expandPath : function(path, attr, callback){
16825         attr = attr || "id";
16826         var keys = path.split(this.pathSeparator);
16827         var curNode = this.root;
16828         if(curNode.attributes[attr] != keys[1]){ // invalid root
16829             if(callback){
16830                 callback(false, null);
16831             }
16832             return;
16833         }
16834         var index = 1;
16835         var f = function(){
16836             if(++index == keys.length){
16837                 if(callback){
16838                     callback(true, curNode);
16839                 }
16840                 return;
16841             }
16842             var c = curNode.findChild(attr, keys[index]);
16843             if(!c){
16844                 if(callback){
16845                     callback(false, curNode);
16846                 }
16847                 return;
16848             }
16849             curNode = c;
16850             c.expand(false, false, f);
16851         };
16852         curNode.expand(false, false, f);
16853     },
16854
16855     /**
16856      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16857      * @param {String} path
16858      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16859      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16860      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16861      */
16862     selectPath : function(path, attr, callback){
16863         attr = attr || "id";
16864         var keys = path.split(this.pathSeparator);
16865         var v = keys.pop();
16866         if(keys.length > 0){
16867             var f = function(success, node){
16868                 if(success && node){
16869                     var n = node.findChild(attr, v);
16870                     if(n){
16871                         n.select();
16872                         if(callback){
16873                             callback(true, n);
16874                         }
16875                     }else if(callback){
16876                         callback(false, n);
16877                     }
16878                 }else{
16879                     if(callback){
16880                         callback(false, n);
16881                     }
16882                 }
16883             };
16884             this.expandPath(keys.join(this.pathSeparator), attr, f);
16885         }else{
16886             this.root.select();
16887             if(callback){
16888                 callback(true, this.root);
16889             }
16890         }
16891     },
16892
16893     getTreeEl : function(){
16894         return this.el;
16895     },
16896
16897     /**
16898      * Trigger rendering of this TreePanel
16899      */
16900     render : function(){
16901         if (this.innerCt) {
16902             return this; // stop it rendering more than once!!
16903         }
16904         
16905         this.innerCt = this.el.createChild({tag:"ul",
16906                cls:"x-tree-root-ct " +
16907                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16908
16909         if(this.containerScroll){
16910             Roo.dd.ScrollManager.register(this.el);
16911         }
16912         if((this.enableDD || this.enableDrop) && !this.dropZone){
16913            /**
16914             * The dropZone used by this tree if drop is enabled
16915             * @type Roo.tree.TreeDropZone
16916             */
16917              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16918                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16919            });
16920         }
16921         if((this.enableDD || this.enableDrag) && !this.dragZone){
16922            /**
16923             * The dragZone used by this tree if drag is enabled
16924             * @type Roo.tree.TreeDragZone
16925             */
16926             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16927                ddGroup: this.ddGroup || "TreeDD",
16928                scroll: this.ddScroll
16929            });
16930         }
16931         this.getSelectionModel().init(this);
16932         if (!this.root) {
16933             Roo.log("ROOT not set in tree");
16934             return this;
16935         }
16936         this.root.render();
16937         if(!this.rootVisible){
16938             this.root.renderChildren();
16939         }
16940         return this;
16941     }
16942 });/*
16943  * Based on:
16944  * Ext JS Library 1.1.1
16945  * Copyright(c) 2006-2007, Ext JS, LLC.
16946  *
16947  * Originally Released Under LGPL - original licence link has changed is not relivant.
16948  *
16949  * Fork - LGPL
16950  * <script type="text/javascript">
16951  */
16952  
16953
16954 /**
16955  * @class Roo.tree.DefaultSelectionModel
16956  * @extends Roo.util.Observable
16957  * The default single selection for a TreePanel.
16958  * @param {Object} cfg Configuration
16959  */
16960 Roo.tree.DefaultSelectionModel = function(cfg){
16961    this.selNode = null;
16962    
16963    
16964    
16965    this.addEvents({
16966        /**
16967         * @event selectionchange
16968         * Fires when the selected node changes
16969         * @param {DefaultSelectionModel} this
16970         * @param {TreeNode} node the new selection
16971         */
16972        "selectionchange" : true,
16973
16974        /**
16975         * @event beforeselect
16976         * Fires before the selected node changes, return false to cancel the change
16977         * @param {DefaultSelectionModel} this
16978         * @param {TreeNode} node the new selection
16979         * @param {TreeNode} node the old selection
16980         */
16981        "beforeselect" : true
16982    });
16983    
16984     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16985 };
16986
16987 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16988     init : function(tree){
16989         this.tree = tree;
16990         tree.getTreeEl().on("keydown", this.onKeyDown, this);
16991         tree.on("click", this.onNodeClick, this);
16992     },
16993     
16994     onNodeClick : function(node, e){
16995         if (e.ctrlKey && this.selNode == node)  {
16996             this.unselect(node);
16997             return;
16998         }
16999         this.select(node);
17000     },
17001     
17002     /**
17003      * Select a node.
17004      * @param {TreeNode} node The node to select
17005      * @return {TreeNode} The selected node
17006      */
17007     select : function(node){
17008         var last = this.selNode;
17009         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17010             if(last){
17011                 last.ui.onSelectedChange(false);
17012             }
17013             this.selNode = node;
17014             node.ui.onSelectedChange(true);
17015             this.fireEvent("selectionchange", this, node, last);
17016         }
17017         return node;
17018     },
17019     
17020     /**
17021      * Deselect a node.
17022      * @param {TreeNode} node The node to unselect
17023      */
17024     unselect : function(node){
17025         if(this.selNode == node){
17026             this.clearSelections();
17027         }    
17028     },
17029     
17030     /**
17031      * Clear all selections
17032      */
17033     clearSelections : function(){
17034         var n = this.selNode;
17035         if(n){
17036             n.ui.onSelectedChange(false);
17037             this.selNode = null;
17038             this.fireEvent("selectionchange", this, null);
17039         }
17040         return n;
17041     },
17042     
17043     /**
17044      * Get the selected node
17045      * @return {TreeNode} The selected node
17046      */
17047     getSelectedNode : function(){
17048         return this.selNode;    
17049     },
17050     
17051     /**
17052      * Returns true if the node is selected
17053      * @param {TreeNode} node The node to check
17054      * @return {Boolean}
17055      */
17056     isSelected : function(node){
17057         return this.selNode == node;  
17058     },
17059
17060     /**
17061      * Selects the node above the selected node in the tree, intelligently walking the nodes
17062      * @return TreeNode The new selection
17063      */
17064     selectPrevious : function(){
17065         var s = this.selNode || this.lastSelNode;
17066         if(!s){
17067             return null;
17068         }
17069         var ps = s.previousSibling;
17070         if(ps){
17071             if(!ps.isExpanded() || ps.childNodes.length < 1){
17072                 return this.select(ps);
17073             } else{
17074                 var lc = ps.lastChild;
17075                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17076                     lc = lc.lastChild;
17077                 }
17078                 return this.select(lc);
17079             }
17080         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17081             return this.select(s.parentNode);
17082         }
17083         return null;
17084     },
17085
17086     /**
17087      * Selects the node above the selected node in the tree, intelligently walking the nodes
17088      * @return TreeNode The new selection
17089      */
17090     selectNext : function(){
17091         var s = this.selNode || this.lastSelNode;
17092         if(!s){
17093             return null;
17094         }
17095         if(s.firstChild && s.isExpanded()){
17096              return this.select(s.firstChild);
17097          }else if(s.nextSibling){
17098              return this.select(s.nextSibling);
17099          }else if(s.parentNode){
17100             var newS = null;
17101             s.parentNode.bubble(function(){
17102                 if(this.nextSibling){
17103                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17104                     return false;
17105                 }
17106             });
17107             return newS;
17108          }
17109         return null;
17110     },
17111
17112     onKeyDown : function(e){
17113         var s = this.selNode || this.lastSelNode;
17114         // undesirable, but required
17115         var sm = this;
17116         if(!s){
17117             return;
17118         }
17119         var k = e.getKey();
17120         switch(k){
17121              case e.DOWN:
17122                  e.stopEvent();
17123                  this.selectNext();
17124              break;
17125              case e.UP:
17126                  e.stopEvent();
17127                  this.selectPrevious();
17128              break;
17129              case e.RIGHT:
17130                  e.preventDefault();
17131                  if(s.hasChildNodes()){
17132                      if(!s.isExpanded()){
17133                          s.expand();
17134                      }else if(s.firstChild){
17135                          this.select(s.firstChild, e);
17136                      }
17137                  }
17138              break;
17139              case e.LEFT:
17140                  e.preventDefault();
17141                  if(s.hasChildNodes() && s.isExpanded()){
17142                      s.collapse();
17143                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17144                      this.select(s.parentNode, e);
17145                  }
17146              break;
17147         };
17148     }
17149 });
17150
17151 /**
17152  * @class Roo.tree.MultiSelectionModel
17153  * @extends Roo.util.Observable
17154  * Multi selection for a TreePanel.
17155  * @param {Object} cfg Configuration
17156  */
17157 Roo.tree.MultiSelectionModel = function(){
17158    this.selNodes = [];
17159    this.selMap = {};
17160    this.addEvents({
17161        /**
17162         * @event selectionchange
17163         * Fires when the selected nodes change
17164         * @param {MultiSelectionModel} this
17165         * @param {Array} nodes Array of the selected nodes
17166         */
17167        "selectionchange" : true
17168    });
17169    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17170    
17171 };
17172
17173 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17174     init : function(tree){
17175         this.tree = tree;
17176         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17177         tree.on("click", this.onNodeClick, this);
17178     },
17179     
17180     onNodeClick : function(node, e){
17181         this.select(node, e, e.ctrlKey);
17182     },
17183     
17184     /**
17185      * Select a node.
17186      * @param {TreeNode} node The node to select
17187      * @param {EventObject} e (optional) An event associated with the selection
17188      * @param {Boolean} keepExisting True to retain existing selections
17189      * @return {TreeNode} The selected node
17190      */
17191     select : function(node, e, keepExisting){
17192         if(keepExisting !== true){
17193             this.clearSelections(true);
17194         }
17195         if(this.isSelected(node)){
17196             this.lastSelNode = node;
17197             return node;
17198         }
17199         this.selNodes.push(node);
17200         this.selMap[node.id] = node;
17201         this.lastSelNode = node;
17202         node.ui.onSelectedChange(true);
17203         this.fireEvent("selectionchange", this, this.selNodes);
17204         return node;
17205     },
17206     
17207     /**
17208      * Deselect a node.
17209      * @param {TreeNode} node The node to unselect
17210      */
17211     unselect : function(node){
17212         if(this.selMap[node.id]){
17213             node.ui.onSelectedChange(false);
17214             var sn = this.selNodes;
17215             var index = -1;
17216             if(sn.indexOf){
17217                 index = sn.indexOf(node);
17218             }else{
17219                 for(var i = 0, len = sn.length; i < len; i++){
17220                     if(sn[i] == node){
17221                         index = i;
17222                         break;
17223                     }
17224                 }
17225             }
17226             if(index != -1){
17227                 this.selNodes.splice(index, 1);
17228             }
17229             delete this.selMap[node.id];
17230             this.fireEvent("selectionchange", this, this.selNodes);
17231         }
17232     },
17233     
17234     /**
17235      * Clear all selections
17236      */
17237     clearSelections : function(suppressEvent){
17238         var sn = this.selNodes;
17239         if(sn.length > 0){
17240             for(var i = 0, len = sn.length; i < len; i++){
17241                 sn[i].ui.onSelectedChange(false);
17242             }
17243             this.selNodes = [];
17244             this.selMap = {};
17245             if(suppressEvent !== true){
17246                 this.fireEvent("selectionchange", this, this.selNodes);
17247             }
17248         }
17249     },
17250     
17251     /**
17252      * Returns true if the node is selected
17253      * @param {TreeNode} node The node to check
17254      * @return {Boolean}
17255      */
17256     isSelected : function(node){
17257         return this.selMap[node.id] ? true : false;  
17258     },
17259     
17260     /**
17261      * Returns an array of the selected nodes
17262      * @return {Array}
17263      */
17264     getSelectedNodes : function(){
17265         return this.selNodes;    
17266     },
17267
17268     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17269
17270     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17271
17272     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17273 });/*
17274  * Based on:
17275  * Ext JS Library 1.1.1
17276  * Copyright(c) 2006-2007, Ext JS, LLC.
17277  *
17278  * Originally Released Under LGPL - original licence link has changed is not relivant.
17279  *
17280  * Fork - LGPL
17281  * <script type="text/javascript">
17282  */
17283  
17284 /**
17285  * @class Roo.tree.TreeNode
17286  * @extends Roo.data.Node
17287  * @cfg {String} text The text for this node
17288  * @cfg {Boolean} expanded true to start the node expanded
17289  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17290  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17291  * @cfg {Boolean} disabled true to start the node disabled
17292  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17293  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17294  * @cfg {String} cls A css class to be added to the node
17295  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17296  * @cfg {String} href URL of the link used for the node (defaults to #)
17297  * @cfg {String} hrefTarget target frame for the link
17298  * @cfg {String} qtip An Ext QuickTip for the node
17299  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17300  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17301  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17302  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17303  * (defaults to undefined with no checkbox rendered)
17304  * @constructor
17305  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17306  */
17307 Roo.tree.TreeNode = function(attributes){
17308     attributes = attributes || {};
17309     if(typeof attributes == "string"){
17310         attributes = {text: attributes};
17311     }
17312     this.childrenRendered = false;
17313     this.rendered = false;
17314     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17315     this.expanded = attributes.expanded === true;
17316     this.isTarget = attributes.isTarget !== false;
17317     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17318     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17319
17320     /**
17321      * Read-only. The text for this node. To change it use setText().
17322      * @type String
17323      */
17324     this.text = attributes.text;
17325     /**
17326      * True if this node is disabled.
17327      * @type Boolean
17328      */
17329     this.disabled = attributes.disabled === true;
17330
17331     this.addEvents({
17332         /**
17333         * @event textchange
17334         * Fires when the text for this node is changed
17335         * @param {Node} this This node
17336         * @param {String} text The new text
17337         * @param {String} oldText The old text
17338         */
17339         "textchange" : true,
17340         /**
17341         * @event beforeexpand
17342         * Fires before this node is expanded, return false to cancel.
17343         * @param {Node} this This node
17344         * @param {Boolean} deep
17345         * @param {Boolean} anim
17346         */
17347         "beforeexpand" : true,
17348         /**
17349         * @event beforecollapse
17350         * Fires before this node is collapsed, return false to cancel.
17351         * @param {Node} this This node
17352         * @param {Boolean} deep
17353         * @param {Boolean} anim
17354         */
17355         "beforecollapse" : true,
17356         /**
17357         * @event expand
17358         * Fires when this node is expanded
17359         * @param {Node} this This node
17360         */
17361         "expand" : true,
17362         /**
17363         * @event disabledchange
17364         * Fires when the disabled status of this node changes
17365         * @param {Node} this This node
17366         * @param {Boolean} disabled
17367         */
17368         "disabledchange" : true,
17369         /**
17370         * @event collapse
17371         * Fires when this node is collapsed
17372         * @param {Node} this This node
17373         */
17374         "collapse" : true,
17375         /**
17376         * @event beforeclick
17377         * Fires before click processing. Return false to cancel the default action.
17378         * @param {Node} this This node
17379         * @param {Roo.EventObject} e The event object
17380         */
17381         "beforeclick":true,
17382         /**
17383         * @event checkchange
17384         * Fires when a node with a checkbox's checked property changes
17385         * @param {Node} this This node
17386         * @param {Boolean} checked
17387         */
17388         "checkchange":true,
17389         /**
17390         * @event click
17391         * Fires when this node is clicked
17392         * @param {Node} this This node
17393         * @param {Roo.EventObject} e The event object
17394         */
17395         "click":true,
17396         /**
17397         * @event dblclick
17398         * Fires when this node is double clicked
17399         * @param {Node} this This node
17400         * @param {Roo.EventObject} e The event object
17401         */
17402         "dblclick":true,
17403         /**
17404         * @event contextmenu
17405         * Fires when this node is right clicked
17406         * @param {Node} this This node
17407         * @param {Roo.EventObject} e The event object
17408         */
17409         "contextmenu":true,
17410         /**
17411         * @event beforechildrenrendered
17412         * Fires right before the child nodes for this node are rendered
17413         * @param {Node} this This node
17414         */
17415         "beforechildrenrendered":true
17416     });
17417
17418     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17419
17420     /**
17421      * Read-only. The UI for this node
17422      * @type TreeNodeUI
17423      */
17424     this.ui = new uiClass(this);
17425     
17426     // finally support items[]
17427     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17428         return;
17429     }
17430     
17431     
17432     Roo.each(this.attributes.items, function(c) {
17433         this.appendChild(Roo.factory(c,Roo.Tree));
17434     }, this);
17435     delete this.attributes.items;
17436     
17437     
17438     
17439 };
17440 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17441     preventHScroll: true,
17442     /**
17443      * Returns true if this node is expanded
17444      * @return {Boolean}
17445      */
17446     isExpanded : function(){
17447         return this.expanded;
17448     },
17449
17450     /**
17451      * Returns the UI object for this node
17452      * @return {TreeNodeUI}
17453      */
17454     getUI : function(){
17455         return this.ui;
17456     },
17457
17458     // private override
17459     setFirstChild : function(node){
17460         var of = this.firstChild;
17461         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17462         if(this.childrenRendered && of && node != of){
17463             of.renderIndent(true, true);
17464         }
17465         if(this.rendered){
17466             this.renderIndent(true, true);
17467         }
17468     },
17469
17470     // private override
17471     setLastChild : function(node){
17472         var ol = this.lastChild;
17473         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17474         if(this.childrenRendered && ol && node != ol){
17475             ol.renderIndent(true, true);
17476         }
17477         if(this.rendered){
17478             this.renderIndent(true, true);
17479         }
17480     },
17481
17482     // these methods are overridden to provide lazy rendering support
17483     // private override
17484     appendChild : function()
17485     {
17486         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17487         if(node && this.childrenRendered){
17488             node.render();
17489         }
17490         this.ui.updateExpandIcon();
17491         return node;
17492     },
17493
17494     // private override
17495     removeChild : function(node){
17496         this.ownerTree.getSelectionModel().unselect(node);
17497         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17498         // if it's been rendered remove dom node
17499         if(this.childrenRendered){
17500             node.ui.remove();
17501         }
17502         if(this.childNodes.length < 1){
17503             this.collapse(false, false);
17504         }else{
17505             this.ui.updateExpandIcon();
17506         }
17507         if(!this.firstChild) {
17508             this.childrenRendered = false;
17509         }
17510         return node;
17511     },
17512
17513     // private override
17514     insertBefore : function(node, refNode){
17515         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17516         if(newNode && refNode && this.childrenRendered){
17517             node.render();
17518         }
17519         this.ui.updateExpandIcon();
17520         return newNode;
17521     },
17522
17523     /**
17524      * Sets the text for this node
17525      * @param {String} text
17526      */
17527     setText : function(text){
17528         var oldText = this.text;
17529         this.text = text;
17530         this.attributes.text = text;
17531         if(this.rendered){ // event without subscribing
17532             this.ui.onTextChange(this, text, oldText);
17533         }
17534         this.fireEvent("textchange", this, text, oldText);
17535     },
17536
17537     /**
17538      * Triggers selection of this node
17539      */
17540     select : function(){
17541         this.getOwnerTree().getSelectionModel().select(this);
17542     },
17543
17544     /**
17545      * Triggers deselection of this node
17546      */
17547     unselect : function(){
17548         this.getOwnerTree().getSelectionModel().unselect(this);
17549     },
17550
17551     /**
17552      * Returns true if this node is selected
17553      * @return {Boolean}
17554      */
17555     isSelected : function(){
17556         return this.getOwnerTree().getSelectionModel().isSelected(this);
17557     },
17558
17559     /**
17560      * Expand this node.
17561      * @param {Boolean} deep (optional) True to expand all children as well
17562      * @param {Boolean} anim (optional) false to cancel the default animation
17563      * @param {Function} callback (optional) A callback to be called when
17564      * expanding this node completes (does not wait for deep expand to complete).
17565      * Called with 1 parameter, this node.
17566      */
17567     expand : function(deep, anim, callback){
17568         if(!this.expanded){
17569             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17570                 return;
17571             }
17572             if(!this.childrenRendered){
17573                 this.renderChildren();
17574             }
17575             this.expanded = true;
17576             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17577                 this.ui.animExpand(function(){
17578                     this.fireEvent("expand", this);
17579                     if(typeof callback == "function"){
17580                         callback(this);
17581                     }
17582                     if(deep === true){
17583                         this.expandChildNodes(true);
17584                     }
17585                 }.createDelegate(this));
17586                 return;
17587             }else{
17588                 this.ui.expand();
17589                 this.fireEvent("expand", this);
17590                 if(typeof callback == "function"){
17591                     callback(this);
17592                 }
17593             }
17594         }else{
17595            if(typeof callback == "function"){
17596                callback(this);
17597            }
17598         }
17599         if(deep === true){
17600             this.expandChildNodes(true);
17601         }
17602     },
17603
17604     isHiddenRoot : function(){
17605         return this.isRoot && !this.getOwnerTree().rootVisible;
17606     },
17607
17608     /**
17609      * Collapse this node.
17610      * @param {Boolean} deep (optional) True to collapse all children as well
17611      * @param {Boolean} anim (optional) false to cancel the default animation
17612      */
17613     collapse : function(deep, anim){
17614         if(this.expanded && !this.isHiddenRoot()){
17615             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17616                 return;
17617             }
17618             this.expanded = false;
17619             if((this.getOwnerTree().animate && anim !== false) || anim){
17620                 this.ui.animCollapse(function(){
17621                     this.fireEvent("collapse", this);
17622                     if(deep === true){
17623                         this.collapseChildNodes(true);
17624                     }
17625                 }.createDelegate(this));
17626                 return;
17627             }else{
17628                 this.ui.collapse();
17629                 this.fireEvent("collapse", this);
17630             }
17631         }
17632         if(deep === true){
17633             var cs = this.childNodes;
17634             for(var i = 0, len = cs.length; i < len; i++) {
17635                 cs[i].collapse(true, false);
17636             }
17637         }
17638     },
17639
17640     // private
17641     delayedExpand : function(delay){
17642         if(!this.expandProcId){
17643             this.expandProcId = this.expand.defer(delay, this);
17644         }
17645     },
17646
17647     // private
17648     cancelExpand : function(){
17649         if(this.expandProcId){
17650             clearTimeout(this.expandProcId);
17651         }
17652         this.expandProcId = false;
17653     },
17654
17655     /**
17656      * Toggles expanded/collapsed state of the node
17657      */
17658     toggle : function(){
17659         if(this.expanded){
17660             this.collapse();
17661         }else{
17662             this.expand();
17663         }
17664     },
17665
17666     /**
17667      * Ensures all parent nodes are expanded
17668      */
17669     ensureVisible : function(callback){
17670         var tree = this.getOwnerTree();
17671         tree.expandPath(this.parentNode.getPath(), false, function(){
17672             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17673             Roo.callback(callback);
17674         }.createDelegate(this));
17675     },
17676
17677     /**
17678      * Expand all child nodes
17679      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17680      */
17681     expandChildNodes : function(deep){
17682         var cs = this.childNodes;
17683         for(var i = 0, len = cs.length; i < len; i++) {
17684                 cs[i].expand(deep);
17685         }
17686     },
17687
17688     /**
17689      * Collapse all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17691      */
17692     collapseChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].collapse(deep);
17696         }
17697     },
17698
17699     /**
17700      * Disables this node
17701      */
17702     disable : function(){
17703         this.disabled = true;
17704         this.unselect();
17705         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17706             this.ui.onDisableChange(this, true);
17707         }
17708         this.fireEvent("disabledchange", this, true);
17709     },
17710
17711     /**
17712      * Enables this node
17713      */
17714     enable : function(){
17715         this.disabled = false;
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, false);
17718         }
17719         this.fireEvent("disabledchange", this, false);
17720     },
17721
17722     // private
17723     renderChildren : function(suppressEvent){
17724         if(suppressEvent !== false){
17725             this.fireEvent("beforechildrenrendered", this);
17726         }
17727         var cs = this.childNodes;
17728         for(var i = 0, len = cs.length; i < len; i++){
17729             cs[i].render(true);
17730         }
17731         this.childrenRendered = true;
17732     },
17733
17734     // private
17735     sort : function(fn, scope){
17736         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17737         if(this.childrenRendered){
17738             var cs = this.childNodes;
17739             for(var i = 0, len = cs.length; i < len; i++){
17740                 cs[i].render(true);
17741             }
17742         }
17743     },
17744
17745     // private
17746     render : function(bulkRender){
17747         this.ui.render(bulkRender);
17748         if(!this.rendered){
17749             this.rendered = true;
17750             if(this.expanded){
17751                 this.expanded = false;
17752                 this.expand(false, false);
17753             }
17754         }
17755     },
17756
17757     // private
17758     renderIndent : function(deep, refresh){
17759         if(refresh){
17760             this.ui.childIndent = null;
17761         }
17762         this.ui.renderIndent();
17763         if(deep === true && this.childrenRendered){
17764             var cs = this.childNodes;
17765             for(var i = 0, len = cs.length; i < len; i++){
17766                 cs[i].renderIndent(true, refresh);
17767             }
17768         }
17769     }
17770 });/*
17771  * Based on:
17772  * Ext JS Library 1.1.1
17773  * Copyright(c) 2006-2007, Ext JS, LLC.
17774  *
17775  * Originally Released Under LGPL - original licence link has changed is not relivant.
17776  *
17777  * Fork - LGPL
17778  * <script type="text/javascript">
17779  */
17780  
17781 /**
17782  * @class Roo.tree.AsyncTreeNode
17783  * @extends Roo.tree.TreeNode
17784  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17785  * @constructor
17786  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17787  */
17788  Roo.tree.AsyncTreeNode = function(config){
17789     this.loaded = false;
17790     this.loading = false;
17791     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17792     /**
17793     * @event beforeload
17794     * Fires before this node is loaded, return false to cancel
17795     * @param {Node} this This node
17796     */
17797     this.addEvents({'beforeload':true, 'load': true});
17798     /**
17799     * @event load
17800     * Fires when this node is loaded
17801     * @param {Node} this This node
17802     */
17803     /**
17804      * The loader used by this node (defaults to using the tree's defined loader)
17805      * @type TreeLoader
17806      * @property loader
17807      */
17808 };
17809 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17810     expand : function(deep, anim, callback){
17811         if(this.loading){ // if an async load is already running, waiting til it's done
17812             var timer;
17813             var f = function(){
17814                 if(!this.loading){ // done loading
17815                     clearInterval(timer);
17816                     this.expand(deep, anim, callback);
17817                 }
17818             }.createDelegate(this);
17819             timer = setInterval(f, 200);
17820             return;
17821         }
17822         if(!this.loaded){
17823             if(this.fireEvent("beforeload", this) === false){
17824                 return;
17825             }
17826             this.loading = true;
17827             this.ui.beforeLoad(this);
17828             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17829             if(loader){
17830                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17831                 return;
17832             }
17833         }
17834         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17835     },
17836     
17837     /**
17838      * Returns true if this node is currently loading
17839      * @return {Boolean}
17840      */
17841     isLoading : function(){
17842         return this.loading;  
17843     },
17844     
17845     loadComplete : function(deep, anim, callback){
17846         this.loading = false;
17847         this.loaded = true;
17848         this.ui.afterLoad(this);
17849         this.fireEvent("load", this);
17850         this.expand(deep, anim, callback);
17851     },
17852     
17853     /**
17854      * Returns true if this node has been loaded
17855      * @return {Boolean}
17856      */
17857     isLoaded : function(){
17858         return this.loaded;
17859     },
17860     
17861     hasChildNodes : function(){
17862         if(!this.isLeaf() && !this.loaded){
17863             return true;
17864         }else{
17865             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17866         }
17867     },
17868
17869     /**
17870      * Trigger a reload for this node
17871      * @param {Function} callback
17872      */
17873     reload : function(callback){
17874         this.collapse(false, false);
17875         while(this.firstChild){
17876             this.removeChild(this.firstChild);
17877         }
17878         this.childrenRendered = false;
17879         this.loaded = false;
17880         if(this.isHiddenRoot()){
17881             this.expanded = false;
17882         }
17883         this.expand(false, false, callback);
17884     }
17885 });/*
17886  * Based on:
17887  * Ext JS Library 1.1.1
17888  * Copyright(c) 2006-2007, Ext JS, LLC.
17889  *
17890  * Originally Released Under LGPL - original licence link has changed is not relivant.
17891  *
17892  * Fork - LGPL
17893  * <script type="text/javascript">
17894  */
17895  
17896 /**
17897  * @class Roo.tree.TreeNodeUI
17898  * @constructor
17899  * @param {Object} node The node to render
17900  * The TreeNode UI implementation is separate from the
17901  * tree implementation. Unless you are customizing the tree UI,
17902  * you should never have to use this directly.
17903  */
17904 Roo.tree.TreeNodeUI = function(node){
17905     this.node = node;
17906     this.rendered = false;
17907     this.animating = false;
17908     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17909 };
17910
17911 Roo.tree.TreeNodeUI.prototype = {
17912     removeChild : function(node){
17913         if(this.rendered){
17914             this.ctNode.removeChild(node.ui.getEl());
17915         }
17916     },
17917
17918     beforeLoad : function(){
17919          this.addClass("x-tree-node-loading");
17920     },
17921
17922     afterLoad : function(){
17923          this.removeClass("x-tree-node-loading");
17924     },
17925
17926     onTextChange : function(node, text, oldText){
17927         if(this.rendered){
17928             this.textNode.innerHTML = text;
17929         }
17930     },
17931
17932     onDisableChange : function(node, state){
17933         this.disabled = state;
17934         if(state){
17935             this.addClass("x-tree-node-disabled");
17936         }else{
17937             this.removeClass("x-tree-node-disabled");
17938         }
17939     },
17940
17941     onSelectedChange : function(state){
17942         if(state){
17943             this.focus();
17944             this.addClass("x-tree-selected");
17945         }else{
17946             //this.blur();
17947             this.removeClass("x-tree-selected");
17948         }
17949     },
17950
17951     onMove : function(tree, node, oldParent, newParent, index, refNode){
17952         this.childIndent = null;
17953         if(this.rendered){
17954             var targetNode = newParent.ui.getContainer();
17955             if(!targetNode){//target not rendered
17956                 this.holder = document.createElement("div");
17957                 this.holder.appendChild(this.wrap);
17958                 return;
17959             }
17960             var insertBefore = refNode ? refNode.ui.getEl() : null;
17961             if(insertBefore){
17962                 targetNode.insertBefore(this.wrap, insertBefore);
17963             }else{
17964                 targetNode.appendChild(this.wrap);
17965             }
17966             this.node.renderIndent(true);
17967         }
17968     },
17969
17970     addClass : function(cls){
17971         if(this.elNode){
17972             Roo.fly(this.elNode).addClass(cls);
17973         }
17974     },
17975
17976     removeClass : function(cls){
17977         if(this.elNode){
17978             Roo.fly(this.elNode).removeClass(cls);
17979         }
17980     },
17981
17982     remove : function(){
17983         if(this.rendered){
17984             this.holder = document.createElement("div");
17985             this.holder.appendChild(this.wrap);
17986         }
17987     },
17988
17989     fireEvent : function(){
17990         return this.node.fireEvent.apply(this.node, arguments);
17991     },
17992
17993     initEvents : function(){
17994         this.node.on("move", this.onMove, this);
17995         var E = Roo.EventManager;
17996         var a = this.anchor;
17997
17998         var el = Roo.fly(a, '_treeui');
17999
18000         if(Roo.isOpera){ // opera render bug ignores the CSS
18001             el.setStyle("text-decoration", "none");
18002         }
18003
18004         el.on("click", this.onClick, this);
18005         el.on("dblclick", this.onDblClick, this);
18006
18007         if(this.checkbox){
18008             Roo.EventManager.on(this.checkbox,
18009                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18010         }
18011
18012         el.on("contextmenu", this.onContextMenu, this);
18013
18014         var icon = Roo.fly(this.iconNode);
18015         icon.on("click", this.onClick, this);
18016         icon.on("dblclick", this.onDblClick, this);
18017         icon.on("contextmenu", this.onContextMenu, this);
18018         E.on(this.ecNode, "click", this.ecClick, this, true);
18019
18020         if(this.node.disabled){
18021             this.addClass("x-tree-node-disabled");
18022         }
18023         if(this.node.hidden){
18024             this.addClass("x-tree-node-disabled");
18025         }
18026         var ot = this.node.getOwnerTree();
18027         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18028         if(dd && (!this.node.isRoot || ot.rootVisible)){
18029             Roo.dd.Registry.register(this.elNode, {
18030                 node: this.node,
18031                 handles: this.getDDHandles(),
18032                 isHandle: false
18033             });
18034         }
18035     },
18036
18037     getDDHandles : function(){
18038         return [this.iconNode, this.textNode];
18039     },
18040
18041     hide : function(){
18042         if(this.rendered){
18043             this.wrap.style.display = "none";
18044         }
18045     },
18046
18047     show : function(){
18048         if(this.rendered){
18049             this.wrap.style.display = "";
18050         }
18051     },
18052
18053     onContextMenu : function(e){
18054         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18055             e.preventDefault();
18056             this.focus();
18057             this.fireEvent("contextmenu", this.node, e);
18058         }
18059     },
18060
18061     onClick : function(e){
18062         if(this.dropping){
18063             e.stopEvent();
18064             return;
18065         }
18066         if(this.fireEvent("beforeclick", this.node, e) !== false){
18067             if(!this.disabled && this.node.attributes.href){
18068                 this.fireEvent("click", this.node, e);
18069                 return;
18070             }
18071             e.preventDefault();
18072             if(this.disabled){
18073                 return;
18074             }
18075
18076             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18077                 this.node.toggle();
18078             }
18079
18080             this.fireEvent("click", this.node, e);
18081         }else{
18082             e.stopEvent();
18083         }
18084     },
18085
18086     onDblClick : function(e){
18087         e.preventDefault();
18088         if(this.disabled){
18089             return;
18090         }
18091         if(this.checkbox){
18092             this.toggleCheck();
18093         }
18094         if(!this.animating && this.node.hasChildNodes()){
18095             this.node.toggle();
18096         }
18097         this.fireEvent("dblclick", this.node, e);
18098     },
18099
18100     onCheckChange : function(){
18101         var checked = this.checkbox.checked;
18102         this.node.attributes.checked = checked;
18103         this.fireEvent('checkchange', this.node, checked);
18104     },
18105
18106     ecClick : function(e){
18107         if(!this.animating && this.node.hasChildNodes()){
18108             this.node.toggle();
18109         }
18110     },
18111
18112     startDrop : function(){
18113         this.dropping = true;
18114     },
18115
18116     // delayed drop so the click event doesn't get fired on a drop
18117     endDrop : function(){
18118        setTimeout(function(){
18119            this.dropping = false;
18120        }.createDelegate(this), 50);
18121     },
18122
18123     expand : function(){
18124         this.updateExpandIcon();
18125         this.ctNode.style.display = "";
18126     },
18127
18128     focus : function(){
18129         if(!this.node.preventHScroll){
18130             try{this.anchor.focus();
18131             }catch(e){}
18132         }else if(!Roo.isIE){
18133             try{
18134                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18135                 var l = noscroll.scrollLeft;
18136                 this.anchor.focus();
18137                 noscroll.scrollLeft = l;
18138             }catch(e){}
18139         }
18140     },
18141
18142     toggleCheck : function(value){
18143         var cb = this.checkbox;
18144         if(cb){
18145             cb.checked = (value === undefined ? !cb.checked : value);
18146         }
18147     },
18148
18149     blur : function(){
18150         try{
18151             this.anchor.blur();
18152         }catch(e){}
18153     },
18154
18155     animExpand : function(callback){
18156         var ct = Roo.get(this.ctNode);
18157         ct.stopFx();
18158         if(!this.node.hasChildNodes()){
18159             this.updateExpandIcon();
18160             this.ctNode.style.display = "";
18161             Roo.callback(callback);
18162             return;
18163         }
18164         this.animating = true;
18165         this.updateExpandIcon();
18166
18167         ct.slideIn('t', {
18168            callback : function(){
18169                this.animating = false;
18170                Roo.callback(callback);
18171             },
18172             scope: this,
18173             duration: this.node.ownerTree.duration || .25
18174         });
18175     },
18176
18177     highlight : function(){
18178         var tree = this.node.getOwnerTree();
18179         Roo.fly(this.wrap).highlight(
18180             tree.hlColor || "C3DAF9",
18181             {endColor: tree.hlBaseColor}
18182         );
18183     },
18184
18185     collapse : function(){
18186         this.updateExpandIcon();
18187         this.ctNode.style.display = "none";
18188     },
18189
18190     animCollapse : function(callback){
18191         var ct = Roo.get(this.ctNode);
18192         ct.enableDisplayMode('block');
18193         ct.stopFx();
18194
18195         this.animating = true;
18196         this.updateExpandIcon();
18197
18198         ct.slideOut('t', {
18199             callback : function(){
18200                this.animating = false;
18201                Roo.callback(callback);
18202             },
18203             scope: this,
18204             duration: this.node.ownerTree.duration || .25
18205         });
18206     },
18207
18208     getContainer : function(){
18209         return this.ctNode;
18210     },
18211
18212     getEl : function(){
18213         return this.wrap;
18214     },
18215
18216     appendDDGhost : function(ghostNode){
18217         ghostNode.appendChild(this.elNode.cloneNode(true));
18218     },
18219
18220     getDDRepairXY : function(){
18221         return Roo.lib.Dom.getXY(this.iconNode);
18222     },
18223
18224     onRender : function(){
18225         this.render();
18226     },
18227
18228     render : function(bulkRender){
18229         var n = this.node, a = n.attributes;
18230         var targetNode = n.parentNode ?
18231               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18232
18233         if(!this.rendered){
18234             this.rendered = true;
18235
18236             this.renderElements(n, a, targetNode, bulkRender);
18237
18238             if(a.qtip){
18239                if(this.textNode.setAttributeNS){
18240                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18241                    if(a.qtipTitle){
18242                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18243                    }
18244                }else{
18245                    this.textNode.setAttribute("ext:qtip", a.qtip);
18246                    if(a.qtipTitle){
18247                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18248                    }
18249                }
18250             }else if(a.qtipCfg){
18251                 a.qtipCfg.target = Roo.id(this.textNode);
18252                 Roo.QuickTips.register(a.qtipCfg);
18253             }
18254             this.initEvents();
18255             if(!this.node.expanded){
18256                 this.updateExpandIcon();
18257             }
18258         }else{
18259             if(bulkRender === true) {
18260                 targetNode.appendChild(this.wrap);
18261             }
18262         }
18263     },
18264
18265     renderElements : function(n, a, targetNode, bulkRender)
18266     {
18267         // add some indent caching, this helps performance when rendering a large tree
18268         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18269         var t = n.getOwnerTree();
18270         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18271         if (typeof(n.attributes.html) != 'undefined') {
18272             txt = n.attributes.html;
18273         }
18274         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18275         var cb = typeof a.checked == 'boolean';
18276         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18277         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18278             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18279             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18280             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18281             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18282             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18283              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18284                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18285             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18286             "</li>"];
18287
18288         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18289             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18290                                 n.nextSibling.ui.getEl(), buf.join(""));
18291         }else{
18292             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18293         }
18294
18295         this.elNode = this.wrap.childNodes[0];
18296         this.ctNode = this.wrap.childNodes[1];
18297         var cs = this.elNode.childNodes;
18298         this.indentNode = cs[0];
18299         this.ecNode = cs[1];
18300         this.iconNode = cs[2];
18301         var index = 3;
18302         if(cb){
18303             this.checkbox = cs[3];
18304             index++;
18305         }
18306         this.anchor = cs[index];
18307         this.textNode = cs[index].firstChild;
18308     },
18309
18310     getAnchor : function(){
18311         return this.anchor;
18312     },
18313
18314     getTextEl : function(){
18315         return this.textNode;
18316     },
18317
18318     getIconEl : function(){
18319         return this.iconNode;
18320     },
18321
18322     isChecked : function(){
18323         return this.checkbox ? this.checkbox.checked : false;
18324     },
18325
18326     updateExpandIcon : function(){
18327         if(this.rendered){
18328             var n = this.node, c1, c2;
18329             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18330             var hasChild = n.hasChildNodes();
18331             if(hasChild){
18332                 if(n.expanded){
18333                     cls += "-minus";
18334                     c1 = "x-tree-node-collapsed";
18335                     c2 = "x-tree-node-expanded";
18336                 }else{
18337                     cls += "-plus";
18338                     c1 = "x-tree-node-expanded";
18339                     c2 = "x-tree-node-collapsed";
18340                 }
18341                 if(this.wasLeaf){
18342                     this.removeClass("x-tree-node-leaf");
18343                     this.wasLeaf = false;
18344                 }
18345                 if(this.c1 != c1 || this.c2 != c2){
18346                     Roo.fly(this.elNode).replaceClass(c1, c2);
18347                     this.c1 = c1; this.c2 = c2;
18348                 }
18349             }else{
18350                 // this changes non-leafs into leafs if they have no children.
18351                 // it's not very rational behaviour..
18352                 
18353                 if(!this.wasLeaf && this.node.leaf){
18354                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18355                     delete this.c1;
18356                     delete this.c2;
18357                     this.wasLeaf = true;
18358                 }
18359             }
18360             var ecc = "x-tree-ec-icon "+cls;
18361             if(this.ecc != ecc){
18362                 this.ecNode.className = ecc;
18363                 this.ecc = ecc;
18364             }
18365         }
18366     },
18367
18368     getChildIndent : function(){
18369         if(!this.childIndent){
18370             var buf = [];
18371             var p = this.node;
18372             while(p){
18373                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18374                     if(!p.isLast()) {
18375                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18376                     } else {
18377                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18378                     }
18379                 }
18380                 p = p.parentNode;
18381             }
18382             this.childIndent = buf.join("");
18383         }
18384         return this.childIndent;
18385     },
18386
18387     renderIndent : function(){
18388         if(this.rendered){
18389             var indent = "";
18390             var p = this.node.parentNode;
18391             if(p){
18392                 indent = p.ui.getChildIndent();
18393             }
18394             if(this.indentMarkup != indent){ // don't rerender if not required
18395                 this.indentNode.innerHTML = indent;
18396                 this.indentMarkup = indent;
18397             }
18398             this.updateExpandIcon();
18399         }
18400     }
18401 };
18402
18403 Roo.tree.RootTreeNodeUI = function(){
18404     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18405 };
18406 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18407     render : function(){
18408         if(!this.rendered){
18409             var targetNode = this.node.ownerTree.innerCt.dom;
18410             this.node.expanded = true;
18411             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18412             this.wrap = this.ctNode = targetNode.firstChild;
18413         }
18414     },
18415     collapse : function(){
18416     },
18417     expand : function(){
18418     }
18419 });/*
18420  * Based on:
18421  * Ext JS Library 1.1.1
18422  * Copyright(c) 2006-2007, Ext JS, LLC.
18423  *
18424  * Originally Released Under LGPL - original licence link has changed is not relivant.
18425  *
18426  * Fork - LGPL
18427  * <script type="text/javascript">
18428  */
18429 /**
18430  * @class Roo.tree.TreeLoader
18431  * @extends Roo.util.Observable
18432  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18433  * nodes from a specified URL. The response must be a javascript Array definition
18434  * who's elements are node definition objects. eg:
18435  * <pre><code>
18436 {  success : true,
18437    data :      [
18438    
18439     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18440     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18441     ]
18442 }
18443
18444
18445 </code></pre>
18446  * <br><br>
18447  * The old style respose with just an array is still supported, but not recommended.
18448  * <br><br>
18449  *
18450  * A server request is sent, and child nodes are loaded only when a node is expanded.
18451  * The loading node's id is passed to the server under the parameter name "node" to
18452  * enable the server to produce the correct child nodes.
18453  * <br><br>
18454  * To pass extra parameters, an event handler may be attached to the "beforeload"
18455  * event, and the parameters specified in the TreeLoader's baseParams property:
18456  * <pre><code>
18457     myTreeLoader.on("beforeload", function(treeLoader, node) {
18458         this.baseParams.category = node.attributes.category;
18459     }, this);
18460 </code></pre><
18461  * This would pass an HTTP parameter called "category" to the server containing
18462  * the value of the Node's "category" attribute.
18463  * @constructor
18464  * Creates a new Treeloader.
18465  * @param {Object} config A config object containing config properties.
18466  */
18467 Roo.tree.TreeLoader = function(config){
18468     this.baseParams = {};
18469     this.requestMethod = "POST";
18470     Roo.apply(this, config);
18471
18472     this.addEvents({
18473     
18474         /**
18475          * @event beforeload
18476          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18477          * @param {Object} This TreeLoader object.
18478          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18479          * @param {Object} callback The callback function specified in the {@link #load} call.
18480          */
18481         beforeload : true,
18482         /**
18483          * @event load
18484          * Fires when the node has been successfuly loaded.
18485          * @param {Object} This TreeLoader object.
18486          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18487          * @param {Object} response The response object containing the data from the server.
18488          */
18489         load : true,
18490         /**
18491          * @event loadexception
18492          * Fires if the network request failed.
18493          * @param {Object} This TreeLoader object.
18494          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18495          * @param {Object} response The response object containing the data from the server.
18496          */
18497         loadexception : true,
18498         /**
18499          * @event create
18500          * Fires before a node is created, enabling you to return custom Node types 
18501          * @param {Object} This TreeLoader object.
18502          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18503          */
18504         create : true
18505     });
18506
18507     Roo.tree.TreeLoader.superclass.constructor.call(this);
18508 };
18509
18510 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18511     /**
18512     * @cfg {String} dataUrl The URL from which to request a Json string which
18513     * specifies an array of node definition object representing the child nodes
18514     * to be loaded.
18515     */
18516     /**
18517     * @cfg {String} requestMethod either GET or POST
18518     * defaults to POST (due to BC)
18519     * to be loaded.
18520     */
18521     /**
18522     * @cfg {Object} baseParams (optional) An object containing properties which
18523     * specify HTTP parameters to be passed to each request for child nodes.
18524     */
18525     /**
18526     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18527     * created by this loader. If the attributes sent by the server have an attribute in this object,
18528     * they take priority.
18529     */
18530     /**
18531     * @cfg {Object} uiProviders (optional) An object containing properties which
18532     * 
18533     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18534     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18535     * <i>uiProvider</i> attribute of a returned child node is a string rather
18536     * than a reference to a TreeNodeUI implementation, this that string value
18537     * is used as a property name in the uiProviders object. You can define the provider named
18538     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18539     */
18540     uiProviders : {},
18541
18542     /**
18543     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18544     * child nodes before loading.
18545     */
18546     clearOnLoad : true,
18547
18548     /**
18549     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18550     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18551     * Grid query { data : [ .....] }
18552     */
18553     
18554     root : false,
18555      /**
18556     * @cfg {String} queryParam (optional) 
18557     * Name of the query as it will be passed on the querystring (defaults to 'node')
18558     * eg. the request will be ?node=[id]
18559     */
18560     
18561     
18562     queryParam: false,
18563     
18564     /**
18565      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18566      * This is called automatically when a node is expanded, but may be used to reload
18567      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18568      * @param {Roo.tree.TreeNode} node
18569      * @param {Function} callback
18570      */
18571     load : function(node, callback){
18572         if(this.clearOnLoad){
18573             while(node.firstChild){
18574                 node.removeChild(node.firstChild);
18575             }
18576         }
18577         if(node.attributes.children){ // preloaded json children
18578             var cs = node.attributes.children;
18579             for(var i = 0, len = cs.length; i < len; i++){
18580                 node.appendChild(this.createNode(cs[i]));
18581             }
18582             if(typeof callback == "function"){
18583                 callback();
18584             }
18585         }else if(this.dataUrl){
18586             this.requestData(node, callback);
18587         }
18588     },
18589
18590     getParams: function(node){
18591         var buf = [], bp = this.baseParams;
18592         for(var key in bp){
18593             if(typeof bp[key] != "function"){
18594                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18595             }
18596         }
18597         var n = this.queryParam === false ? 'node' : this.queryParam;
18598         buf.push(n + "=", encodeURIComponent(node.id));
18599         return buf.join("");
18600     },
18601
18602     requestData : function(node, callback){
18603         if(this.fireEvent("beforeload", this, node, callback) !== false){
18604             this.transId = Roo.Ajax.request({
18605                 method:this.requestMethod,
18606                 url: this.dataUrl||this.url,
18607                 success: this.handleResponse,
18608                 failure: this.handleFailure,
18609                 scope: this,
18610                 argument: {callback: callback, node: node},
18611                 params: this.getParams(node)
18612             });
18613         }else{
18614             // if the load is cancelled, make sure we notify
18615             // the node that we are done
18616             if(typeof callback == "function"){
18617                 callback();
18618             }
18619         }
18620     },
18621
18622     isLoading : function(){
18623         return this.transId ? true : false;
18624     },
18625
18626     abort : function(){
18627         if(this.isLoading()){
18628             Roo.Ajax.abort(this.transId);
18629         }
18630     },
18631
18632     // private
18633     createNode : function(attr)
18634     {
18635         // apply baseAttrs, nice idea Corey!
18636         if(this.baseAttrs){
18637             Roo.applyIf(attr, this.baseAttrs);
18638         }
18639         if(this.applyLoader !== false){
18640             attr.loader = this;
18641         }
18642         // uiProvider = depreciated..
18643         
18644         if(typeof(attr.uiProvider) == 'string'){
18645            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18646                 /**  eval:var:attr */ eval(attr.uiProvider);
18647         }
18648         if(typeof(this.uiProviders['default']) != 'undefined') {
18649             attr.uiProvider = this.uiProviders['default'];
18650         }
18651         
18652         this.fireEvent('create', this, attr);
18653         
18654         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18655         return(attr.leaf ?
18656                         new Roo.tree.TreeNode(attr) :
18657                         new Roo.tree.AsyncTreeNode(attr));
18658     },
18659
18660     processResponse : function(response, node, callback)
18661     {
18662         var json = response.responseText;
18663         try {
18664             
18665             var o = Roo.decode(json);
18666             
18667             if (this.root === false && typeof(o.success) != undefined) {
18668                 this.root = 'data'; // the default behaviour for list like data..
18669                 }
18670                 
18671             if (this.root !== false &&  !o.success) {
18672                 // it's a failure condition.
18673                 var a = response.argument;
18674                 this.fireEvent("loadexception", this, a.node, response);
18675                 Roo.log("Load failed - should have a handler really");
18676                 return;
18677             }
18678             
18679             
18680             
18681             if (this.root !== false) {
18682                  o = o[this.root];
18683             }
18684             
18685             for(var i = 0, len = o.length; i < len; i++){
18686                 var n = this.createNode(o[i]);
18687                 if(n){
18688                     node.appendChild(n);
18689                 }
18690             }
18691             if(typeof callback == "function"){
18692                 callback(this, node);
18693             }
18694         }catch(e){
18695             this.handleFailure(response);
18696         }
18697     },
18698
18699     handleResponse : function(response){
18700         this.transId = false;
18701         var a = response.argument;
18702         this.processResponse(response, a.node, a.callback);
18703         this.fireEvent("load", this, a.node, response);
18704     },
18705
18706     handleFailure : function(response)
18707     {
18708         // should handle failure better..
18709         this.transId = false;
18710         var a = response.argument;
18711         this.fireEvent("loadexception", this, a.node, response);
18712         if(typeof a.callback == "function"){
18713             a.callback(this, a.node);
18714         }
18715     }
18716 });/*
18717  * Based on:
18718  * Ext JS Library 1.1.1
18719  * Copyright(c) 2006-2007, Ext JS, LLC.
18720  *
18721  * Originally Released Under LGPL - original licence link has changed is not relivant.
18722  *
18723  * Fork - LGPL
18724  * <script type="text/javascript">
18725  */
18726
18727 /**
18728 * @class Roo.tree.TreeFilter
18729 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18730 * @param {TreePanel} tree
18731 * @param {Object} config (optional)
18732  */
18733 Roo.tree.TreeFilter = function(tree, config){
18734     this.tree = tree;
18735     this.filtered = {};
18736     Roo.apply(this, config);
18737 };
18738
18739 Roo.tree.TreeFilter.prototype = {
18740     clearBlank:false,
18741     reverse:false,
18742     autoClear:false,
18743     remove:false,
18744
18745      /**
18746      * Filter the data by a specific attribute.
18747      * @param {String/RegExp} value Either string that the attribute value
18748      * should start with or a RegExp to test against the attribute
18749      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18750      * @param {TreeNode} startNode (optional) The node to start the filter at.
18751      */
18752     filter : function(value, attr, startNode){
18753         attr = attr || "text";
18754         var f;
18755         if(typeof value == "string"){
18756             var vlen = value.length;
18757             // auto clear empty filter
18758             if(vlen == 0 && this.clearBlank){
18759                 this.clear();
18760                 return;
18761             }
18762             value = value.toLowerCase();
18763             f = function(n){
18764                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18765             };
18766         }else if(value.exec){ // regex?
18767             f = function(n){
18768                 return value.test(n.attributes[attr]);
18769             };
18770         }else{
18771             throw 'Illegal filter type, must be string or regex';
18772         }
18773         this.filterBy(f, null, startNode);
18774         },
18775
18776     /**
18777      * Filter by a function. The passed function will be called with each
18778      * node in the tree (or from the startNode). If the function returns true, the node is kept
18779      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18780      * @param {Function} fn The filter function
18781      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18782      */
18783     filterBy : function(fn, scope, startNode){
18784         startNode = startNode || this.tree.root;
18785         if(this.autoClear){
18786             this.clear();
18787         }
18788         var af = this.filtered, rv = this.reverse;
18789         var f = function(n){
18790             if(n == startNode){
18791                 return true;
18792             }
18793             if(af[n.id]){
18794                 return false;
18795             }
18796             var m = fn.call(scope || n, n);
18797             if(!m || rv){
18798                 af[n.id] = n;
18799                 n.ui.hide();
18800                 return false;
18801             }
18802             return true;
18803         };
18804         startNode.cascade(f);
18805         if(this.remove){
18806            for(var id in af){
18807                if(typeof id != "function"){
18808                    var n = af[id];
18809                    if(n && n.parentNode){
18810                        n.parentNode.removeChild(n);
18811                    }
18812                }
18813            }
18814         }
18815     },
18816
18817     /**
18818      * Clears the current filter. Note: with the "remove" option
18819      * set a filter cannot be cleared.
18820      */
18821     clear : function(){
18822         var t = this.tree;
18823         var af = this.filtered;
18824         for(var id in af){
18825             if(typeof id != "function"){
18826                 var n = af[id];
18827                 if(n){
18828                     n.ui.show();
18829                 }
18830             }
18831         }
18832         this.filtered = {};
18833     }
18834 };
18835 /*
18836  * Based on:
18837  * Ext JS Library 1.1.1
18838  * Copyright(c) 2006-2007, Ext JS, LLC.
18839  *
18840  * Originally Released Under LGPL - original licence link has changed is not relivant.
18841  *
18842  * Fork - LGPL
18843  * <script type="text/javascript">
18844  */
18845  
18846
18847 /**
18848  * @class Roo.tree.TreeSorter
18849  * Provides sorting of nodes in a TreePanel
18850  * 
18851  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18852  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18853  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18854  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18855  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18856  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18857  * @constructor
18858  * @param {TreePanel} tree
18859  * @param {Object} config
18860  */
18861 Roo.tree.TreeSorter = function(tree, config){
18862     Roo.apply(this, config);
18863     tree.on("beforechildrenrendered", this.doSort, this);
18864     tree.on("append", this.updateSort, this);
18865     tree.on("insert", this.updateSort, this);
18866     
18867     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18868     var p = this.property || "text";
18869     var sortType = this.sortType;
18870     var fs = this.folderSort;
18871     var cs = this.caseSensitive === true;
18872     var leafAttr = this.leafAttr || 'leaf';
18873
18874     this.sortFn = function(n1, n2){
18875         if(fs){
18876             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18877                 return 1;
18878             }
18879             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18880                 return -1;
18881             }
18882         }
18883         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18884         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18885         if(v1 < v2){
18886                         return dsc ? +1 : -1;
18887                 }else if(v1 > v2){
18888                         return dsc ? -1 : +1;
18889         }else{
18890                 return 0;
18891         }
18892     };
18893 };
18894
18895 Roo.tree.TreeSorter.prototype = {
18896     doSort : function(node){
18897         node.sort(this.sortFn);
18898     },
18899     
18900     compareNodes : function(n1, n2){
18901         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18902     },
18903     
18904     updateSort : function(tree, node){
18905         if(node.childrenRendered){
18906             this.doSort.defer(1, this, [node]);
18907         }
18908     }
18909 };/*
18910  * Based on:
18911  * Ext JS Library 1.1.1
18912  * Copyright(c) 2006-2007, Ext JS, LLC.
18913  *
18914  * Originally Released Under LGPL - original licence link has changed is not relivant.
18915  *
18916  * Fork - LGPL
18917  * <script type="text/javascript">
18918  */
18919
18920 if(Roo.dd.DropZone){
18921     
18922 Roo.tree.TreeDropZone = function(tree, config){
18923     this.allowParentInsert = false;
18924     this.allowContainerDrop = false;
18925     this.appendOnly = false;
18926     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18927     this.tree = tree;
18928     this.lastInsertClass = "x-tree-no-status";
18929     this.dragOverData = {};
18930 };
18931
18932 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18933     ddGroup : "TreeDD",
18934     
18935     expandDelay : 1000,
18936     
18937     expandNode : function(node){
18938         if(node.hasChildNodes() && !node.isExpanded()){
18939             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18940         }
18941     },
18942     
18943     queueExpand : function(node){
18944         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18945     },
18946     
18947     cancelExpand : function(){
18948         if(this.expandProcId){
18949             clearTimeout(this.expandProcId);
18950             this.expandProcId = false;
18951         }
18952     },
18953     
18954     isValidDropPoint : function(n, pt, dd, e, data){
18955         if(!n || !data){ return false; }
18956         var targetNode = n.node;
18957         var dropNode = data.node;
18958         // default drop rules
18959         if(!(targetNode && targetNode.isTarget && pt)){
18960             return false;
18961         }
18962         if(pt == "append" && targetNode.allowChildren === false){
18963             return false;
18964         }
18965         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18966             return false;
18967         }
18968         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18969             return false;
18970         }
18971         // reuse the object
18972         var overEvent = this.dragOverData;
18973         overEvent.tree = this.tree;
18974         overEvent.target = targetNode;
18975         overEvent.data = data;
18976         overEvent.point = pt;
18977         overEvent.source = dd;
18978         overEvent.rawEvent = e;
18979         overEvent.dropNode = dropNode;
18980         overEvent.cancel = false;  
18981         var result = this.tree.fireEvent("nodedragover", overEvent);
18982         return overEvent.cancel === false && result !== false;
18983     },
18984     
18985     getDropPoint : function(e, n, dd){
18986         var tn = n.node;
18987         if(tn.isRoot){
18988             return tn.allowChildren !== false ? "append" : false; // always append for root
18989         }
18990         var dragEl = n.ddel;
18991         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
18992         var y = Roo.lib.Event.getPageY(e);
18993         //var noAppend = tn.allowChildren === false || tn.isLeaf();
18994         
18995         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
18996         var noAppend = tn.allowChildren === false;
18997         if(this.appendOnly || tn.parentNode.allowChildren === false){
18998             return noAppend ? false : "append";
18999         }
19000         var noBelow = false;
19001         if(!this.allowParentInsert){
19002             noBelow = tn.hasChildNodes() && tn.isExpanded();
19003         }
19004         var q = (b - t) / (noAppend ? 2 : 3);
19005         if(y >= t && y < (t + q)){
19006             return "above";
19007         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19008             return "below";
19009         }else{
19010             return "append";
19011         }
19012     },
19013     
19014     onNodeEnter : function(n, dd, e, data){
19015         this.cancelExpand();
19016     },
19017     
19018     onNodeOver : function(n, dd, e, data){
19019         var pt = this.getDropPoint(e, n, dd);
19020         var node = n.node;
19021         
19022         // auto node expand check
19023         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19024             this.queueExpand(node);
19025         }else if(pt != "append"){
19026             this.cancelExpand();
19027         }
19028         
19029         // set the insert point style on the target node
19030         var returnCls = this.dropNotAllowed;
19031         if(this.isValidDropPoint(n, pt, dd, e, data)){
19032            if(pt){
19033                var el = n.ddel;
19034                var cls;
19035                if(pt == "above"){
19036                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19037                    cls = "x-tree-drag-insert-above";
19038                }else if(pt == "below"){
19039                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19040                    cls = "x-tree-drag-insert-below";
19041                }else{
19042                    returnCls = "x-tree-drop-ok-append";
19043                    cls = "x-tree-drag-append";
19044                }
19045                if(this.lastInsertClass != cls){
19046                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19047                    this.lastInsertClass = cls;
19048                }
19049            }
19050        }
19051        return returnCls;
19052     },
19053     
19054     onNodeOut : function(n, dd, e, data){
19055         this.cancelExpand();
19056         this.removeDropIndicators(n);
19057     },
19058     
19059     onNodeDrop : function(n, dd, e, data){
19060         var point = this.getDropPoint(e, n, dd);
19061         var targetNode = n.node;
19062         targetNode.ui.startDrop();
19063         if(!this.isValidDropPoint(n, point, dd, e, data)){
19064             targetNode.ui.endDrop();
19065             return false;
19066         }
19067         // first try to find the drop node
19068         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19069         var dropEvent = {
19070             tree : this.tree,
19071             target: targetNode,
19072             data: data,
19073             point: point,
19074             source: dd,
19075             rawEvent: e,
19076             dropNode: dropNode,
19077             cancel: !dropNode   
19078         };
19079         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19080         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19081             targetNode.ui.endDrop();
19082             return false;
19083         }
19084         // allow target changing
19085         targetNode = dropEvent.target;
19086         if(point == "append" && !targetNode.isExpanded()){
19087             targetNode.expand(false, null, function(){
19088                 this.completeDrop(dropEvent);
19089             }.createDelegate(this));
19090         }else{
19091             this.completeDrop(dropEvent);
19092         }
19093         return true;
19094     },
19095     
19096     completeDrop : function(de){
19097         var ns = de.dropNode, p = de.point, t = de.target;
19098         if(!(ns instanceof Array)){
19099             ns = [ns];
19100         }
19101         var n;
19102         for(var i = 0, len = ns.length; i < len; i++){
19103             n = ns[i];
19104             if(p == "above"){
19105                 t.parentNode.insertBefore(n, t);
19106             }else if(p == "below"){
19107                 t.parentNode.insertBefore(n, t.nextSibling);
19108             }else{
19109                 t.appendChild(n);
19110             }
19111         }
19112         n.ui.focus();
19113         if(this.tree.hlDrop){
19114             n.ui.highlight();
19115         }
19116         t.ui.endDrop();
19117         this.tree.fireEvent("nodedrop", de);
19118     },
19119     
19120     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19121         if(this.tree.hlDrop){
19122             dropNode.ui.focus();
19123             dropNode.ui.highlight();
19124         }
19125         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19126     },
19127     
19128     getTree : function(){
19129         return this.tree;
19130     },
19131     
19132     removeDropIndicators : function(n){
19133         if(n && n.ddel){
19134             var el = n.ddel;
19135             Roo.fly(el).removeClass([
19136                     "x-tree-drag-insert-above",
19137                     "x-tree-drag-insert-below",
19138                     "x-tree-drag-append"]);
19139             this.lastInsertClass = "_noclass";
19140         }
19141     },
19142     
19143     beforeDragDrop : function(target, e, id){
19144         this.cancelExpand();
19145         return true;
19146     },
19147     
19148     afterRepair : function(data){
19149         if(data && Roo.enableFx){
19150             data.node.ui.highlight();
19151         }
19152         this.hideProxy();
19153     }    
19154 });
19155
19156 }
19157 /*
19158  * Based on:
19159  * Ext JS Library 1.1.1
19160  * Copyright(c) 2006-2007, Ext JS, LLC.
19161  *
19162  * Originally Released Under LGPL - original licence link has changed is not relivant.
19163  *
19164  * Fork - LGPL
19165  * <script type="text/javascript">
19166  */
19167  
19168
19169 if(Roo.dd.DragZone){
19170 Roo.tree.TreeDragZone = function(tree, config){
19171     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19172     this.tree = tree;
19173 };
19174
19175 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19176     ddGroup : "TreeDD",
19177     
19178     onBeforeDrag : function(data, e){
19179         var n = data.node;
19180         return n && n.draggable && !n.disabled;
19181     },
19182     
19183     onInitDrag : function(e){
19184         var data = this.dragData;
19185         this.tree.getSelectionModel().select(data.node);
19186         this.proxy.update("");
19187         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19188         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19189     },
19190     
19191     getRepairXY : function(e, data){
19192         return data.node.ui.getDDRepairXY();
19193     },
19194     
19195     onEndDrag : function(data, e){
19196         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19197     },
19198     
19199     onValidDrop : function(dd, e, id){
19200         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19201         this.hideProxy();
19202     },
19203     
19204     beforeInvalidDrop : function(e, id){
19205         // this scrolls the original position back into view
19206         var sm = this.tree.getSelectionModel();
19207         sm.clearSelections();
19208         sm.select(this.dragData.node);
19209     }
19210 });
19211 }/*
19212  * Based on:
19213  * Ext JS Library 1.1.1
19214  * Copyright(c) 2006-2007, Ext JS, LLC.
19215  *
19216  * Originally Released Under LGPL - original licence link has changed is not relivant.
19217  *
19218  * Fork - LGPL
19219  * <script type="text/javascript">
19220  */
19221 /**
19222  * @class Roo.tree.TreeEditor
19223  * @extends Roo.Editor
19224  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19225  * as the editor field.
19226  * @constructor
19227  * @param {Object} config (used to be the tree panel.)
19228  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19229  * 
19230  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19231  * @cfg {Roo.form.TextField|Object} field The field configuration
19232  *
19233  * 
19234  */
19235 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19236     var tree = config;
19237     var field;
19238     if (oldconfig) { // old style..
19239         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19240     } else {
19241         // new style..
19242         tree = config.tree;
19243         config.field = config.field  || {};
19244         config.field.xtype = 'TextField';
19245         field = Roo.factory(config.field, Roo.form);
19246     }
19247     config = config || {};
19248     
19249     
19250     this.addEvents({
19251         /**
19252          * @event beforenodeedit
19253          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19254          * false from the handler of this event.
19255          * @param {Editor} this
19256          * @param {Roo.tree.Node} node 
19257          */
19258         "beforenodeedit" : true
19259     });
19260     
19261     //Roo.log(config);
19262     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19263
19264     this.tree = tree;
19265
19266     tree.on('beforeclick', this.beforeNodeClick, this);
19267     tree.getTreeEl().on('mousedown', this.hide, this);
19268     this.on('complete', this.updateNode, this);
19269     this.on('beforestartedit', this.fitToTree, this);
19270     this.on('startedit', this.bindScroll, this, {delay:10});
19271     this.on('specialkey', this.onSpecialKey, this);
19272 };
19273
19274 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19275     /**
19276      * @cfg {String} alignment
19277      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19278      */
19279     alignment: "l-l",
19280     // inherit
19281     autoSize: false,
19282     /**
19283      * @cfg {Boolean} hideEl
19284      * True to hide the bound element while the editor is displayed (defaults to false)
19285      */
19286     hideEl : false,
19287     /**
19288      * @cfg {String} cls
19289      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19290      */
19291     cls: "x-small-editor x-tree-editor",
19292     /**
19293      * @cfg {Boolean} shim
19294      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19295      */
19296     shim:false,
19297     // inherit
19298     shadow:"frame",
19299     /**
19300      * @cfg {Number} maxWidth
19301      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19302      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19303      * scroll and client offsets into account prior to each edit.
19304      */
19305     maxWidth: 250,
19306
19307     editDelay : 350,
19308
19309     // private
19310     fitToTree : function(ed, el){
19311         var td = this.tree.getTreeEl().dom, nd = el.dom;
19312         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19313             td.scrollLeft = nd.offsetLeft;
19314         }
19315         var w = Math.min(
19316                 this.maxWidth,
19317                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19318         this.setSize(w, '');
19319         
19320         return this.fireEvent('beforenodeedit', this, this.editNode);
19321         
19322     },
19323
19324     // private
19325     triggerEdit : function(node){
19326         this.completeEdit();
19327         this.editNode = node;
19328         this.startEdit(node.ui.textNode, node.text);
19329     },
19330
19331     // private
19332     bindScroll : function(){
19333         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19334     },
19335
19336     // private
19337     beforeNodeClick : function(node, e){
19338         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19339         this.lastClick = new Date();
19340         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19341             e.stopEvent();
19342             this.triggerEdit(node);
19343             return false;
19344         }
19345         return true;
19346     },
19347
19348     // private
19349     updateNode : function(ed, value){
19350         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19351         this.editNode.setText(value);
19352     },
19353
19354     // private
19355     onHide : function(){
19356         Roo.tree.TreeEditor.superclass.onHide.call(this);
19357         if(this.editNode){
19358             this.editNode.ui.focus();
19359         }
19360     },
19361
19362     // private
19363     onSpecialKey : function(field, e){
19364         var k = e.getKey();
19365         if(k == e.ESC){
19366             e.stopEvent();
19367             this.cancelEdit();
19368         }else if(k == e.ENTER && !e.hasModifier()){
19369             e.stopEvent();
19370             this.completeEdit();
19371         }
19372     }
19373 });//<Script type="text/javascript">
19374 /*
19375  * Based on:
19376  * Ext JS Library 1.1.1
19377  * Copyright(c) 2006-2007, Ext JS, LLC.
19378  *
19379  * Originally Released Under LGPL - original licence link has changed is not relivant.
19380  *
19381  * Fork - LGPL
19382  * <script type="text/javascript">
19383  */
19384  
19385 /**
19386  * Not documented??? - probably should be...
19387  */
19388
19389 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19390     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19391     
19392     renderElements : function(n, a, targetNode, bulkRender){
19393         //consel.log("renderElements?");
19394         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19395
19396         var t = n.getOwnerTree();
19397         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19398         
19399         var cols = t.columns;
19400         var bw = t.borderWidth;
19401         var c = cols[0];
19402         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19403          var cb = typeof a.checked == "boolean";
19404         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19405         var colcls = 'x-t-' + tid + '-c0';
19406         var buf = [
19407             '<li class="x-tree-node">',
19408             
19409                 
19410                 '<div class="x-tree-node-el ', a.cls,'">',
19411                     // extran...
19412                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19413                 
19414                 
19415                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19416                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19417                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19418                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19419                            (a.iconCls ? ' '+a.iconCls : ''),
19420                            '" unselectable="on" />',
19421                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19422                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19423                              
19424                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19425                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19426                             '<span unselectable="on" qtip="' + tx + '">',
19427                              tx,
19428                              '</span></a>' ,
19429                     '</div>',
19430                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19431                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19432                  ];
19433         for(var i = 1, len = cols.length; i < len; i++){
19434             c = cols[i];
19435             colcls = 'x-t-' + tid + '-c' +i;
19436             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19437             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19438                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19439                       "</div>");
19440          }
19441          
19442          buf.push(
19443             '</a>',
19444             '<div class="x-clear"></div></div>',
19445             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19446             "</li>");
19447         
19448         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19449             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19450                                 n.nextSibling.ui.getEl(), buf.join(""));
19451         }else{
19452             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19453         }
19454         var el = this.wrap.firstChild;
19455         this.elRow = el;
19456         this.elNode = el.firstChild;
19457         this.ranchor = el.childNodes[1];
19458         this.ctNode = this.wrap.childNodes[1];
19459         var cs = el.firstChild.childNodes;
19460         this.indentNode = cs[0];
19461         this.ecNode = cs[1];
19462         this.iconNode = cs[2];
19463         var index = 3;
19464         if(cb){
19465             this.checkbox = cs[3];
19466             index++;
19467         }
19468         this.anchor = cs[index];
19469         
19470         this.textNode = cs[index].firstChild;
19471         
19472         //el.on("click", this.onClick, this);
19473         //el.on("dblclick", this.onDblClick, this);
19474         
19475         
19476        // console.log(this);
19477     },
19478     initEvents : function(){
19479         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19480         
19481             
19482         var a = this.ranchor;
19483
19484         var el = Roo.get(a);
19485
19486         if(Roo.isOpera){ // opera render bug ignores the CSS
19487             el.setStyle("text-decoration", "none");
19488         }
19489
19490         el.on("click", this.onClick, this);
19491         el.on("dblclick", this.onDblClick, this);
19492         el.on("contextmenu", this.onContextMenu, this);
19493         
19494     },
19495     
19496     /*onSelectedChange : function(state){
19497         if(state){
19498             this.focus();
19499             this.addClass("x-tree-selected");
19500         }else{
19501             //this.blur();
19502             this.removeClass("x-tree-selected");
19503         }
19504     },*/
19505     addClass : function(cls){
19506         if(this.elRow){
19507             Roo.fly(this.elRow).addClass(cls);
19508         }
19509         
19510     },
19511     
19512     
19513     removeClass : function(cls){
19514         if(this.elRow){
19515             Roo.fly(this.elRow).removeClass(cls);
19516         }
19517     }
19518
19519     
19520     
19521 });//<Script type="text/javascript">
19522
19523 /*
19524  * Based on:
19525  * Ext JS Library 1.1.1
19526  * Copyright(c) 2006-2007, Ext JS, LLC.
19527  *
19528  * Originally Released Under LGPL - original licence link has changed is not relivant.
19529  *
19530  * Fork - LGPL
19531  * <script type="text/javascript">
19532  */
19533  
19534
19535 /**
19536  * @class Roo.tree.ColumnTree
19537  * @extends Roo.data.TreePanel
19538  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19539  * @cfg {int} borderWidth  compined right/left border allowance
19540  * @constructor
19541  * @param {String/HTMLElement/Element} el The container element
19542  * @param {Object} config
19543  */
19544 Roo.tree.ColumnTree =  function(el, config)
19545 {
19546    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19547    this.addEvents({
19548         /**
19549         * @event resize
19550         * Fire this event on a container when it resizes
19551         * @param {int} w Width
19552         * @param {int} h Height
19553         */
19554        "resize" : true
19555     });
19556     this.on('resize', this.onResize, this);
19557 };
19558
19559 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19560     //lines:false,
19561     
19562     
19563     borderWidth: Roo.isBorderBox ? 0 : 2, 
19564     headEls : false,
19565     
19566     render : function(){
19567         // add the header.....
19568        
19569         Roo.tree.ColumnTree.superclass.render.apply(this);
19570         
19571         this.el.addClass('x-column-tree');
19572         
19573         this.headers = this.el.createChild(
19574             {cls:'x-tree-headers'},this.innerCt.dom);
19575    
19576         var cols = this.columns, c;
19577         var totalWidth = 0;
19578         this.headEls = [];
19579         var  len = cols.length;
19580         for(var i = 0; i < len; i++){
19581              c = cols[i];
19582              totalWidth += c.width;
19583             this.headEls.push(this.headers.createChild({
19584                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19585                  cn: {
19586                      cls:'x-tree-hd-text',
19587                      html: c.header
19588                  },
19589                  style:'width:'+(c.width-this.borderWidth)+'px;'
19590              }));
19591         }
19592         this.headers.createChild({cls:'x-clear'});
19593         // prevent floats from wrapping when clipped
19594         this.headers.setWidth(totalWidth);
19595         //this.innerCt.setWidth(totalWidth);
19596         this.innerCt.setStyle({ overflow: 'auto' });
19597         this.onResize(this.width, this.height);
19598              
19599         
19600     },
19601     onResize : function(w,h)
19602     {
19603         this.height = h;
19604         this.width = w;
19605         // resize cols..
19606         this.innerCt.setWidth(this.width);
19607         this.innerCt.setHeight(this.height-20);
19608         
19609         // headers...
19610         var cols = this.columns, c;
19611         var totalWidth = 0;
19612         var expEl = false;
19613         var len = cols.length;
19614         for(var i = 0; i < len; i++){
19615             c = cols[i];
19616             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19617                 // it's the expander..
19618                 expEl  = this.headEls[i];
19619                 continue;
19620             }
19621             totalWidth += c.width;
19622             
19623         }
19624         if (expEl) {
19625             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19626         }
19627         this.headers.setWidth(w-20);
19628
19629         
19630         
19631         
19632     }
19633 });
19634 /*
19635  * Based on:
19636  * Ext JS Library 1.1.1
19637  * Copyright(c) 2006-2007, Ext JS, LLC.
19638  *
19639  * Originally Released Under LGPL - original licence link has changed is not relivant.
19640  *
19641  * Fork - LGPL
19642  * <script type="text/javascript">
19643  */
19644  
19645 /**
19646  * @class Roo.menu.Menu
19647  * @extends Roo.util.Observable
19648  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19649  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19650  * @constructor
19651  * Creates a new Menu
19652  * @param {Object} config Configuration options
19653  */
19654 Roo.menu.Menu = function(config){
19655     Roo.apply(this, config);
19656     this.id = this.id || Roo.id();
19657     this.addEvents({
19658         /**
19659          * @event beforeshow
19660          * Fires before this menu is displayed
19661          * @param {Roo.menu.Menu} this
19662          */
19663         beforeshow : true,
19664         /**
19665          * @event beforehide
19666          * Fires before this menu is hidden
19667          * @param {Roo.menu.Menu} this
19668          */
19669         beforehide : true,
19670         /**
19671          * @event show
19672          * Fires after this menu is displayed
19673          * @param {Roo.menu.Menu} this
19674          */
19675         show : true,
19676         /**
19677          * @event hide
19678          * Fires after this menu is hidden
19679          * @param {Roo.menu.Menu} this
19680          */
19681         hide : true,
19682         /**
19683          * @event click
19684          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19685          * @param {Roo.menu.Menu} this
19686          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19687          * @param {Roo.EventObject} e
19688          */
19689         click : true,
19690         /**
19691          * @event mouseover
19692          * Fires when the mouse is hovering over this menu
19693          * @param {Roo.menu.Menu} this
19694          * @param {Roo.EventObject} e
19695          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19696          */
19697         mouseover : true,
19698         /**
19699          * @event mouseout
19700          * Fires when the mouse exits this menu
19701          * @param {Roo.menu.Menu} this
19702          * @param {Roo.EventObject} e
19703          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19704          */
19705         mouseout : true,
19706         /**
19707          * @event itemclick
19708          * Fires when a menu item contained in this menu is clicked
19709          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19710          * @param {Roo.EventObject} e
19711          */
19712         itemclick: true
19713     });
19714     if (this.registerMenu) {
19715         Roo.menu.MenuMgr.register(this);
19716     }
19717     
19718     var mis = this.items;
19719     this.items = new Roo.util.MixedCollection();
19720     if(mis){
19721         this.add.apply(this, mis);
19722     }
19723 };
19724
19725 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19726     /**
19727      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19728      */
19729     minWidth : 120,
19730     /**
19731      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19732      * for bottom-right shadow (defaults to "sides")
19733      */
19734     shadow : "sides",
19735     /**
19736      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19737      * this menu (defaults to "tl-tr?")
19738      */
19739     subMenuAlign : "tl-tr?",
19740     /**
19741      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19742      * relative to its element of origin (defaults to "tl-bl?")
19743      */
19744     defaultAlign : "tl-bl?",
19745     /**
19746      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19747      */
19748     allowOtherMenus : false,
19749     /**
19750      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19751      */
19752     registerMenu : true,
19753
19754     hidden:true,
19755
19756     // private
19757     render : function(){
19758         if(this.el){
19759             return;
19760         }
19761         var el = this.el = new Roo.Layer({
19762             cls: "x-menu",
19763             shadow:this.shadow,
19764             constrain: false,
19765             parentEl: this.parentEl || document.body,
19766             zindex:15000
19767         });
19768
19769         this.keyNav = new Roo.menu.MenuNav(this);
19770
19771         if(this.plain){
19772             el.addClass("x-menu-plain");
19773         }
19774         if(this.cls){
19775             el.addClass(this.cls);
19776         }
19777         // generic focus element
19778         this.focusEl = el.createChild({
19779             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19780         });
19781         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19782         ul.on("click", this.onClick, this);
19783         ul.on("mouseover", this.onMouseOver, this);
19784         ul.on("mouseout", this.onMouseOut, this);
19785         this.items.each(function(item){
19786             var li = document.createElement("li");
19787             li.className = "x-menu-list-item";
19788             ul.dom.appendChild(li);
19789             item.render(li, this);
19790         }, this);
19791         this.ul = ul;
19792         this.autoWidth();
19793     },
19794
19795     // private
19796     autoWidth : function(){
19797         var el = this.el, ul = this.ul;
19798         if(!el){
19799             return;
19800         }
19801         var w = this.width;
19802         if(w){
19803             el.setWidth(w);
19804         }else if(Roo.isIE){
19805             el.setWidth(this.minWidth);
19806             var t = el.dom.offsetWidth; // force recalc
19807             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19808         }
19809     },
19810
19811     // private
19812     delayAutoWidth : function(){
19813         if(this.rendered){
19814             if(!this.awTask){
19815                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19816             }
19817             this.awTask.delay(20);
19818         }
19819     },
19820
19821     // private
19822     findTargetItem : function(e){
19823         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19824         if(t && t.menuItemId){
19825             return this.items.get(t.menuItemId);
19826         }
19827     },
19828
19829     // private
19830     onClick : function(e){
19831         var t;
19832         if(t = this.findTargetItem(e)){
19833             t.onClick(e);
19834             this.fireEvent("click", this, t, e);
19835         }
19836     },
19837
19838     // private
19839     setActiveItem : function(item, autoExpand){
19840         if(item != this.activeItem){
19841             if(this.activeItem){
19842                 this.activeItem.deactivate();
19843             }
19844             this.activeItem = item;
19845             item.activate(autoExpand);
19846         }else if(autoExpand){
19847             item.expandMenu();
19848         }
19849     },
19850
19851     // private
19852     tryActivate : function(start, step){
19853         var items = this.items;
19854         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19855             var item = items.get(i);
19856             if(!item.disabled && item.canActivate){
19857                 this.setActiveItem(item, false);
19858                 return item;
19859             }
19860         }
19861         return false;
19862     },
19863
19864     // private
19865     onMouseOver : function(e){
19866         var t;
19867         if(t = this.findTargetItem(e)){
19868             if(t.canActivate && !t.disabled){
19869                 this.setActiveItem(t, true);
19870             }
19871         }
19872         this.fireEvent("mouseover", this, e, t);
19873     },
19874
19875     // private
19876     onMouseOut : function(e){
19877         var t;
19878         if(t = this.findTargetItem(e)){
19879             if(t == this.activeItem && t.shouldDeactivate(e)){
19880                 this.activeItem.deactivate();
19881                 delete this.activeItem;
19882             }
19883         }
19884         this.fireEvent("mouseout", this, e, t);
19885     },
19886
19887     /**
19888      * Read-only.  Returns true if the menu is currently displayed, else false.
19889      * @type Boolean
19890      */
19891     isVisible : function(){
19892         return this.el && !this.hidden;
19893     },
19894
19895     /**
19896      * Displays this menu relative to another element
19897      * @param {String/HTMLElement/Roo.Element} element The element to align to
19898      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19899      * the element (defaults to this.defaultAlign)
19900      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19901      */
19902     show : function(el, pos, parentMenu){
19903         this.parentMenu = parentMenu;
19904         if(!this.el){
19905             this.render();
19906         }
19907         this.fireEvent("beforeshow", this);
19908         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19909     },
19910
19911     /**
19912      * Displays this menu at a specific xy position
19913      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19914      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19915      */
19916     showAt : function(xy, parentMenu, /* private: */_e){
19917         this.parentMenu = parentMenu;
19918         if(!this.el){
19919             this.render();
19920         }
19921         if(_e !== false){
19922             this.fireEvent("beforeshow", this);
19923             xy = this.el.adjustForConstraints(xy);
19924         }
19925         this.el.setXY(xy);
19926         this.el.show();
19927         this.hidden = false;
19928         this.focus();
19929         this.fireEvent("show", this);
19930     },
19931
19932     focus : function(){
19933         if(!this.hidden){
19934             this.doFocus.defer(50, this);
19935         }
19936     },
19937
19938     doFocus : function(){
19939         if(!this.hidden){
19940             this.focusEl.focus();
19941         }
19942     },
19943
19944     /**
19945      * Hides this menu and optionally all parent menus
19946      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19947      */
19948     hide : function(deep){
19949         if(this.el && this.isVisible()){
19950             this.fireEvent("beforehide", this);
19951             if(this.activeItem){
19952                 this.activeItem.deactivate();
19953                 this.activeItem = null;
19954             }
19955             this.el.hide();
19956             this.hidden = true;
19957             this.fireEvent("hide", this);
19958         }
19959         if(deep === true && this.parentMenu){
19960             this.parentMenu.hide(true);
19961         }
19962     },
19963
19964     /**
19965      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19966      * Any of the following are valid:
19967      * <ul>
19968      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19969      * <li>An HTMLElement object which will be converted to a menu item</li>
19970      * <li>A menu item config object that will be created as a new menu item</li>
19971      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19972      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19973      * </ul>
19974      * Usage:
19975      * <pre><code>
19976 // Create the menu
19977 var menu = new Roo.menu.Menu();
19978
19979 // Create a menu item to add by reference
19980 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
19981
19982 // Add a bunch of items at once using different methods.
19983 // Only the last item added will be returned.
19984 var item = menu.add(
19985     menuItem,                // add existing item by ref
19986     'Dynamic Item',          // new TextItem
19987     '-',                     // new separator
19988     { text: 'Config Item' }  // new item by config
19989 );
19990 </code></pre>
19991      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
19992      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
19993      */
19994     add : function(){
19995         var a = arguments, l = a.length, item;
19996         for(var i = 0; i < l; i++){
19997             var el = a[i];
19998             if ((typeof(el) == "object") && el.xtype && el.xns) {
19999                 el = Roo.factory(el, Roo.menu);
20000             }
20001             
20002             if(el.render){ // some kind of Item
20003                 item = this.addItem(el);
20004             }else if(typeof el == "string"){ // string
20005                 if(el == "separator" || el == "-"){
20006                     item = this.addSeparator();
20007                 }else{
20008                     item = this.addText(el);
20009                 }
20010             }else if(el.tagName || el.el){ // element
20011                 item = this.addElement(el);
20012             }else if(typeof el == "object"){ // must be menu item config?
20013                 item = this.addMenuItem(el);
20014             }
20015         }
20016         return item;
20017     },
20018
20019     /**
20020      * Returns this menu's underlying {@link Roo.Element} object
20021      * @return {Roo.Element} The element
20022      */
20023     getEl : function(){
20024         if(!this.el){
20025             this.render();
20026         }
20027         return this.el;
20028     },
20029
20030     /**
20031      * Adds a separator bar to the menu
20032      * @return {Roo.menu.Item} The menu item that was added
20033      */
20034     addSeparator : function(){
20035         return this.addItem(new Roo.menu.Separator());
20036     },
20037
20038     /**
20039      * Adds an {@link Roo.Element} object to the menu
20040      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20041      * @return {Roo.menu.Item} The menu item that was added
20042      */
20043     addElement : function(el){
20044         return this.addItem(new Roo.menu.BaseItem(el));
20045     },
20046
20047     /**
20048      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20049      * @param {Roo.menu.Item} item The menu item to add
20050      * @return {Roo.menu.Item} The menu item that was added
20051      */
20052     addItem : function(item){
20053         this.items.add(item);
20054         if(this.ul){
20055             var li = document.createElement("li");
20056             li.className = "x-menu-list-item";
20057             this.ul.dom.appendChild(li);
20058             item.render(li, this);
20059             this.delayAutoWidth();
20060         }
20061         return item;
20062     },
20063
20064     /**
20065      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20066      * @param {Object} config A MenuItem config object
20067      * @return {Roo.menu.Item} The menu item that was added
20068      */
20069     addMenuItem : function(config){
20070         if(!(config instanceof Roo.menu.Item)){
20071             if(typeof config.checked == "boolean"){ // must be check menu item config?
20072                 config = new Roo.menu.CheckItem(config);
20073             }else{
20074                 config = new Roo.menu.Item(config);
20075             }
20076         }
20077         return this.addItem(config);
20078     },
20079
20080     /**
20081      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20082      * @param {String} text The text to display in the menu item
20083      * @return {Roo.menu.Item} The menu item that was added
20084      */
20085     addText : function(text){
20086         return this.addItem(new Roo.menu.TextItem({ text : text }));
20087     },
20088
20089     /**
20090      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20091      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20092      * @param {Roo.menu.Item} item The menu item to add
20093      * @return {Roo.menu.Item} The menu item that was added
20094      */
20095     insert : function(index, item){
20096         this.items.insert(index, item);
20097         if(this.ul){
20098             var li = document.createElement("li");
20099             li.className = "x-menu-list-item";
20100             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20101             item.render(li, this);
20102             this.delayAutoWidth();
20103         }
20104         return item;
20105     },
20106
20107     /**
20108      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20109      * @param {Roo.menu.Item} item The menu item to remove
20110      */
20111     remove : function(item){
20112         this.items.removeKey(item.id);
20113         item.destroy();
20114     },
20115
20116     /**
20117      * Removes and destroys all items in the menu
20118      */
20119     removeAll : function(){
20120         var f;
20121         while(f = this.items.first()){
20122             this.remove(f);
20123         }
20124     }
20125 });
20126
20127 // MenuNav is a private utility class used internally by the Menu
20128 Roo.menu.MenuNav = function(menu){
20129     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20130     this.scope = this.menu = menu;
20131 };
20132
20133 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20134     doRelay : function(e, h){
20135         var k = e.getKey();
20136         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20137             this.menu.tryActivate(0, 1);
20138             return false;
20139         }
20140         return h.call(this.scope || this, e, this.menu);
20141     },
20142
20143     up : function(e, m){
20144         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20145             m.tryActivate(m.items.length-1, -1);
20146         }
20147     },
20148
20149     down : function(e, m){
20150         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20151             m.tryActivate(0, 1);
20152         }
20153     },
20154
20155     right : function(e, m){
20156         if(m.activeItem){
20157             m.activeItem.expandMenu(true);
20158         }
20159     },
20160
20161     left : function(e, m){
20162         m.hide();
20163         if(m.parentMenu && m.parentMenu.activeItem){
20164             m.parentMenu.activeItem.activate();
20165         }
20166     },
20167
20168     enter : function(e, m){
20169         if(m.activeItem){
20170             e.stopPropagation();
20171             m.activeItem.onClick(e);
20172             m.fireEvent("click", this, m.activeItem);
20173             return true;
20174         }
20175     }
20176 });/*
20177  * Based on:
20178  * Ext JS Library 1.1.1
20179  * Copyright(c) 2006-2007, Ext JS, LLC.
20180  *
20181  * Originally Released Under LGPL - original licence link has changed is not relivant.
20182  *
20183  * Fork - LGPL
20184  * <script type="text/javascript">
20185  */
20186  
20187 /**
20188  * @class Roo.menu.MenuMgr
20189  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20190  * @singleton
20191  */
20192 Roo.menu.MenuMgr = function(){
20193    var menus, active, groups = {}, attached = false, lastShow = new Date();
20194
20195    // private - called when first menu is created
20196    function init(){
20197        menus = {};
20198        active = new Roo.util.MixedCollection();
20199        Roo.get(document).addKeyListener(27, function(){
20200            if(active.length > 0){
20201                hideAll();
20202            }
20203        });
20204    }
20205
20206    // private
20207    function hideAll(){
20208        if(active && active.length > 0){
20209            var c = active.clone();
20210            c.each(function(m){
20211                m.hide();
20212            });
20213        }
20214    }
20215
20216    // private
20217    function onHide(m){
20218        active.remove(m);
20219        if(active.length < 1){
20220            Roo.get(document).un("mousedown", onMouseDown);
20221            attached = false;
20222        }
20223    }
20224
20225    // private
20226    function onShow(m){
20227        var last = active.last();
20228        lastShow = new Date();
20229        active.add(m);
20230        if(!attached){
20231            Roo.get(document).on("mousedown", onMouseDown);
20232            attached = true;
20233        }
20234        if(m.parentMenu){
20235           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20236           m.parentMenu.activeChild = m;
20237        }else if(last && last.isVisible()){
20238           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20239        }
20240    }
20241
20242    // private
20243    function onBeforeHide(m){
20244        if(m.activeChild){
20245            m.activeChild.hide();
20246        }
20247        if(m.autoHideTimer){
20248            clearTimeout(m.autoHideTimer);
20249            delete m.autoHideTimer;
20250        }
20251    }
20252
20253    // private
20254    function onBeforeShow(m){
20255        var pm = m.parentMenu;
20256        if(!pm && !m.allowOtherMenus){
20257            hideAll();
20258        }else if(pm && pm.activeChild && active != m){
20259            pm.activeChild.hide();
20260        }
20261    }
20262
20263    // private
20264    function onMouseDown(e){
20265        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20266            hideAll();
20267        }
20268    }
20269
20270    // private
20271    function onBeforeCheck(mi, state){
20272        if(state){
20273            var g = groups[mi.group];
20274            for(var i = 0, l = g.length; i < l; i++){
20275                if(g[i] != mi){
20276                    g[i].setChecked(false);
20277                }
20278            }
20279        }
20280    }
20281
20282    return {
20283
20284        /**
20285         * Hides all menus that are currently visible
20286         */
20287        hideAll : function(){
20288             hideAll();  
20289        },
20290
20291        // private
20292        register : function(menu){
20293            if(!menus){
20294                init();
20295            }
20296            menus[menu.id] = menu;
20297            menu.on("beforehide", onBeforeHide);
20298            menu.on("hide", onHide);
20299            menu.on("beforeshow", onBeforeShow);
20300            menu.on("show", onShow);
20301            var g = menu.group;
20302            if(g && menu.events["checkchange"]){
20303                if(!groups[g]){
20304                    groups[g] = [];
20305                }
20306                groups[g].push(menu);
20307                menu.on("checkchange", onCheck);
20308            }
20309        },
20310
20311         /**
20312          * Returns a {@link Roo.menu.Menu} object
20313          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20314          * be used to generate and return a new Menu instance.
20315          */
20316        get : function(menu){
20317            if(typeof menu == "string"){ // menu id
20318                return menus[menu];
20319            }else if(menu.events){  // menu instance
20320                return menu;
20321            }else if(typeof menu.length == 'number'){ // array of menu items?
20322                return new Roo.menu.Menu({items:menu});
20323            }else{ // otherwise, must be a config
20324                return new Roo.menu.Menu(menu);
20325            }
20326        },
20327
20328        // private
20329        unregister : function(menu){
20330            delete menus[menu.id];
20331            menu.un("beforehide", onBeforeHide);
20332            menu.un("hide", onHide);
20333            menu.un("beforeshow", onBeforeShow);
20334            menu.un("show", onShow);
20335            var g = menu.group;
20336            if(g && menu.events["checkchange"]){
20337                groups[g].remove(menu);
20338                menu.un("checkchange", onCheck);
20339            }
20340        },
20341
20342        // private
20343        registerCheckable : function(menuItem){
20344            var g = menuItem.group;
20345            if(g){
20346                if(!groups[g]){
20347                    groups[g] = [];
20348                }
20349                groups[g].push(menuItem);
20350                menuItem.on("beforecheckchange", onBeforeCheck);
20351            }
20352        },
20353
20354        // private
20355        unregisterCheckable : function(menuItem){
20356            var g = menuItem.group;
20357            if(g){
20358                groups[g].remove(menuItem);
20359                menuItem.un("beforecheckchange", onBeforeCheck);
20360            }
20361        }
20362    };
20363 }();/*
20364  * Based on:
20365  * Ext JS Library 1.1.1
20366  * Copyright(c) 2006-2007, Ext JS, LLC.
20367  *
20368  * Originally Released Under LGPL - original licence link has changed is not relivant.
20369  *
20370  * Fork - LGPL
20371  * <script type="text/javascript">
20372  */
20373  
20374
20375 /**
20376  * @class Roo.menu.BaseItem
20377  * @extends Roo.Component
20378  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20379  * management and base configuration options shared by all menu components.
20380  * @constructor
20381  * Creates a new BaseItem
20382  * @param {Object} config Configuration options
20383  */
20384 Roo.menu.BaseItem = function(config){
20385     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20386
20387     this.addEvents({
20388         /**
20389          * @event click
20390          * Fires when this item is clicked
20391          * @param {Roo.menu.BaseItem} this
20392          * @param {Roo.EventObject} e
20393          */
20394         click: true,
20395         /**
20396          * @event activate
20397          * Fires when this item is activated
20398          * @param {Roo.menu.BaseItem} this
20399          */
20400         activate : true,
20401         /**
20402          * @event deactivate
20403          * Fires when this item is deactivated
20404          * @param {Roo.menu.BaseItem} this
20405          */
20406         deactivate : true
20407     });
20408
20409     if(this.handler){
20410         this.on("click", this.handler, this.scope, true);
20411     }
20412 };
20413
20414 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20415     /**
20416      * @cfg {Function} handler
20417      * A function that will handle the click event of this menu item (defaults to undefined)
20418      */
20419     /**
20420      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20421      */
20422     canActivate : false,
20423     /**
20424      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20425      */
20426     activeClass : "x-menu-item-active",
20427     /**
20428      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20429      */
20430     hideOnClick : true,
20431     /**
20432      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20433      */
20434     hideDelay : 100,
20435
20436     // private
20437     ctype: "Roo.menu.BaseItem",
20438
20439     // private
20440     actionMode : "container",
20441
20442     // private
20443     render : function(container, parentMenu){
20444         this.parentMenu = parentMenu;
20445         Roo.menu.BaseItem.superclass.render.call(this, container);
20446         this.container.menuItemId = this.id;
20447     },
20448
20449     // private
20450     onRender : function(container, position){
20451         this.el = Roo.get(this.el);
20452         container.dom.appendChild(this.el.dom);
20453     },
20454
20455     // private
20456     onClick : function(e){
20457         if(!this.disabled && this.fireEvent("click", this, e) !== false
20458                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20459             this.handleClick(e);
20460         }else{
20461             e.stopEvent();
20462         }
20463     },
20464
20465     // private
20466     activate : function(){
20467         if(this.disabled){
20468             return false;
20469         }
20470         var li = this.container;
20471         li.addClass(this.activeClass);
20472         this.region = li.getRegion().adjust(2, 2, -2, -2);
20473         this.fireEvent("activate", this);
20474         return true;
20475     },
20476
20477     // private
20478     deactivate : function(){
20479         this.container.removeClass(this.activeClass);
20480         this.fireEvent("deactivate", this);
20481     },
20482
20483     // private
20484     shouldDeactivate : function(e){
20485         return !this.region || !this.region.contains(e.getPoint());
20486     },
20487
20488     // private
20489     handleClick : function(e){
20490         if(this.hideOnClick){
20491             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20492         }
20493     },
20494
20495     // private
20496     expandMenu : function(autoActivate){
20497         // do nothing
20498     },
20499
20500     // private
20501     hideMenu : function(){
20502         // do nothing
20503     }
20504 });/*
20505  * Based on:
20506  * Ext JS Library 1.1.1
20507  * Copyright(c) 2006-2007, Ext JS, LLC.
20508  *
20509  * Originally Released Under LGPL - original licence link has changed is not relivant.
20510  *
20511  * Fork - LGPL
20512  * <script type="text/javascript">
20513  */
20514  
20515 /**
20516  * @class Roo.menu.Adapter
20517  * @extends Roo.menu.BaseItem
20518  * 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.
20519  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20520  * @constructor
20521  * Creates a new Adapter
20522  * @param {Object} config Configuration options
20523  */
20524 Roo.menu.Adapter = function(component, config){
20525     Roo.menu.Adapter.superclass.constructor.call(this, config);
20526     this.component = component;
20527 };
20528 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20529     // private
20530     canActivate : true,
20531
20532     // private
20533     onRender : function(container, position){
20534         this.component.render(container);
20535         this.el = this.component.getEl();
20536     },
20537
20538     // private
20539     activate : function(){
20540         if(this.disabled){
20541             return false;
20542         }
20543         this.component.focus();
20544         this.fireEvent("activate", this);
20545         return true;
20546     },
20547
20548     // private
20549     deactivate : function(){
20550         this.fireEvent("deactivate", this);
20551     },
20552
20553     // private
20554     disable : function(){
20555         this.component.disable();
20556         Roo.menu.Adapter.superclass.disable.call(this);
20557     },
20558
20559     // private
20560     enable : function(){
20561         this.component.enable();
20562         Roo.menu.Adapter.superclass.enable.call(this);
20563     }
20564 });/*
20565  * Based on:
20566  * Ext JS Library 1.1.1
20567  * Copyright(c) 2006-2007, Ext JS, LLC.
20568  *
20569  * Originally Released Under LGPL - original licence link has changed is not relivant.
20570  *
20571  * Fork - LGPL
20572  * <script type="text/javascript">
20573  */
20574
20575 /**
20576  * @class Roo.menu.TextItem
20577  * @extends Roo.menu.BaseItem
20578  * Adds a static text string to a menu, usually used as either a heading or group separator.
20579  * Note: old style constructor with text is still supported.
20580  * 
20581  * @constructor
20582  * Creates a new TextItem
20583  * @param {Object} cfg Configuration
20584  */
20585 Roo.menu.TextItem = function(cfg){
20586     if (typeof(cfg) == 'string') {
20587         this.text = cfg;
20588     } else {
20589         Roo.apply(this,cfg);
20590     }
20591     
20592     Roo.menu.TextItem.superclass.constructor.call(this);
20593 };
20594
20595 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20596     /**
20597      * @cfg {Boolean} text Text to show on item.
20598      */
20599     text : '',
20600     
20601     /**
20602      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20603      */
20604     hideOnClick : false,
20605     /**
20606      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20607      */
20608     itemCls : "x-menu-text",
20609
20610     // private
20611     onRender : function(){
20612         var s = document.createElement("span");
20613         s.className = this.itemCls;
20614         s.innerHTML = this.text;
20615         this.el = s;
20616         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20617     }
20618 });/*
20619  * Based on:
20620  * Ext JS Library 1.1.1
20621  * Copyright(c) 2006-2007, Ext JS, LLC.
20622  *
20623  * Originally Released Under LGPL - original licence link has changed is not relivant.
20624  *
20625  * Fork - LGPL
20626  * <script type="text/javascript">
20627  */
20628
20629 /**
20630  * @class Roo.menu.Separator
20631  * @extends Roo.menu.BaseItem
20632  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20633  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20634  * @constructor
20635  * @param {Object} config Configuration options
20636  */
20637 Roo.menu.Separator = function(config){
20638     Roo.menu.Separator.superclass.constructor.call(this, config);
20639 };
20640
20641 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20642     /**
20643      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20644      */
20645     itemCls : "x-menu-sep",
20646     /**
20647      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20648      */
20649     hideOnClick : false,
20650
20651     // private
20652     onRender : function(li){
20653         var s = document.createElement("span");
20654         s.className = this.itemCls;
20655         s.innerHTML = "&#160;";
20656         this.el = s;
20657         li.addClass("x-menu-sep-li");
20658         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20659     }
20660 });/*
20661  * Based on:
20662  * Ext JS Library 1.1.1
20663  * Copyright(c) 2006-2007, Ext JS, LLC.
20664  *
20665  * Originally Released Under LGPL - original licence link has changed is not relivant.
20666  *
20667  * Fork - LGPL
20668  * <script type="text/javascript">
20669  */
20670 /**
20671  * @class Roo.menu.Item
20672  * @extends Roo.menu.BaseItem
20673  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20674  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20675  * activation and click handling.
20676  * @constructor
20677  * Creates a new Item
20678  * @param {Object} config Configuration options
20679  */
20680 Roo.menu.Item = function(config){
20681     Roo.menu.Item.superclass.constructor.call(this, config);
20682     if(this.menu){
20683         this.menu = Roo.menu.MenuMgr.get(this.menu);
20684     }
20685 };
20686 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20687     
20688     /**
20689      * @cfg {String} text
20690      * The text to show on the menu item.
20691      */
20692     text: '',
20693      /**
20694      * @cfg {String} HTML to render in menu
20695      * The text to show on the menu item (HTML version).
20696      */
20697     html: '',
20698     /**
20699      * @cfg {String} icon
20700      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20701      */
20702     icon: undefined,
20703     /**
20704      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20705      */
20706     itemCls : "x-menu-item",
20707     /**
20708      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20709      */
20710     canActivate : true,
20711     /**
20712      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20713      */
20714     showDelay: 200,
20715     // doc'd in BaseItem
20716     hideDelay: 200,
20717
20718     // private
20719     ctype: "Roo.menu.Item",
20720     
20721     // private
20722     onRender : function(container, position){
20723         var el = document.createElement("a");
20724         el.hideFocus = true;
20725         el.unselectable = "on";
20726         el.href = this.href || "#";
20727         if(this.hrefTarget){
20728             el.target = this.hrefTarget;
20729         }
20730         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20731         
20732         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20733         
20734         el.innerHTML = String.format(
20735                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20736                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20737         this.el = el;
20738         Roo.menu.Item.superclass.onRender.call(this, container, position);
20739     },
20740
20741     /**
20742      * Sets the text to display in this menu item
20743      * @param {String} text The text to display
20744      * @param {Boolean} isHTML true to indicate text is pure html.
20745      */
20746     setText : function(text, isHTML){
20747         if (isHTML) {
20748             this.html = text;
20749         } else {
20750             this.text = text;
20751             this.html = '';
20752         }
20753         if(this.rendered){
20754             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20755      
20756             this.el.update(String.format(
20757                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20758                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20759             this.parentMenu.autoWidth();
20760         }
20761     },
20762
20763     // private
20764     handleClick : function(e){
20765         if(!this.href){ // if no link defined, stop the event automatically
20766             e.stopEvent();
20767         }
20768         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20769     },
20770
20771     // private
20772     activate : function(autoExpand){
20773         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20774             this.focus();
20775             if(autoExpand){
20776                 this.expandMenu();
20777             }
20778         }
20779         return true;
20780     },
20781
20782     // private
20783     shouldDeactivate : function(e){
20784         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20785             if(this.menu && this.menu.isVisible()){
20786                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20787             }
20788             return true;
20789         }
20790         return false;
20791     },
20792
20793     // private
20794     deactivate : function(){
20795         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20796         this.hideMenu();
20797     },
20798
20799     // private
20800     expandMenu : function(autoActivate){
20801         if(!this.disabled && this.menu){
20802             clearTimeout(this.hideTimer);
20803             delete this.hideTimer;
20804             if(!this.menu.isVisible() && !this.showTimer){
20805                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20806             }else if (this.menu.isVisible() && autoActivate){
20807                 this.menu.tryActivate(0, 1);
20808             }
20809         }
20810     },
20811
20812     // private
20813     deferExpand : function(autoActivate){
20814         delete this.showTimer;
20815         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20816         if(autoActivate){
20817             this.menu.tryActivate(0, 1);
20818         }
20819     },
20820
20821     // private
20822     hideMenu : function(){
20823         clearTimeout(this.showTimer);
20824         delete this.showTimer;
20825         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20826             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20827         }
20828     },
20829
20830     // private
20831     deferHide : function(){
20832         delete this.hideTimer;
20833         this.menu.hide();
20834     }
20835 });/*
20836  * Based on:
20837  * Ext JS Library 1.1.1
20838  * Copyright(c) 2006-2007, Ext JS, LLC.
20839  *
20840  * Originally Released Under LGPL - original licence link has changed is not relivant.
20841  *
20842  * Fork - LGPL
20843  * <script type="text/javascript">
20844  */
20845  
20846 /**
20847  * @class Roo.menu.CheckItem
20848  * @extends Roo.menu.Item
20849  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20850  * @constructor
20851  * Creates a new CheckItem
20852  * @param {Object} config Configuration options
20853  */
20854 Roo.menu.CheckItem = function(config){
20855     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20856     this.addEvents({
20857         /**
20858          * @event beforecheckchange
20859          * Fires before the checked value is set, providing an opportunity to cancel if needed
20860          * @param {Roo.menu.CheckItem} this
20861          * @param {Boolean} checked The new checked value that will be set
20862          */
20863         "beforecheckchange" : true,
20864         /**
20865          * @event checkchange
20866          * Fires after the checked value has been set
20867          * @param {Roo.menu.CheckItem} this
20868          * @param {Boolean} checked The checked value that was set
20869          */
20870         "checkchange" : true
20871     });
20872     if(this.checkHandler){
20873         this.on('checkchange', this.checkHandler, this.scope);
20874     }
20875 };
20876 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20877     /**
20878      * @cfg {String} group
20879      * All check items with the same group name will automatically be grouped into a single-select
20880      * radio button group (defaults to '')
20881      */
20882     /**
20883      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20884      */
20885     itemCls : "x-menu-item x-menu-check-item",
20886     /**
20887      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20888      */
20889     groupClass : "x-menu-group-item",
20890
20891     /**
20892      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20893      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20894      * initialized with checked = true will be rendered as checked.
20895      */
20896     checked: false,
20897
20898     // private
20899     ctype: "Roo.menu.CheckItem",
20900
20901     // private
20902     onRender : function(c){
20903         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20904         if(this.group){
20905             this.el.addClass(this.groupClass);
20906         }
20907         Roo.menu.MenuMgr.registerCheckable(this);
20908         if(this.checked){
20909             this.checked = false;
20910             this.setChecked(true, true);
20911         }
20912     },
20913
20914     // private
20915     destroy : function(){
20916         if(this.rendered){
20917             Roo.menu.MenuMgr.unregisterCheckable(this);
20918         }
20919         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20920     },
20921
20922     /**
20923      * Set the checked state of this item
20924      * @param {Boolean} checked The new checked value
20925      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20926      */
20927     setChecked : function(state, suppressEvent){
20928         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20929             if(this.container){
20930                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20931             }
20932             this.checked = state;
20933             if(suppressEvent !== true){
20934                 this.fireEvent("checkchange", this, state);
20935             }
20936         }
20937     },
20938
20939     // private
20940     handleClick : function(e){
20941        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20942            this.setChecked(!this.checked);
20943        }
20944        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20945     }
20946 });/*
20947  * Based on:
20948  * Ext JS Library 1.1.1
20949  * Copyright(c) 2006-2007, Ext JS, LLC.
20950  *
20951  * Originally Released Under LGPL - original licence link has changed is not relivant.
20952  *
20953  * Fork - LGPL
20954  * <script type="text/javascript">
20955  */
20956  
20957 /**
20958  * @class Roo.menu.DateItem
20959  * @extends Roo.menu.Adapter
20960  * A menu item that wraps the {@link Roo.DatPicker} component.
20961  * @constructor
20962  * Creates a new DateItem
20963  * @param {Object} config Configuration options
20964  */
20965 Roo.menu.DateItem = function(config){
20966     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20967     /** The Roo.DatePicker object @type Roo.DatePicker */
20968     this.picker = this.component;
20969     this.addEvents({select: true});
20970     
20971     this.picker.on("render", function(picker){
20972         picker.getEl().swallowEvent("click");
20973         picker.container.addClass("x-menu-date-item");
20974     });
20975
20976     this.picker.on("select", this.onSelect, this);
20977 };
20978
20979 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
20980     // private
20981     onSelect : function(picker, date){
20982         this.fireEvent("select", this, date, picker);
20983         Roo.menu.DateItem.superclass.handleClick.call(this);
20984     }
20985 });/*
20986  * Based on:
20987  * Ext JS Library 1.1.1
20988  * Copyright(c) 2006-2007, Ext JS, LLC.
20989  *
20990  * Originally Released Under LGPL - original licence link has changed is not relivant.
20991  *
20992  * Fork - LGPL
20993  * <script type="text/javascript">
20994  */
20995  
20996 /**
20997  * @class Roo.menu.ColorItem
20998  * @extends Roo.menu.Adapter
20999  * A menu item that wraps the {@link Roo.ColorPalette} component.
21000  * @constructor
21001  * Creates a new ColorItem
21002  * @param {Object} config Configuration options
21003  */
21004 Roo.menu.ColorItem = function(config){
21005     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21006     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21007     this.palette = this.component;
21008     this.relayEvents(this.palette, ["select"]);
21009     if(this.selectHandler){
21010         this.on('select', this.selectHandler, this.scope);
21011     }
21012 };
21013 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21014  * Based on:
21015  * Ext JS Library 1.1.1
21016  * Copyright(c) 2006-2007, Ext JS, LLC.
21017  *
21018  * Originally Released Under LGPL - original licence link has changed is not relivant.
21019  *
21020  * Fork - LGPL
21021  * <script type="text/javascript">
21022  */
21023  
21024
21025 /**
21026  * @class Roo.menu.DateMenu
21027  * @extends Roo.menu.Menu
21028  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21029  * @constructor
21030  * Creates a new DateMenu
21031  * @param {Object} config Configuration options
21032  */
21033 Roo.menu.DateMenu = function(config){
21034     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21035     this.plain = true;
21036     var di = new Roo.menu.DateItem(config);
21037     this.add(di);
21038     /**
21039      * The {@link Roo.DatePicker} instance for this DateMenu
21040      * @type DatePicker
21041      */
21042     this.picker = di.picker;
21043     /**
21044      * @event select
21045      * @param {DatePicker} picker
21046      * @param {Date} date
21047      */
21048     this.relayEvents(di, ["select"]);
21049
21050     this.on('beforeshow', function(){
21051         if(this.picker){
21052             this.picker.hideMonthPicker(true);
21053         }
21054     }, this);
21055 };
21056 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21057     cls:'x-date-menu'
21058 });/*
21059  * Based on:
21060  * Ext JS Library 1.1.1
21061  * Copyright(c) 2006-2007, Ext JS, LLC.
21062  *
21063  * Originally Released Under LGPL - original licence link has changed is not relivant.
21064  *
21065  * Fork - LGPL
21066  * <script type="text/javascript">
21067  */
21068  
21069
21070 /**
21071  * @class Roo.menu.ColorMenu
21072  * @extends Roo.menu.Menu
21073  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21074  * @constructor
21075  * Creates a new ColorMenu
21076  * @param {Object} config Configuration options
21077  */
21078 Roo.menu.ColorMenu = function(config){
21079     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21080     this.plain = true;
21081     var ci = new Roo.menu.ColorItem(config);
21082     this.add(ci);
21083     /**
21084      * The {@link Roo.ColorPalette} instance for this ColorMenu
21085      * @type ColorPalette
21086      */
21087     this.palette = ci.palette;
21088     /**
21089      * @event select
21090      * @param {ColorPalette} palette
21091      * @param {String} color
21092      */
21093     this.relayEvents(ci, ["select"]);
21094 };
21095 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21096  * Based on:
21097  * Ext JS Library 1.1.1
21098  * Copyright(c) 2006-2007, Ext JS, LLC.
21099  *
21100  * Originally Released Under LGPL - original licence link has changed is not relivant.
21101  *
21102  * Fork - LGPL
21103  * <script type="text/javascript">
21104  */
21105  
21106 /**
21107  * @class Roo.form.Field
21108  * @extends Roo.BoxComponent
21109  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21110  * @constructor
21111  * Creates a new Field
21112  * @param {Object} config Configuration options
21113  */
21114 Roo.form.Field = function(config){
21115     Roo.form.Field.superclass.constructor.call(this, config);
21116 };
21117
21118 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21119     /**
21120      * @cfg {String} fieldLabel Label to use when rendering a form.
21121      */
21122        /**
21123      * @cfg {String} qtip Mouse over tip
21124      */
21125      
21126     /**
21127      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21128      */
21129     invalidClass : "x-form-invalid",
21130     /**
21131      * @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")
21132      */
21133     invalidText : "The value in this field is invalid",
21134     /**
21135      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21136      */
21137     focusClass : "x-form-focus",
21138     /**
21139      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21140       automatic validation (defaults to "keyup").
21141      */
21142     validationEvent : "keyup",
21143     /**
21144      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21145      */
21146     validateOnBlur : true,
21147     /**
21148      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21149      */
21150     validationDelay : 250,
21151     /**
21152      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21153      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21154      */
21155     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21156     /**
21157      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21158      */
21159     fieldClass : "x-form-field",
21160     /**
21161      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21162      *<pre>
21163 Value         Description
21164 -----------   ----------------------------------------------------------------------
21165 qtip          Display a quick tip when the user hovers over the field
21166 title         Display a default browser title attribute popup
21167 under         Add a block div beneath the field containing the error text
21168 side          Add an error icon to the right of the field with a popup on hover
21169 [element id]  Add the error text directly to the innerHTML of the specified element
21170 </pre>
21171      */
21172     msgTarget : 'qtip',
21173     /**
21174      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21175      */
21176     msgFx : 'normal',
21177
21178     /**
21179      * @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.
21180      */
21181     readOnly : false,
21182
21183     /**
21184      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21185      */
21186     disabled : false,
21187
21188     /**
21189      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21190      */
21191     inputType : undefined,
21192     
21193     /**
21194      * @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).
21195          */
21196         tabIndex : undefined,
21197         
21198     // private
21199     isFormField : true,
21200
21201     // private
21202     hasFocus : false,
21203     /**
21204      * @property {Roo.Element} fieldEl
21205      * Element Containing the rendered Field (with label etc.)
21206      */
21207     /**
21208      * @cfg {Mixed} value A value to initialize this field with.
21209      */
21210     value : undefined,
21211
21212     /**
21213      * @cfg {String} name The field's HTML name attribute.
21214      */
21215     /**
21216      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21217      */
21218
21219         // private ??
21220         initComponent : function(){
21221         Roo.form.Field.superclass.initComponent.call(this);
21222         this.addEvents({
21223             /**
21224              * @event focus
21225              * Fires when this field receives input focus.
21226              * @param {Roo.form.Field} this
21227              */
21228             focus : true,
21229             /**
21230              * @event blur
21231              * Fires when this field loses input focus.
21232              * @param {Roo.form.Field} this
21233              */
21234             blur : true,
21235             /**
21236              * @event specialkey
21237              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21238              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21239              * @param {Roo.form.Field} this
21240              * @param {Roo.EventObject} e The event object
21241              */
21242             specialkey : true,
21243             /**
21244              * @event change
21245              * Fires just before the field blurs if the field value has changed.
21246              * @param {Roo.form.Field} this
21247              * @param {Mixed} newValue The new value
21248              * @param {Mixed} oldValue The original value
21249              */
21250             change : true,
21251             /**
21252              * @event invalid
21253              * Fires after the field has been marked as invalid.
21254              * @param {Roo.form.Field} this
21255              * @param {String} msg The validation message
21256              */
21257             invalid : true,
21258             /**
21259              * @event valid
21260              * Fires after the field has been validated with no errors.
21261              * @param {Roo.form.Field} this
21262              */
21263             valid : true,
21264              /**
21265              * @event keyup
21266              * Fires after the key up
21267              * @param {Roo.form.Field} this
21268              * @param {Roo.EventObject}  e The event Object
21269              */
21270             keyup : true
21271         });
21272     },
21273
21274     /**
21275      * Returns the name attribute of the field if available
21276      * @return {String} name The field name
21277      */
21278     getName: function(){
21279          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21280     },
21281
21282     // private
21283     onRender : function(ct, position){
21284         Roo.form.Field.superclass.onRender.call(this, ct, position);
21285         if(!this.el){
21286             var cfg = this.getAutoCreate();
21287             if(!cfg.name){
21288                 cfg.name = this.name || this.id;
21289             }
21290             if(this.inputType){
21291                 cfg.type = this.inputType;
21292             }
21293             this.el = ct.createChild(cfg, position);
21294         }
21295         var type = this.el.dom.type;
21296         if(type){
21297             if(type == 'password'){
21298                 type = 'text';
21299             }
21300             this.el.addClass('x-form-'+type);
21301         }
21302         if(this.readOnly){
21303             this.el.dom.readOnly = true;
21304         }
21305         if(this.tabIndex !== undefined){
21306             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21307         }
21308
21309         this.el.addClass([this.fieldClass, this.cls]);
21310         this.initValue();
21311     },
21312
21313     /**
21314      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21315      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21316      * @return {Roo.form.Field} this
21317      */
21318     applyTo : function(target){
21319         this.allowDomMove = false;
21320         this.el = Roo.get(target);
21321         this.render(this.el.dom.parentNode);
21322         return this;
21323     },
21324
21325     // private
21326     initValue : function(){
21327         if(this.value !== undefined){
21328             this.setValue(this.value);
21329         }else if(this.el.dom.value.length > 0){
21330             this.setValue(this.el.dom.value);
21331         }
21332     },
21333
21334     /**
21335      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21336      */
21337     isDirty : function() {
21338         if(this.disabled) {
21339             return false;
21340         }
21341         return String(this.getValue()) !== String(this.originalValue);
21342     },
21343
21344     // private
21345     afterRender : function(){
21346         Roo.form.Field.superclass.afterRender.call(this);
21347         this.initEvents();
21348     },
21349
21350     // private
21351     fireKey : function(e){
21352         //Roo.log('field ' + e.getKey());
21353         if(e.isNavKeyPress()){
21354             this.fireEvent("specialkey", this, e);
21355         }
21356     },
21357
21358     /**
21359      * Resets the current field value to the originally loaded value and clears any validation messages
21360      */
21361     reset : function(){
21362         this.setValue(this.originalValue);
21363         this.clearInvalid();
21364     },
21365
21366     // private
21367     initEvents : function(){
21368         // safari killled keypress - so keydown is now used..
21369         this.el.on("keydown" , this.fireKey,  this);
21370         this.el.on("focus", this.onFocus,  this);
21371         this.el.on("blur", this.onBlur,  this);
21372         this.el.relayEvent('keyup', this);
21373
21374         // reference to original value for reset
21375         this.originalValue = this.getValue();
21376     },
21377
21378     // private
21379     onFocus : function(){
21380         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21381             this.el.addClass(this.focusClass);
21382         }
21383         if(!this.hasFocus){
21384             this.hasFocus = true;
21385             this.startValue = this.getValue();
21386             this.fireEvent("focus", this);
21387         }
21388     },
21389
21390     beforeBlur : Roo.emptyFn,
21391
21392     // private
21393     onBlur : function(){
21394         this.beforeBlur();
21395         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21396             this.el.removeClass(this.focusClass);
21397         }
21398         this.hasFocus = false;
21399         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21400             this.validate();
21401         }
21402         var v = this.getValue();
21403         if(String(v) !== String(this.startValue)){
21404             this.fireEvent('change', this, v, this.startValue);
21405         }
21406         this.fireEvent("blur", this);
21407     },
21408
21409     /**
21410      * Returns whether or not the field value is currently valid
21411      * @param {Boolean} preventMark True to disable marking the field invalid
21412      * @return {Boolean} True if the value is valid, else false
21413      */
21414     isValid : function(preventMark){
21415         if(this.disabled){
21416             return true;
21417         }
21418         var restore = this.preventMark;
21419         this.preventMark = preventMark === true;
21420         var v = this.validateValue(this.processValue(this.getRawValue()));
21421         this.preventMark = restore;
21422         return v;
21423     },
21424
21425     /**
21426      * Validates the field value
21427      * @return {Boolean} True if the value is valid, else false
21428      */
21429     validate : function(){
21430         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21431             this.clearInvalid();
21432             return true;
21433         }
21434         return false;
21435     },
21436
21437     processValue : function(value){
21438         return value;
21439     },
21440
21441     // private
21442     // Subclasses should provide the validation implementation by overriding this
21443     validateValue : function(value){
21444         return true;
21445     },
21446
21447     /**
21448      * Mark this field as invalid
21449      * @param {String} msg The validation message
21450      */
21451     markInvalid : function(msg){
21452         if(!this.rendered || this.preventMark){ // not rendered
21453             return;
21454         }
21455         this.el.addClass(this.invalidClass);
21456         msg = msg || this.invalidText;
21457         switch(this.msgTarget){
21458             case 'qtip':
21459                 this.el.dom.qtip = msg;
21460                 this.el.dom.qclass = 'x-form-invalid-tip';
21461                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21462                     Roo.QuickTips.enable();
21463                 }
21464                 break;
21465             case 'title':
21466                 this.el.dom.title = msg;
21467                 break;
21468             case 'under':
21469                 if(!this.errorEl){
21470                     var elp = this.el.findParent('.x-form-element', 5, true);
21471                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21472                     this.errorEl.setWidth(elp.getWidth(true)-20);
21473                 }
21474                 this.errorEl.update(msg);
21475                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21476                 break;
21477             case 'side':
21478                 if(!this.errorIcon){
21479                     var elp = this.el.findParent('.x-form-element', 5, true);
21480                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21481                 }
21482                 this.alignErrorIcon();
21483                 this.errorIcon.dom.qtip = msg;
21484                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21485                 this.errorIcon.show();
21486                 this.on('resize', this.alignErrorIcon, this);
21487                 break;
21488             default:
21489                 var t = Roo.getDom(this.msgTarget);
21490                 t.innerHTML = msg;
21491                 t.style.display = this.msgDisplay;
21492                 break;
21493         }
21494         this.fireEvent('invalid', this, msg);
21495     },
21496
21497     // private
21498     alignErrorIcon : function(){
21499         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21500     },
21501
21502     /**
21503      * Clear any invalid styles/messages for this field
21504      */
21505     clearInvalid : function(){
21506         if(!this.rendered || this.preventMark){ // not rendered
21507             return;
21508         }
21509         this.el.removeClass(this.invalidClass);
21510         switch(this.msgTarget){
21511             case 'qtip':
21512                 this.el.dom.qtip = '';
21513                 break;
21514             case 'title':
21515                 this.el.dom.title = '';
21516                 break;
21517             case 'under':
21518                 if(this.errorEl){
21519                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21520                 }
21521                 break;
21522             case 'side':
21523                 if(this.errorIcon){
21524                     this.errorIcon.dom.qtip = '';
21525                     this.errorIcon.hide();
21526                     this.un('resize', this.alignErrorIcon, this);
21527                 }
21528                 break;
21529             default:
21530                 var t = Roo.getDom(this.msgTarget);
21531                 t.innerHTML = '';
21532                 t.style.display = 'none';
21533                 break;
21534         }
21535         this.fireEvent('valid', this);
21536     },
21537
21538     /**
21539      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21540      * @return {Mixed} value The field value
21541      */
21542     getRawValue : function(){
21543         var v = this.el.getValue();
21544         if(v === this.emptyText){
21545             v = '';
21546         }
21547         return v;
21548     },
21549
21550     /**
21551      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21552      * @return {Mixed} value The field value
21553      */
21554     getValue : function(){
21555         var v = this.el.getValue();
21556         if(v === this.emptyText || v === undefined){
21557             v = '';
21558         }
21559         return v;
21560     },
21561
21562     /**
21563      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21564      * @param {Mixed} value The value to set
21565      */
21566     setRawValue : function(v){
21567         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21568     },
21569
21570     /**
21571      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21572      * @param {Mixed} value The value to set
21573      */
21574     setValue : function(v){
21575         this.value = v;
21576         if(this.rendered){
21577             this.el.dom.value = (v === null || v === undefined ? '' : v);
21578              this.validate();
21579         }
21580     },
21581
21582     adjustSize : function(w, h){
21583         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21584         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21585         return s;
21586     },
21587
21588     adjustWidth : function(tag, w){
21589         tag = tag.toLowerCase();
21590         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21591             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21592                 if(tag == 'input'){
21593                     return w + 2;
21594                 }
21595                 if(tag = 'textarea'){
21596                     return w-2;
21597                 }
21598             }else if(Roo.isOpera){
21599                 if(tag == 'input'){
21600                     return w + 2;
21601                 }
21602                 if(tag = 'textarea'){
21603                     return w-2;
21604                 }
21605             }
21606         }
21607         return w;
21608     }
21609 });
21610
21611
21612 // anything other than normal should be considered experimental
21613 Roo.form.Field.msgFx = {
21614     normal : {
21615         show: function(msgEl, f){
21616             msgEl.setDisplayed('block');
21617         },
21618
21619         hide : function(msgEl, f){
21620             msgEl.setDisplayed(false).update('');
21621         }
21622     },
21623
21624     slide : {
21625         show: function(msgEl, f){
21626             msgEl.slideIn('t', {stopFx:true});
21627         },
21628
21629         hide : function(msgEl, f){
21630             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21631         }
21632     },
21633
21634     slideRight : {
21635         show: function(msgEl, f){
21636             msgEl.fixDisplay();
21637             msgEl.alignTo(f.el, 'tl-tr');
21638             msgEl.slideIn('l', {stopFx:true});
21639         },
21640
21641         hide : function(msgEl, f){
21642             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21643         }
21644     }
21645 };/*
21646  * Based on:
21647  * Ext JS Library 1.1.1
21648  * Copyright(c) 2006-2007, Ext JS, LLC.
21649  *
21650  * Originally Released Under LGPL - original licence link has changed is not relivant.
21651  *
21652  * Fork - LGPL
21653  * <script type="text/javascript">
21654  */
21655  
21656
21657 /**
21658  * @class Roo.form.TextField
21659  * @extends Roo.form.Field
21660  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21661  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21662  * @constructor
21663  * Creates a new TextField
21664  * @param {Object} config Configuration options
21665  */
21666 Roo.form.TextField = function(config){
21667     Roo.form.TextField.superclass.constructor.call(this, config);
21668     this.addEvents({
21669         /**
21670          * @event autosize
21671          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21672          * according to the default logic, but this event provides a hook for the developer to apply additional
21673          * logic at runtime to resize the field if needed.
21674              * @param {Roo.form.Field} this This text field
21675              * @param {Number} width The new field width
21676              */
21677         autosize : true
21678     });
21679 };
21680
21681 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21682     /**
21683      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21684      */
21685     grow : false,
21686     /**
21687      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21688      */
21689     growMin : 30,
21690     /**
21691      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21692      */
21693     growMax : 800,
21694     /**
21695      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21696      */
21697     vtype : null,
21698     /**
21699      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21700      */
21701     maskRe : null,
21702     /**
21703      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21704      */
21705     disableKeyFilter : false,
21706     /**
21707      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21708      */
21709     allowBlank : true,
21710     /**
21711      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21712      */
21713     minLength : 0,
21714     /**
21715      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21716      */
21717     maxLength : Number.MAX_VALUE,
21718     /**
21719      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21720      */
21721     minLengthText : "The minimum length for this field is {0}",
21722     /**
21723      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21724      */
21725     maxLengthText : "The maximum length for this field is {0}",
21726     /**
21727      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21728      */
21729     selectOnFocus : false,
21730     /**
21731      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21732      */
21733     blankText : "This field is required",
21734     /**
21735      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21736      * If available, this function will be called only after the basic validators all return true, and will be passed the
21737      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21738      */
21739     validator : null,
21740     /**
21741      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21742      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21743      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21744      */
21745     regex : null,
21746     /**
21747      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21748      */
21749     regexText : "",
21750     /**
21751      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21752      */
21753     emptyText : null,
21754     /**
21755      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21756      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21757      */
21758     emptyClass : 'x-form-empty-field',
21759
21760     // private
21761     initEvents : function(){
21762         Roo.form.TextField.superclass.initEvents.call(this);
21763         if(this.validationEvent == 'keyup'){
21764             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21765             this.el.on('keyup', this.filterValidation, this);
21766         }
21767         else if(this.validationEvent !== false){
21768             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21769         }
21770         if(this.selectOnFocus || this.emptyText){
21771             this.on("focus", this.preFocus, this);
21772             if(this.emptyText){
21773                 this.on('blur', this.postBlur, this);
21774                 this.applyEmptyText();
21775             }
21776         }
21777         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21778             this.el.on("keypress", this.filterKeys, this);
21779         }
21780         if(this.grow){
21781             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21782             this.el.on("click", this.autoSize,  this);
21783         }
21784     },
21785
21786     processValue : function(value){
21787         if(this.stripCharsRe){
21788             var newValue = value.replace(this.stripCharsRe, '');
21789             if(newValue !== value){
21790                 this.setRawValue(newValue);
21791                 return newValue;
21792             }
21793         }
21794         return value;
21795     },
21796
21797     filterValidation : function(e){
21798         if(!e.isNavKeyPress()){
21799             this.validationTask.delay(this.validationDelay);
21800         }
21801     },
21802
21803     // private
21804     onKeyUp : function(e){
21805         if(!e.isNavKeyPress()){
21806             this.autoSize();
21807         }
21808     },
21809
21810     /**
21811      * Resets the current field value to the originally-loaded value and clears any validation messages.
21812      * Also adds emptyText and emptyClass if the original value was blank.
21813      */
21814     reset : function(){
21815         Roo.form.TextField.superclass.reset.call(this);
21816         this.applyEmptyText();
21817     },
21818
21819     applyEmptyText : function(){
21820         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21821             this.setRawValue(this.emptyText);
21822             this.el.addClass(this.emptyClass);
21823         }
21824     },
21825
21826     // private
21827     preFocus : function(){
21828         if(this.emptyText){
21829             if(this.el.dom.value == this.emptyText){
21830                 this.setRawValue('');
21831             }
21832             this.el.removeClass(this.emptyClass);
21833         }
21834         if(this.selectOnFocus){
21835             this.el.dom.select();
21836         }
21837     },
21838
21839     // private
21840     postBlur : function(){
21841         this.applyEmptyText();
21842     },
21843
21844     // private
21845     filterKeys : function(e){
21846         var k = e.getKey();
21847         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21848             return;
21849         }
21850         var c = e.getCharCode(), cc = String.fromCharCode(c);
21851         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21852             return;
21853         }
21854         if(!this.maskRe.test(cc)){
21855             e.stopEvent();
21856         }
21857     },
21858
21859     setValue : function(v){
21860         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21861             this.el.removeClass(this.emptyClass);
21862         }
21863         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21864         this.applyEmptyText();
21865         this.autoSize();
21866     },
21867
21868     /**
21869      * Validates a value according to the field's validation rules and marks the field as invalid
21870      * if the validation fails
21871      * @param {Mixed} value The value to validate
21872      * @return {Boolean} True if the value is valid, else false
21873      */
21874     validateValue : function(value){
21875         if(value.length < 1 || value === this.emptyText){ // if it's blank
21876              if(this.allowBlank){
21877                 this.clearInvalid();
21878                 return true;
21879              }else{
21880                 this.markInvalid(this.blankText);
21881                 return false;
21882              }
21883         }
21884         if(value.length < this.minLength){
21885             this.markInvalid(String.format(this.minLengthText, this.minLength));
21886             return false;
21887         }
21888         if(value.length > this.maxLength){
21889             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21890             return false;
21891         }
21892         if(this.vtype){
21893             var vt = Roo.form.VTypes;
21894             if(!vt[this.vtype](value, this)){
21895                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21896                 return false;
21897             }
21898         }
21899         if(typeof this.validator == "function"){
21900             var msg = this.validator(value);
21901             if(msg !== true){
21902                 this.markInvalid(msg);
21903                 return false;
21904             }
21905         }
21906         if(this.regex && !this.regex.test(value)){
21907             this.markInvalid(this.regexText);
21908             return false;
21909         }
21910         return true;
21911     },
21912
21913     /**
21914      * Selects text in this field
21915      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21916      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21917      */
21918     selectText : function(start, end){
21919         var v = this.getRawValue();
21920         if(v.length > 0){
21921             start = start === undefined ? 0 : start;
21922             end = end === undefined ? v.length : end;
21923             var d = this.el.dom;
21924             if(d.setSelectionRange){
21925                 d.setSelectionRange(start, end);
21926             }else if(d.createTextRange){
21927                 var range = d.createTextRange();
21928                 range.moveStart("character", start);
21929                 range.moveEnd("character", v.length-end);
21930                 range.select();
21931             }
21932         }
21933     },
21934
21935     /**
21936      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21937      * This only takes effect if grow = true, and fires the autosize event.
21938      */
21939     autoSize : function(){
21940         if(!this.grow || !this.rendered){
21941             return;
21942         }
21943         if(!this.metrics){
21944             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21945         }
21946         var el = this.el;
21947         var v = el.dom.value;
21948         var d = document.createElement('div');
21949         d.appendChild(document.createTextNode(v));
21950         v = d.innerHTML;
21951         d = null;
21952         v += "&#160;";
21953         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21954         this.el.setWidth(w);
21955         this.fireEvent("autosize", this, w);
21956     }
21957 });/*
21958  * Based on:
21959  * Ext JS Library 1.1.1
21960  * Copyright(c) 2006-2007, Ext JS, LLC.
21961  *
21962  * Originally Released Under LGPL - original licence link has changed is not relivant.
21963  *
21964  * Fork - LGPL
21965  * <script type="text/javascript">
21966  */
21967  
21968 /**
21969  * @class Roo.form.Hidden
21970  * @extends Roo.form.TextField
21971  * Simple Hidden element used on forms 
21972  * 
21973  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21974  * 
21975  * @constructor
21976  * Creates a new Hidden form element.
21977  * @param {Object} config Configuration options
21978  */
21979
21980
21981
21982 // easy hidden field...
21983 Roo.form.Hidden = function(config){
21984     Roo.form.Hidden.superclass.constructor.call(this, config);
21985 };
21986   
21987 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
21988     fieldLabel:      '',
21989     inputType:      'hidden',
21990     width:          50,
21991     allowBlank:     true,
21992     labelSeparator: '',
21993     hidden:         true,
21994     itemCls :       'x-form-item-display-none'
21995
21996
21997 });
21998
21999
22000 /*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010  
22011 /**
22012  * @class Roo.form.TriggerField
22013  * @extends Roo.form.TextField
22014  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22015  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22016  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22017  * for which you can provide a custom implementation.  For example:
22018  * <pre><code>
22019 var trigger = new Roo.form.TriggerField();
22020 trigger.onTriggerClick = myTriggerFn;
22021 trigger.applyTo('my-field');
22022 </code></pre>
22023  *
22024  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22025  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22026  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22027  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22028  * @constructor
22029  * Create a new TriggerField.
22030  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22031  * to the base TextField)
22032  */
22033 Roo.form.TriggerField = function(config){
22034     this.mimicing = false;
22035     Roo.form.TriggerField.superclass.constructor.call(this, config);
22036 };
22037
22038 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22039     /**
22040      * @cfg {String} triggerClass A CSS class to apply to the trigger
22041      */
22042     /**
22043      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22044      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22045      */
22046     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22047     /**
22048      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22049      */
22050     hideTrigger:false,
22051
22052     /** @cfg {Boolean} grow @hide */
22053     /** @cfg {Number} growMin @hide */
22054     /** @cfg {Number} growMax @hide */
22055
22056     /**
22057      * @hide 
22058      * @method
22059      */
22060     autoSize: Roo.emptyFn,
22061     // private
22062     monitorTab : true,
22063     // private
22064     deferHeight : true,
22065
22066     
22067     actionMode : 'wrap',
22068     // private
22069     onResize : function(w, h){
22070         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22071         if(typeof w == 'number'){
22072             var x = w - this.trigger.getWidth();
22073             this.el.setWidth(this.adjustWidth('input', x));
22074             this.trigger.setStyle('left', x+'px');
22075         }
22076     },
22077
22078     // private
22079     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22080
22081     // private
22082     getResizeEl : function(){
22083         return this.wrap;
22084     },
22085
22086     // private
22087     getPositionEl : function(){
22088         return this.wrap;
22089     },
22090
22091     // private
22092     alignErrorIcon : function(){
22093         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22094     },
22095
22096     // private
22097     onRender : function(ct, position){
22098         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22099         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22100         this.trigger = this.wrap.createChild(this.triggerConfig ||
22101                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22102         if(this.hideTrigger){
22103             this.trigger.setDisplayed(false);
22104         }
22105         this.initTrigger();
22106         if(!this.width){
22107             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22108         }
22109     },
22110
22111     // private
22112     initTrigger : function(){
22113         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22114         this.trigger.addClassOnOver('x-form-trigger-over');
22115         this.trigger.addClassOnClick('x-form-trigger-click');
22116     },
22117
22118     // private
22119     onDestroy : function(){
22120         if(this.trigger){
22121             this.trigger.removeAllListeners();
22122             this.trigger.remove();
22123         }
22124         if(this.wrap){
22125             this.wrap.remove();
22126         }
22127         Roo.form.TriggerField.superclass.onDestroy.call(this);
22128     },
22129
22130     // private
22131     onFocus : function(){
22132         Roo.form.TriggerField.superclass.onFocus.call(this);
22133         if(!this.mimicing){
22134             this.wrap.addClass('x-trigger-wrap-focus');
22135             this.mimicing = true;
22136             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22137             if(this.monitorTab){
22138                 this.el.on("keydown", this.checkTab, this);
22139             }
22140         }
22141     },
22142
22143     // private
22144     checkTab : function(e){
22145         if(e.getKey() == e.TAB){
22146             this.triggerBlur();
22147         }
22148     },
22149
22150     // private
22151     onBlur : function(){
22152         // do nothing
22153     },
22154
22155     // private
22156     mimicBlur : function(e, t){
22157         if(!this.wrap.contains(t) && this.validateBlur()){
22158             this.triggerBlur();
22159         }
22160     },
22161
22162     // private
22163     triggerBlur : function(){
22164         this.mimicing = false;
22165         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22166         if(this.monitorTab){
22167             this.el.un("keydown", this.checkTab, this);
22168         }
22169         this.wrap.removeClass('x-trigger-wrap-focus');
22170         Roo.form.TriggerField.superclass.onBlur.call(this);
22171     },
22172
22173     // private
22174     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22175     validateBlur : function(e, t){
22176         return true;
22177     },
22178
22179     // private
22180     onDisable : function(){
22181         Roo.form.TriggerField.superclass.onDisable.call(this);
22182         if(this.wrap){
22183             this.wrap.addClass('x-item-disabled');
22184         }
22185     },
22186
22187     // private
22188     onEnable : function(){
22189         Roo.form.TriggerField.superclass.onEnable.call(this);
22190         if(this.wrap){
22191             this.wrap.removeClass('x-item-disabled');
22192         }
22193     },
22194
22195     // private
22196     onShow : function(){
22197         var ae = this.getActionEl();
22198         
22199         if(ae){
22200             ae.dom.style.display = '';
22201             ae.dom.style.visibility = 'visible';
22202         }
22203     },
22204
22205     // private
22206     
22207     onHide : function(){
22208         var ae = this.getActionEl();
22209         ae.dom.style.display = 'none';
22210     },
22211
22212     /**
22213      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22214      * by an implementing function.
22215      * @method
22216      * @param {EventObject} e
22217      */
22218     onTriggerClick : Roo.emptyFn
22219 });
22220
22221 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22222 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22223 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22224 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22225     initComponent : function(){
22226         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22227
22228         this.triggerConfig = {
22229             tag:'span', cls:'x-form-twin-triggers', cn:[
22230             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22231             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22232         ]};
22233     },
22234
22235     getTrigger : function(index){
22236         return this.triggers[index];
22237     },
22238
22239     initTrigger : function(){
22240         var ts = this.trigger.select('.x-form-trigger', true);
22241         this.wrap.setStyle('overflow', 'hidden');
22242         var triggerField = this;
22243         ts.each(function(t, all, index){
22244             t.hide = function(){
22245                 var w = triggerField.wrap.getWidth();
22246                 this.dom.style.display = 'none';
22247                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22248             };
22249             t.show = function(){
22250                 var w = triggerField.wrap.getWidth();
22251                 this.dom.style.display = '';
22252                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22253             };
22254             var triggerIndex = 'Trigger'+(index+1);
22255
22256             if(this['hide'+triggerIndex]){
22257                 t.dom.style.display = 'none';
22258             }
22259             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22260             t.addClassOnOver('x-form-trigger-over');
22261             t.addClassOnClick('x-form-trigger-click');
22262         }, this);
22263         this.triggers = ts.elements;
22264     },
22265
22266     onTrigger1Click : Roo.emptyFn,
22267     onTrigger2Click : Roo.emptyFn
22268 });/*
22269  * Based on:
22270  * Ext JS Library 1.1.1
22271  * Copyright(c) 2006-2007, Ext JS, LLC.
22272  *
22273  * Originally Released Under LGPL - original licence link has changed is not relivant.
22274  *
22275  * Fork - LGPL
22276  * <script type="text/javascript">
22277  */
22278  
22279 /**
22280  * @class Roo.form.TextArea
22281  * @extends Roo.form.TextField
22282  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22283  * support for auto-sizing.
22284  * @constructor
22285  * Creates a new TextArea
22286  * @param {Object} config Configuration options
22287  */
22288 Roo.form.TextArea = function(config){
22289     Roo.form.TextArea.superclass.constructor.call(this, config);
22290     // these are provided exchanges for backwards compat
22291     // minHeight/maxHeight were replaced by growMin/growMax to be
22292     // compatible with TextField growing config values
22293     if(this.minHeight !== undefined){
22294         this.growMin = this.minHeight;
22295     }
22296     if(this.maxHeight !== undefined){
22297         this.growMax = this.maxHeight;
22298     }
22299 };
22300
22301 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22302     /**
22303      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22304      */
22305     growMin : 60,
22306     /**
22307      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22308      */
22309     growMax: 1000,
22310     /**
22311      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22312      * in the field (equivalent to setting overflow: hidden, defaults to false)
22313      */
22314     preventScrollbars: false,
22315     /**
22316      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22317      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22318      */
22319
22320     // private
22321     onRender : function(ct, position){
22322         if(!this.el){
22323             this.defaultAutoCreate = {
22324                 tag: "textarea",
22325                 style:"width:300px;height:60px;",
22326                 autocomplete: "off"
22327             };
22328         }
22329         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22330         if(this.grow){
22331             this.textSizeEl = Roo.DomHelper.append(document.body, {
22332                 tag: "pre", cls: "x-form-grow-sizer"
22333             });
22334             if(this.preventScrollbars){
22335                 this.el.setStyle("overflow", "hidden");
22336             }
22337             this.el.setHeight(this.growMin);
22338         }
22339     },
22340
22341     onDestroy : function(){
22342         if(this.textSizeEl){
22343             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22344         }
22345         Roo.form.TextArea.superclass.onDestroy.call(this);
22346     },
22347
22348     // private
22349     onKeyUp : function(e){
22350         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22351             this.autoSize();
22352         }
22353     },
22354
22355     /**
22356      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22357      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22358      */
22359     autoSize : function(){
22360         if(!this.grow || !this.textSizeEl){
22361             return;
22362         }
22363         var el = this.el;
22364         var v = el.dom.value;
22365         var ts = this.textSizeEl;
22366
22367         ts.innerHTML = '';
22368         ts.appendChild(document.createTextNode(v));
22369         v = ts.innerHTML;
22370
22371         Roo.fly(ts).setWidth(this.el.getWidth());
22372         if(v.length < 1){
22373             v = "&#160;&#160;";
22374         }else{
22375             if(Roo.isIE){
22376                 v = v.replace(/\n/g, '<p>&#160;</p>');
22377             }
22378             v += "&#160;\n&#160;";
22379         }
22380         ts.innerHTML = v;
22381         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22382         if(h != this.lastHeight){
22383             this.lastHeight = h;
22384             this.el.setHeight(h);
22385             this.fireEvent("autosize", this, h);
22386         }
22387     }
22388 });/*
22389  * Based on:
22390  * Ext JS Library 1.1.1
22391  * Copyright(c) 2006-2007, Ext JS, LLC.
22392  *
22393  * Originally Released Under LGPL - original licence link has changed is not relivant.
22394  *
22395  * Fork - LGPL
22396  * <script type="text/javascript">
22397  */
22398  
22399
22400 /**
22401  * @class Roo.form.NumberField
22402  * @extends Roo.form.TextField
22403  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22404  * @constructor
22405  * Creates a new NumberField
22406  * @param {Object} config Configuration options
22407  */
22408 Roo.form.NumberField = function(config){
22409     Roo.form.NumberField.superclass.constructor.call(this, config);
22410 };
22411
22412 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22413     /**
22414      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22415      */
22416     fieldClass: "x-form-field x-form-num-field",
22417     /**
22418      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22419      */
22420     allowDecimals : true,
22421     /**
22422      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22423      */
22424     decimalSeparator : ".",
22425     /**
22426      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22427      */
22428     decimalPrecision : 2,
22429     /**
22430      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22431      */
22432     allowNegative : true,
22433     /**
22434      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22435      */
22436     minValue : Number.NEGATIVE_INFINITY,
22437     /**
22438      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22439      */
22440     maxValue : Number.MAX_VALUE,
22441     /**
22442      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22443      */
22444     minText : "The minimum value for this field is {0}",
22445     /**
22446      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22447      */
22448     maxText : "The maximum value for this field is {0}",
22449     /**
22450      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22451      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22452      */
22453     nanText : "{0} is not a valid number",
22454
22455     // private
22456     initEvents : function(){
22457         Roo.form.NumberField.superclass.initEvents.call(this);
22458         var allowed = "0123456789";
22459         if(this.allowDecimals){
22460             allowed += this.decimalSeparator;
22461         }
22462         if(this.allowNegative){
22463             allowed += "-";
22464         }
22465         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22466         var keyPress = function(e){
22467             var k = e.getKey();
22468             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22469                 return;
22470             }
22471             var c = e.getCharCode();
22472             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22473                 e.stopEvent();
22474             }
22475         };
22476         this.el.on("keypress", keyPress, this);
22477     },
22478
22479     // private
22480     validateValue : function(value){
22481         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22482             return false;
22483         }
22484         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22485              return true;
22486         }
22487         var num = this.parseValue(value);
22488         if(isNaN(num)){
22489             this.markInvalid(String.format(this.nanText, value));
22490             return false;
22491         }
22492         if(num < this.minValue){
22493             this.markInvalid(String.format(this.minText, this.minValue));
22494             return false;
22495         }
22496         if(num > this.maxValue){
22497             this.markInvalid(String.format(this.maxText, this.maxValue));
22498             return false;
22499         }
22500         return true;
22501     },
22502
22503     getValue : function(){
22504         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22505     },
22506
22507     // private
22508     parseValue : function(value){
22509         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22510         return isNaN(value) ? '' : value;
22511     },
22512
22513     // private
22514     fixPrecision : function(value){
22515         var nan = isNaN(value);
22516         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22517             return nan ? '' : value;
22518         }
22519         return parseFloat(value).toFixed(this.decimalPrecision);
22520     },
22521
22522     setValue : function(v){
22523         v = this.fixPrecision(v);
22524         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22525     },
22526
22527     // private
22528     decimalPrecisionFcn : function(v){
22529         return Math.floor(v);
22530     },
22531
22532     beforeBlur : function(){
22533         var v = this.parseValue(this.getRawValue());
22534         if(v){
22535             this.setValue(v);
22536         }
22537     }
22538 });/*
22539  * Based on:
22540  * Ext JS Library 1.1.1
22541  * Copyright(c) 2006-2007, Ext JS, LLC.
22542  *
22543  * Originally Released Under LGPL - original licence link has changed is not relivant.
22544  *
22545  * Fork - LGPL
22546  * <script type="text/javascript">
22547  */
22548  
22549 /**
22550  * @class Roo.form.DateField
22551  * @extends Roo.form.TriggerField
22552  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22553 * @constructor
22554 * Create a new DateField
22555 * @param {Object} config
22556  */
22557 Roo.form.DateField = function(config){
22558     Roo.form.DateField.superclass.constructor.call(this, config);
22559     
22560       this.addEvents({
22561          
22562         /**
22563          * @event select
22564          * Fires when a date is selected
22565              * @param {Roo.form.DateField} combo This combo box
22566              * @param {Date} date The date selected
22567              */
22568         'select' : true
22569          
22570     });
22571     
22572     
22573     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22574     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22575     this.ddMatch = null;
22576     if(this.disabledDates){
22577         var dd = this.disabledDates;
22578         var re = "(?:";
22579         for(var i = 0; i < dd.length; i++){
22580             re += dd[i];
22581             if(i != dd.length-1) re += "|";
22582         }
22583         this.ddMatch = new RegExp(re + ")");
22584     }
22585 };
22586
22587 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22588     /**
22589      * @cfg {String} format
22590      * The default date format string which can be overriden for localization support.  The format must be
22591      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22592      */
22593     format : "m/d/y",
22594     /**
22595      * @cfg {String} altFormats
22596      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22597      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22598      */
22599     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22600     /**
22601      * @cfg {Array} disabledDays
22602      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22603      */
22604     disabledDays : null,
22605     /**
22606      * @cfg {String} disabledDaysText
22607      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22608      */
22609     disabledDaysText : "Disabled",
22610     /**
22611      * @cfg {Array} disabledDates
22612      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22613      * expression so they are very powerful. Some examples:
22614      * <ul>
22615      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22616      * <li>["03/08", "09/16"] would disable those days for every year</li>
22617      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22618      * <li>["03/../2006"] would disable every day in March 2006</li>
22619      * <li>["^03"] would disable every day in every March</li>
22620      * </ul>
22621      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22622      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22623      */
22624     disabledDates : null,
22625     /**
22626      * @cfg {String} disabledDatesText
22627      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22628      */
22629     disabledDatesText : "Disabled",
22630     /**
22631      * @cfg {Date/String} minValue
22632      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22633      * valid format (defaults to null).
22634      */
22635     minValue : null,
22636     /**
22637      * @cfg {Date/String} maxValue
22638      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22639      * valid format (defaults to null).
22640      */
22641     maxValue : null,
22642     /**
22643      * @cfg {String} minText
22644      * The error text to display when the date in the cell is before minValue (defaults to
22645      * 'The date in this field must be after {minValue}').
22646      */
22647     minText : "The date in this field must be equal to or after {0}",
22648     /**
22649      * @cfg {String} maxText
22650      * The error text to display when the date in the cell is after maxValue (defaults to
22651      * 'The date in this field must be before {maxValue}').
22652      */
22653     maxText : "The date in this field must be equal to or before {0}",
22654     /**
22655      * @cfg {String} invalidText
22656      * The error text to display when the date in the field is invalid (defaults to
22657      * '{value} is not a valid date - it must be in the format {format}').
22658      */
22659     invalidText : "{0} is not a valid date - it must be in the format {1}",
22660     /**
22661      * @cfg {String} triggerClass
22662      * An additional CSS class used to style the trigger button.  The trigger will always get the
22663      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22664      * which displays a calendar icon).
22665      */
22666     triggerClass : 'x-form-date-trigger',
22667     
22668
22669     /**
22670      * @cfg {bool} useIso
22671      * if enabled, then the date field will use a hidden field to store the 
22672      * real value as iso formated date. default (false)
22673      */ 
22674     useIso : false,
22675     /**
22676      * @cfg {String/Object} autoCreate
22677      * A DomHelper element spec, or true for a default element spec (defaults to
22678      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22679      */ 
22680     // private
22681     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22682     
22683     // private
22684     hiddenField: false,
22685     
22686     onRender : function(ct, position)
22687     {
22688         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22689         if (this.useIso) {
22690             this.el.dom.removeAttribute('name'); 
22691             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22692                     'before', true);
22693             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
22694             // prevent input submission
22695             this.hiddenName = this.name;
22696         }
22697             
22698             
22699     },
22700     
22701     // private
22702     validateValue : function(value)
22703     {
22704         value = this.formatDate(value);
22705         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22706             return false;
22707         }
22708         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22709              return true;
22710         }
22711         var svalue = value;
22712         value = this.parseDate(value);
22713         if(!value){
22714             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22715             return false;
22716         }
22717         var time = value.getTime();
22718         if(this.minValue && time < this.minValue.getTime()){
22719             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22720             return false;
22721         }
22722         if(this.maxValue && time > this.maxValue.getTime()){
22723             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22724             return false;
22725         }
22726         if(this.disabledDays){
22727             var day = value.getDay();
22728             for(var i = 0; i < this.disabledDays.length; i++) {
22729                 if(day === this.disabledDays[i]){
22730                     this.markInvalid(this.disabledDaysText);
22731                     return false;
22732                 }
22733             }
22734         }
22735         var fvalue = this.formatDate(value);
22736         if(this.ddMatch && this.ddMatch.test(fvalue)){
22737             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22738             return false;
22739         }
22740         return true;
22741     },
22742
22743     // private
22744     // Provides logic to override the default TriggerField.validateBlur which just returns true
22745     validateBlur : function(){
22746         return !this.menu || !this.menu.isVisible();
22747     },
22748
22749     /**
22750      * Returns the current date value of the date field.
22751      * @return {Date} The date value
22752      */
22753     getValue : function(){
22754         
22755         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22756     },
22757
22758     /**
22759      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22760      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22761      * (the default format used is "m/d/y").
22762      * <br />Usage:
22763      * <pre><code>
22764 //All of these calls set the same date value (May 4, 2006)
22765
22766 //Pass a date object:
22767 var dt = new Date('5/4/06');
22768 dateField.setValue(dt);
22769
22770 //Pass a date string (default format):
22771 dateField.setValue('5/4/06');
22772
22773 //Pass a date string (custom format):
22774 dateField.format = 'Y-m-d';
22775 dateField.setValue('2006-5-4');
22776 </code></pre>
22777      * @param {String/Date} date The date or valid date string
22778      */
22779     setValue : function(date){
22780         if (this.hiddenField) {
22781             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22782         }
22783         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22784     },
22785
22786     // private
22787     parseDate : function(value){
22788         if(!value || value instanceof Date){
22789             return value;
22790         }
22791         var v = Date.parseDate(value, this.format);
22792         if(!v && this.altFormats){
22793             if(!this.altFormatsArray){
22794                 this.altFormatsArray = this.altFormats.split("|");
22795             }
22796             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22797                 v = Date.parseDate(value, this.altFormatsArray[i]);
22798             }
22799         }
22800         return v;
22801     },
22802
22803     // private
22804     formatDate : function(date, fmt){
22805         return (!date || !(date instanceof Date)) ?
22806                date : date.dateFormat(fmt || this.format);
22807     },
22808
22809     // private
22810     menuListeners : {
22811         select: function(m, d){
22812             this.setValue(d);
22813             this.fireEvent('select', this, d);
22814         },
22815         show : function(){ // retain focus styling
22816             this.onFocus();
22817         },
22818         hide : function(){
22819             this.focus.defer(10, this);
22820             var ml = this.menuListeners;
22821             this.menu.un("select", ml.select,  this);
22822             this.menu.un("show", ml.show,  this);
22823             this.menu.un("hide", ml.hide,  this);
22824         }
22825     },
22826
22827     // private
22828     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22829     onTriggerClick : function(){
22830         if(this.disabled){
22831             return;
22832         }
22833         if(this.menu == null){
22834             this.menu = new Roo.menu.DateMenu();
22835         }
22836         Roo.apply(this.menu.picker,  {
22837             showClear: this.allowBlank,
22838             minDate : this.minValue,
22839             maxDate : this.maxValue,
22840             disabledDatesRE : this.ddMatch,
22841             disabledDatesText : this.disabledDatesText,
22842             disabledDays : this.disabledDays,
22843             disabledDaysText : this.disabledDaysText,
22844             format : this.format,
22845             minText : String.format(this.minText, this.formatDate(this.minValue)),
22846             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22847         });
22848         this.menu.on(Roo.apply({}, this.menuListeners, {
22849             scope:this
22850         }));
22851         this.menu.picker.setValue(this.getValue() || new Date());
22852         this.menu.show(this.el, "tl-bl?");
22853     },
22854
22855     beforeBlur : function(){
22856         var v = this.parseDate(this.getRawValue());
22857         if(v){
22858             this.setValue(v);
22859         }
22860     }
22861
22862     /** @cfg {Boolean} grow @hide */
22863     /** @cfg {Number} growMin @hide */
22864     /** @cfg {Number} growMax @hide */
22865     /**
22866      * @hide
22867      * @method autoSize
22868      */
22869 });/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879  
22880
22881 /**
22882  * @class Roo.form.ComboBox
22883  * @extends Roo.form.TriggerField
22884  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
22885  * @constructor
22886  * Create a new ComboBox.
22887  * @param {Object} config Configuration options
22888  */
22889 Roo.form.ComboBox = function(config){
22890     Roo.form.ComboBox.superclass.constructor.call(this, config);
22891     this.addEvents({
22892         /**
22893          * @event expand
22894          * Fires when the dropdown list is expanded
22895              * @param {Roo.form.ComboBox} combo This combo box
22896              */
22897         'expand' : true,
22898         /**
22899          * @event collapse
22900          * Fires when the dropdown list is collapsed
22901              * @param {Roo.form.ComboBox} combo This combo box
22902              */
22903         'collapse' : true,
22904         /**
22905          * @event beforeselect
22906          * Fires before a list item is selected. Return false to cancel the selection.
22907              * @param {Roo.form.ComboBox} combo This combo box
22908              * @param {Roo.data.Record} record The data record returned from the underlying store
22909              * @param {Number} index The index of the selected item in the dropdown list
22910              */
22911         'beforeselect' : true,
22912         /**
22913          * @event select
22914          * Fires when a list item is selected
22915              * @param {Roo.form.ComboBox} combo This combo box
22916              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
22917              * @param {Number} index The index of the selected item in the dropdown list
22918              */
22919         'select' : true,
22920         /**
22921          * @event beforequery
22922          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
22923          * The event object passed has these properties:
22924              * @param {Roo.form.ComboBox} combo This combo box
22925              * @param {String} query The query
22926              * @param {Boolean} forceAll true to force "all" query
22927              * @param {Boolean} cancel true to cancel the query
22928              * @param {Object} e The query event object
22929              */
22930         'beforequery': true,
22931          /**
22932          * @event add
22933          * Fires when the 'add' icon is pressed (add a listener to enable add button)
22934              * @param {Roo.form.ComboBox} combo This combo box
22935              */
22936         'add' : true,
22937         /**
22938          * @event edit
22939          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
22940              * @param {Roo.form.ComboBox} combo This combo box
22941              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
22942              */
22943         'edit' : true
22944         
22945         
22946     });
22947     if(this.transform){
22948         this.allowDomMove = false;
22949         var s = Roo.getDom(this.transform);
22950         if(!this.hiddenName){
22951             this.hiddenName = s.name;
22952         }
22953         if(!this.store){
22954             this.mode = 'local';
22955             var d = [], opts = s.options;
22956             for(var i = 0, len = opts.length;i < len; i++){
22957                 var o = opts[i];
22958                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
22959                 if(o.selected) {
22960                     this.value = value;
22961                 }
22962                 d.push([value, o.text]);
22963             }
22964             this.store = new Roo.data.SimpleStore({
22965                 'id': 0,
22966                 fields: ['value', 'text'],
22967                 data : d
22968             });
22969             this.valueField = 'value';
22970             this.displayField = 'text';
22971         }
22972         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
22973         if(!this.lazyRender){
22974             this.target = true;
22975             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
22976             s.parentNode.removeChild(s); // remove it
22977             this.render(this.el.parentNode);
22978         }else{
22979             s.parentNode.removeChild(s); // remove it
22980         }
22981
22982     }
22983     if (this.store) {
22984         this.store = Roo.factory(this.store, Roo.data);
22985     }
22986     
22987     this.selectedIndex = -1;
22988     if(this.mode == 'local'){
22989         if(config.queryDelay === undefined){
22990             this.queryDelay = 10;
22991         }
22992         if(config.minChars === undefined){
22993             this.minChars = 0;
22994         }
22995     }
22996 };
22997
22998 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
22999     /**
23000      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23001      */
23002     /**
23003      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23004      * rendering into an Roo.Editor, defaults to false)
23005      */
23006     /**
23007      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23008      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23009      */
23010     /**
23011      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23012      */
23013     /**
23014      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23015      * the dropdown list (defaults to undefined, with no header element)
23016      */
23017
23018      /**
23019      * @cfg {String/Roo.Template} tpl The template to use to render the output
23020      */
23021      
23022     // private
23023     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23024     /**
23025      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23026      */
23027     listWidth: undefined,
23028     /**
23029      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23030      * mode = 'remote' or 'text' if mode = 'local')
23031      */
23032     displayField: undefined,
23033     /**
23034      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23035      * mode = 'remote' or 'value' if mode = 'local'). 
23036      * Note: use of a valueField requires the user make a selection
23037      * in order for a value to be mapped.
23038      */
23039     valueField: undefined,
23040     
23041     
23042     /**
23043      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23044      * field's data value (defaults to the underlying DOM element's name)
23045      */
23046     hiddenName: undefined,
23047     /**
23048      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23049      */
23050     listClass: '',
23051     /**
23052      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23053      */
23054     selectedClass: 'x-combo-selected',
23055     /**
23056      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23057      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23058      * which displays a downward arrow icon).
23059      */
23060     triggerClass : 'x-form-arrow-trigger',
23061     /**
23062      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23063      */
23064     shadow:'sides',
23065     /**
23066      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23067      * anchor positions (defaults to 'tl-bl')
23068      */
23069     listAlign: 'tl-bl?',
23070     /**
23071      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23072      */
23073     maxHeight: 300,
23074     /**
23075      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23076      * query specified by the allQuery config option (defaults to 'query')
23077      */
23078     triggerAction: 'query',
23079     /**
23080      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23081      * (defaults to 4, does not apply if editable = false)
23082      */
23083     minChars : 4,
23084     /**
23085      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23086      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23087      */
23088     typeAhead: false,
23089     /**
23090      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23091      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23092      */
23093     queryDelay: 500,
23094     /**
23095      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23096      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23097      */
23098     pageSize: 0,
23099     /**
23100      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23101      * when editable = true (defaults to false)
23102      */
23103     selectOnFocus:false,
23104     /**
23105      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23106      */
23107     queryParam: 'query',
23108     /**
23109      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23110      * when mode = 'remote' (defaults to 'Loading...')
23111      */
23112     loadingText: 'Loading...',
23113     /**
23114      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23115      */
23116     resizable: false,
23117     /**
23118      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23119      */
23120     handleHeight : 8,
23121     /**
23122      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23123      * traditional select (defaults to true)
23124      */
23125     editable: true,
23126     /**
23127      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23128      */
23129     allQuery: '',
23130     /**
23131      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23132      */
23133     mode: 'remote',
23134     /**
23135      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23136      * listWidth has a higher value)
23137      */
23138     minListWidth : 70,
23139     /**
23140      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23141      * allow the user to set arbitrary text into the field (defaults to false)
23142      */
23143     forceSelection:false,
23144     /**
23145      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23146      * if typeAhead = true (defaults to 250)
23147      */
23148     typeAheadDelay : 250,
23149     /**
23150      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23151      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23152      */
23153     valueNotFoundText : undefined,
23154     /**
23155      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23156      */
23157     blockFocus : false,
23158     
23159     /**
23160      * @cfg {Boolean} disableClear Disable showing of clear button.
23161      */
23162     disableClear : false,
23163     /**
23164      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23165      */
23166     alwaysQuery : false,
23167     
23168     //private
23169     addicon : false,
23170     editicon: false,
23171     
23172     // element that contains real text value.. (when hidden is used..)
23173      
23174     // private
23175     onRender : function(ct, position){
23176         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23177         if(this.hiddenName){
23178             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23179                     'before', true);
23180             this.hiddenField.value =
23181                 this.hiddenValue !== undefined ? this.hiddenValue :
23182                 this.value !== undefined ? this.value : '';
23183
23184             // prevent input submission
23185             this.el.dom.removeAttribute('name');
23186              
23187              
23188         }
23189         if(Roo.isGecko){
23190             this.el.dom.setAttribute('autocomplete', 'off');
23191         }
23192
23193         var cls = 'x-combo-list';
23194
23195         this.list = new Roo.Layer({
23196             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23197         });
23198
23199         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23200         this.list.setWidth(lw);
23201         this.list.swallowEvent('mousewheel');
23202         this.assetHeight = 0;
23203
23204         if(this.title){
23205             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23206             this.assetHeight += this.header.getHeight();
23207         }
23208
23209         this.innerList = this.list.createChild({cls:cls+'-inner'});
23210         this.innerList.on('mouseover', this.onViewOver, this);
23211         this.innerList.on('mousemove', this.onViewMove, this);
23212         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23213         
23214         if(this.allowBlank && !this.pageSize && !this.disableClear){
23215             this.footer = this.list.createChild({cls:cls+'-ft'});
23216             this.pageTb = new Roo.Toolbar(this.footer);
23217            
23218         }
23219         if(this.pageSize){
23220             this.footer = this.list.createChild({cls:cls+'-ft'});
23221             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23222                     {pageSize: this.pageSize});
23223             
23224         }
23225         
23226         if (this.pageTb && this.allowBlank && !this.disableClear) {
23227             var _this = this;
23228             this.pageTb.add(new Roo.Toolbar.Fill(), {
23229                 cls: 'x-btn-icon x-btn-clear',
23230                 text: '&#160;',
23231                 handler: function()
23232                 {
23233                     _this.collapse();
23234                     _this.clearValue();
23235                     _this.onSelect(false, -1);
23236                 }
23237             });
23238         }
23239         if (this.footer) {
23240             this.assetHeight += this.footer.getHeight();
23241         }
23242         
23243
23244         if(!this.tpl){
23245             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23246         }
23247
23248         this.view = new Roo.View(this.innerList, this.tpl, {
23249             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23250         });
23251
23252         this.view.on('click', this.onViewClick, this);
23253
23254         this.store.on('beforeload', this.onBeforeLoad, this);
23255         this.store.on('load', this.onLoad, this);
23256         this.store.on('loadexception', this.onLoadException, this);
23257
23258         if(this.resizable){
23259             this.resizer = new Roo.Resizable(this.list,  {
23260                pinned:true, handles:'se'
23261             });
23262             this.resizer.on('resize', function(r, w, h){
23263                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23264                 this.listWidth = w;
23265                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23266                 this.restrictHeight();
23267             }, this);
23268             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23269         }
23270         if(!this.editable){
23271             this.editable = true;
23272             this.setEditable(false);
23273         }  
23274         
23275         
23276         if (typeof(this.events.add.listeners) != 'undefined') {
23277             
23278             this.addicon = this.wrap.createChild(
23279                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23280        
23281             this.addicon.on('click', function(e) {
23282                 this.fireEvent('add', this);
23283             }, this);
23284         }
23285         if (typeof(this.events.edit.listeners) != 'undefined') {
23286             
23287             this.editicon = this.wrap.createChild(
23288                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23289             if (this.addicon) {
23290                 this.editicon.setStyle('margin-left', '40px');
23291             }
23292             this.editicon.on('click', function(e) {
23293                 
23294                 // we fire even  if inothing is selected..
23295                 this.fireEvent('edit', this, this.lastData );
23296                 
23297             }, this);
23298         }
23299         
23300         
23301         
23302     },
23303
23304     // private
23305     initEvents : function(){
23306         Roo.form.ComboBox.superclass.initEvents.call(this);
23307
23308         this.keyNav = new Roo.KeyNav(this.el, {
23309             "up" : function(e){
23310                 this.inKeyMode = true;
23311                 this.selectPrev();
23312             },
23313
23314             "down" : function(e){
23315                 if(!this.isExpanded()){
23316                     this.onTriggerClick();
23317                 }else{
23318                     this.inKeyMode = true;
23319                     this.selectNext();
23320                 }
23321             },
23322
23323             "enter" : function(e){
23324                 this.onViewClick();
23325                 //return true;
23326             },
23327
23328             "esc" : function(e){
23329                 this.collapse();
23330             },
23331
23332             "tab" : function(e){
23333                 this.onViewClick(false);
23334                 this.fireEvent("specialkey", this, e);
23335                 return true;
23336             },
23337
23338             scope : this,
23339
23340             doRelay : function(foo, bar, hname){
23341                 if(hname == 'down' || this.scope.isExpanded()){
23342                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23343                 }
23344                 return true;
23345             },
23346
23347             forceKeyDown: true
23348         });
23349         this.queryDelay = Math.max(this.queryDelay || 10,
23350                 this.mode == 'local' ? 10 : 250);
23351         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23352         if(this.typeAhead){
23353             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23354         }
23355         if(this.editable !== false){
23356             this.el.on("keyup", this.onKeyUp, this);
23357         }
23358         if(this.forceSelection){
23359             this.on('blur', this.doForce, this);
23360         }
23361     },
23362
23363     onDestroy : function(){
23364         if(this.view){
23365             this.view.setStore(null);
23366             this.view.el.removeAllListeners();
23367             this.view.el.remove();
23368             this.view.purgeListeners();
23369         }
23370         if(this.list){
23371             this.list.destroy();
23372         }
23373         if(this.store){
23374             this.store.un('beforeload', this.onBeforeLoad, this);
23375             this.store.un('load', this.onLoad, this);
23376             this.store.un('loadexception', this.onLoadException, this);
23377         }
23378         Roo.form.ComboBox.superclass.onDestroy.call(this);
23379     },
23380
23381     // private
23382     fireKey : function(e){
23383         if(e.isNavKeyPress() && !this.list.isVisible()){
23384             this.fireEvent("specialkey", this, e);
23385         }
23386     },
23387
23388     // private
23389     onResize: function(w, h){
23390         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23391         
23392         if(typeof w != 'number'){
23393             // we do not handle it!?!?
23394             return;
23395         }
23396         var tw = this.trigger.getWidth();
23397         tw += this.addicon ? this.addicon.getWidth() : 0;
23398         tw += this.editicon ? this.editicon.getWidth() : 0;
23399         var x = w - tw;
23400         this.el.setWidth( this.adjustWidth('input', x));
23401             
23402         this.trigger.setStyle('left', x+'px');
23403         
23404         if(this.list && this.listWidth === undefined){
23405             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23406             this.list.setWidth(lw);
23407             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23408         }
23409         
23410     
23411         
23412     },
23413
23414     /**
23415      * Allow or prevent the user from directly editing the field text.  If false is passed,
23416      * the user will only be able to select from the items defined in the dropdown list.  This method
23417      * is the runtime equivalent of setting the 'editable' config option at config time.
23418      * @param {Boolean} value True to allow the user to directly edit the field text
23419      */
23420     setEditable : function(value){
23421         if(value == this.editable){
23422             return;
23423         }
23424         this.editable = value;
23425         if(!value){
23426             this.el.dom.setAttribute('readOnly', true);
23427             this.el.on('mousedown', this.onTriggerClick,  this);
23428             this.el.addClass('x-combo-noedit');
23429         }else{
23430             this.el.dom.setAttribute('readOnly', false);
23431             this.el.un('mousedown', this.onTriggerClick,  this);
23432             this.el.removeClass('x-combo-noedit');
23433         }
23434     },
23435
23436     // private
23437     onBeforeLoad : function(){
23438         if(!this.hasFocus){
23439             return;
23440         }
23441         this.innerList.update(this.loadingText ?
23442                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23443         this.restrictHeight();
23444         this.selectedIndex = -1;
23445     },
23446
23447     // private
23448     onLoad : function(){
23449         if(!this.hasFocus){
23450             return;
23451         }
23452         if(this.store.getCount() > 0){
23453             this.expand();
23454             this.restrictHeight();
23455             if(this.lastQuery == this.allQuery){
23456                 if(this.editable){
23457                     this.el.dom.select();
23458                 }
23459                 if(!this.selectByValue(this.value, true)){
23460                     this.select(0, true);
23461                 }
23462             }else{
23463                 this.selectNext();
23464                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23465                     this.taTask.delay(this.typeAheadDelay);
23466                 }
23467             }
23468         }else{
23469             this.onEmptyResults();
23470         }
23471         //this.el.focus();
23472     },
23473     // private
23474     onLoadException : function()
23475     {
23476         this.collapse();
23477         Roo.log(this.store.reader.jsonData);
23478         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23479             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23480         }
23481         
23482         
23483     },
23484     // private
23485     onTypeAhead : function(){
23486         if(this.store.getCount() > 0){
23487             var r = this.store.getAt(0);
23488             var newValue = r.data[this.displayField];
23489             var len = newValue.length;
23490             var selStart = this.getRawValue().length;
23491             if(selStart != len){
23492                 this.setRawValue(newValue);
23493                 this.selectText(selStart, newValue.length);
23494             }
23495         }
23496     },
23497
23498     // private
23499     onSelect : function(record, index){
23500         if(this.fireEvent('beforeselect', this, record, index) !== false){
23501             this.setFromData(index > -1 ? record.data : false);
23502             this.collapse();
23503             this.fireEvent('select', this, record, index);
23504         }
23505     },
23506
23507     /**
23508      * Returns the currently selected field value or empty string if no value is set.
23509      * @return {String} value The selected value
23510      */
23511     getValue : function(){
23512         if(this.valueField){
23513             return typeof this.value != 'undefined' ? this.value : '';
23514         }else{
23515             return Roo.form.ComboBox.superclass.getValue.call(this);
23516         }
23517     },
23518
23519     /**
23520      * Clears any text/value currently set in the field
23521      */
23522     clearValue : function(){
23523         if(this.hiddenField){
23524             this.hiddenField.value = '';
23525         }
23526         this.value = '';
23527         this.setRawValue('');
23528         this.lastSelectionText = '';
23529         this.applyEmptyText();
23530     },
23531
23532     /**
23533      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23534      * will be displayed in the field.  If the value does not match the data value of an existing item,
23535      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23536      * Otherwise the field will be blank (although the value will still be set).
23537      * @param {String} value The value to match
23538      */
23539     setValue : function(v){
23540         var text = v;
23541         if(this.valueField){
23542             var r = this.findRecord(this.valueField, v);
23543             if(r){
23544                 text = r.data[this.displayField];
23545             }else if(this.valueNotFoundText !== undefined){
23546                 text = this.valueNotFoundText;
23547             }
23548         }
23549         this.lastSelectionText = text;
23550         if(this.hiddenField){
23551             this.hiddenField.value = v;
23552         }
23553         Roo.form.ComboBox.superclass.setValue.call(this, text);
23554         this.value = v;
23555     },
23556     /**
23557      * @property {Object} the last set data for the element
23558      */
23559     
23560     lastData : false,
23561     /**
23562      * Sets the value of the field based on a object which is related to the record format for the store.
23563      * @param {Object} value the value to set as. or false on reset?
23564      */
23565     setFromData : function(o){
23566         var dv = ''; // display value
23567         var vv = ''; // value value..
23568         this.lastData = o;
23569         if (this.displayField) {
23570             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23571         } else {
23572             // this is an error condition!!!
23573             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23574         }
23575         
23576         if(this.valueField){
23577             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23578         }
23579         if(this.hiddenField){
23580             this.hiddenField.value = vv;
23581             
23582             this.lastSelectionText = dv;
23583             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23584             this.value = vv;
23585             return;
23586         }
23587         // no hidden field.. - we store the value in 'value', but still display
23588         // display field!!!!
23589         this.lastSelectionText = dv;
23590         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23591         this.value = vv;
23592         
23593         
23594     },
23595     // private
23596     reset : function(){
23597         // overridden so that last data is reset..
23598         this.setValue(this.originalValue);
23599         this.clearInvalid();
23600         this.lastData = false;
23601     },
23602     // private
23603     findRecord : function(prop, value){
23604         var record;
23605         if(this.store.getCount() > 0){
23606             this.store.each(function(r){
23607                 if(r.data[prop] == value){
23608                     record = r;
23609                     return false;
23610                 }
23611                 return true;
23612             });
23613         }
23614         return record;
23615     },
23616     
23617     getName: function()
23618     {
23619         // returns hidden if it's set..
23620         if (!this.rendered) {return ''};
23621         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
23622         
23623     },
23624     // private
23625     onViewMove : function(e, t){
23626         this.inKeyMode = false;
23627     },
23628
23629     // private
23630     onViewOver : function(e, t){
23631         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
23632             return;
23633         }
23634         var item = this.view.findItemFromChild(t);
23635         if(item){
23636             var index = this.view.indexOf(item);
23637             this.select(index, false);
23638         }
23639     },
23640
23641     // private
23642     onViewClick : function(doFocus)
23643     {
23644         var index = this.view.getSelectedIndexes()[0];
23645         var r = this.store.getAt(index);
23646         if(r){
23647             this.onSelect(r, index);
23648         }
23649         if(doFocus !== false && !this.blockFocus){
23650             this.el.focus();
23651         }
23652     },
23653
23654     // private
23655     restrictHeight : function(){
23656         this.innerList.dom.style.height = '';
23657         var inner = this.innerList.dom;
23658         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
23659         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
23660         this.list.beginUpdate();
23661         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
23662         this.list.alignTo(this.el, this.listAlign);
23663         this.list.endUpdate();
23664     },
23665
23666     // private
23667     onEmptyResults : function(){
23668         this.collapse();
23669     },
23670
23671     /**
23672      * Returns true if the dropdown list is expanded, else false.
23673      */
23674     isExpanded : function(){
23675         return this.list.isVisible();
23676     },
23677
23678     /**
23679      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
23680      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23681      * @param {String} value The data value of the item to select
23682      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23683      * selected item if it is not currently in view (defaults to true)
23684      * @return {Boolean} True if the value matched an item in the list, else false
23685      */
23686     selectByValue : function(v, scrollIntoView){
23687         if(v !== undefined && v !== null){
23688             var r = this.findRecord(this.valueField || this.displayField, v);
23689             if(r){
23690                 this.select(this.store.indexOf(r), scrollIntoView);
23691                 return true;
23692             }
23693         }
23694         return false;
23695     },
23696
23697     /**
23698      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
23699      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
23700      * @param {Number} index The zero-based index of the list item to select
23701      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
23702      * selected item if it is not currently in view (defaults to true)
23703      */
23704     select : function(index, scrollIntoView){
23705         this.selectedIndex = index;
23706         this.view.select(index);
23707         if(scrollIntoView !== false){
23708             var el = this.view.getNode(index);
23709             if(el){
23710                 this.innerList.scrollChildIntoView(el, false);
23711             }
23712         }
23713     },
23714
23715     // private
23716     selectNext : function(){
23717         var ct = this.store.getCount();
23718         if(ct > 0){
23719             if(this.selectedIndex == -1){
23720                 this.select(0);
23721             }else if(this.selectedIndex < ct-1){
23722                 this.select(this.selectedIndex+1);
23723             }
23724         }
23725     },
23726
23727     // private
23728     selectPrev : function(){
23729         var ct = this.store.getCount();
23730         if(ct > 0){
23731             if(this.selectedIndex == -1){
23732                 this.select(0);
23733             }else if(this.selectedIndex != 0){
23734                 this.select(this.selectedIndex-1);
23735             }
23736         }
23737     },
23738
23739     // private
23740     onKeyUp : function(e){
23741         if(this.editable !== false && !e.isSpecialKey()){
23742             this.lastKey = e.getKey();
23743             this.dqTask.delay(this.queryDelay);
23744         }
23745     },
23746
23747     // private
23748     validateBlur : function(){
23749         return !this.list || !this.list.isVisible();   
23750     },
23751
23752     // private
23753     initQuery : function(){
23754         this.doQuery(this.getRawValue());
23755     },
23756
23757     // private
23758     doForce : function(){
23759         if(this.el.dom.value.length > 0){
23760             this.el.dom.value =
23761                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
23762             this.applyEmptyText();
23763         }
23764     },
23765
23766     /**
23767      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
23768      * query allowing the query action to be canceled if needed.
23769      * @param {String} query The SQL query to execute
23770      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
23771      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
23772      * saved in the current store (defaults to false)
23773      */
23774     doQuery : function(q, forceAll){
23775         if(q === undefined || q === null){
23776             q = '';
23777         }
23778         var qe = {
23779             query: q,
23780             forceAll: forceAll,
23781             combo: this,
23782             cancel:false
23783         };
23784         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
23785             return false;
23786         }
23787         q = qe.query;
23788         forceAll = qe.forceAll;
23789         if(forceAll === true || (q.length >= this.minChars)){
23790             if(this.lastQuery != q || this.alwaysQuery){
23791                 this.lastQuery = q;
23792                 if(this.mode == 'local'){
23793                     this.selectedIndex = -1;
23794                     if(forceAll){
23795                         this.store.clearFilter();
23796                     }else{
23797                         this.store.filter(this.displayField, q);
23798                     }
23799                     this.onLoad();
23800                 }else{
23801                     this.store.baseParams[this.queryParam] = q;
23802                     this.store.load({
23803                         params: this.getParams(q)
23804                     });
23805                     this.expand();
23806                 }
23807             }else{
23808                 this.selectedIndex = -1;
23809                 this.onLoad();   
23810             }
23811         }
23812     },
23813
23814     // private
23815     getParams : function(q){
23816         var p = {};
23817         //p[this.queryParam] = q;
23818         if(this.pageSize){
23819             p.start = 0;
23820             p.limit = this.pageSize;
23821         }
23822         return p;
23823     },
23824
23825     /**
23826      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
23827      */
23828     collapse : function(){
23829         if(!this.isExpanded()){
23830             return;
23831         }
23832         this.list.hide();
23833         Roo.get(document).un('mousedown', this.collapseIf, this);
23834         Roo.get(document).un('mousewheel', this.collapseIf, this);
23835         if (!this.editable) {
23836             Roo.get(document).un('keydown', this.listKeyPress, this);
23837         }
23838         this.fireEvent('collapse', this);
23839     },
23840
23841     // private
23842     collapseIf : function(e){
23843         if(!e.within(this.wrap) && !e.within(this.list)){
23844             this.collapse();
23845         }
23846     },
23847
23848     /**
23849      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
23850      */
23851     expand : function(){
23852         if(this.isExpanded() || !this.hasFocus){
23853             return;
23854         }
23855         this.list.alignTo(this.el, this.listAlign);
23856         this.list.show();
23857         Roo.get(document).on('mousedown', this.collapseIf, this);
23858         Roo.get(document).on('mousewheel', this.collapseIf, this);
23859         if (!this.editable) {
23860             Roo.get(document).on('keydown', this.listKeyPress, this);
23861         }
23862         
23863         this.fireEvent('expand', this);
23864     },
23865
23866     // private
23867     // Implements the default empty TriggerField.onTriggerClick function
23868     onTriggerClick : function(){
23869         if(this.disabled){
23870             return;
23871         }
23872         if(this.isExpanded()){
23873             this.collapse();
23874             if (!this.blockFocus) {
23875                 this.el.focus();
23876             }
23877             
23878         }else {
23879             this.hasFocus = true;
23880             if(this.triggerAction == 'all') {
23881                 this.doQuery(this.allQuery, true);
23882             } else {
23883                 this.doQuery(this.getRawValue());
23884             }
23885             if (!this.blockFocus) {
23886                 this.el.focus();
23887             }
23888         }
23889     },
23890     listKeyPress : function(e)
23891     {
23892         //Roo.log('listkeypress');
23893         // scroll to first matching element based on key pres..
23894         if (e.isSpecialKey()) {
23895             return false;
23896         }
23897         var k = String.fromCharCode(e.getKey()).toUpperCase();
23898         //Roo.log(k);
23899         var match  = false;
23900         var csel = this.view.getSelectedNodes();
23901         var cselitem = false;
23902         if (csel.length) {
23903             var ix = this.view.indexOf(csel[0]);
23904             cselitem  = this.store.getAt(ix);
23905             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
23906                 cselitem = false;
23907             }
23908             
23909         }
23910         
23911         this.store.each(function(v) { 
23912             if (cselitem) {
23913                 // start at existing selection.
23914                 if (cselitem.id == v.id) {
23915                     cselitem = false;
23916                 }
23917                 return;
23918             }
23919                 
23920             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
23921                 match = this.store.indexOf(v);
23922                 return false;
23923             }
23924         }, this);
23925         
23926         if (match === false) {
23927             return true; // no more action?
23928         }
23929         // scroll to?
23930         this.view.select(match);
23931         var sn = Roo.get(this.view.getSelectedNodes()[0])
23932         sn.scrollIntoView(sn.dom.parentNode, false);
23933     }
23934
23935     /** 
23936     * @cfg {Boolean} grow 
23937     * @hide 
23938     */
23939     /** 
23940     * @cfg {Number} growMin 
23941     * @hide 
23942     */
23943     /** 
23944     * @cfg {Number} growMax 
23945     * @hide 
23946     */
23947     /**
23948      * @hide
23949      * @method autoSize
23950      */
23951 });/*
23952  * Copyright(c) 2010-2012, Roo J Solutions Limited
23953  *
23954  * Licence LGPL
23955  *
23956  */
23957
23958 /**
23959  * @class Roo.form.ComboBoxArray
23960  * @extends Roo.form.TextField
23961  * A facebook style adder... for lists of email / people / countries  etc...
23962  * pick multiple items from a combo box, and shows each one.
23963  *
23964  *  Fred [x]  Brian [x]  [Pick another |v]
23965  *
23966  *
23967  *  For this to work: it needs various extra information
23968  *    - normal combo problay has
23969  *      name, hiddenName
23970  *    + displayField, valueField
23971  *
23972  *    For our purpose...
23973  *
23974  *
23975  *   If we change from 'extends' to wrapping...
23976  *   
23977  *  
23978  *
23979  
23980  
23981  * @constructor
23982  * Create a new ComboBoxArray.
23983  * @param {Object} config Configuration options
23984  */
23985  
23986
23987 Roo.form.ComboBoxArray = function(config)
23988 {
23989     
23990     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
23991     
23992     this.items = new Roo.util.MixedCollection(false);
23993     
23994     // construct the child combo...
23995     
23996     
23997     
23998     
23999    
24000     
24001 }
24002
24003  
24004 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24005
24006     /**
24007      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24008      */
24009     
24010     lastData : false,
24011     
24012     // behavies liek a hiddne field
24013     inputType:      'hidden',
24014     /**
24015      * @cfg {Number} width The width of the box that displays the selected element
24016      */ 
24017     width:          300,
24018
24019     
24020     
24021     /**
24022      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24023      */
24024     name : false,
24025     /**
24026      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24027      */
24028     hiddenName : false,
24029     
24030     
24031     // private the array of items that are displayed..
24032     items  : false,
24033     // private - the hidden field el.
24034     hiddenEl : false,
24035     // private - the filed el..
24036     el : false,
24037     
24038     //validateValue : function() { return true; }, // all values are ok!
24039     //onAddClick: function() { },
24040     
24041     onRender : function(ct, position) 
24042     {
24043         
24044         // create the standard hidden element
24045         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24046         
24047         
24048         // give fake names to child combo;
24049         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24050         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24051         
24052         this.combo = Roo.factory(this.combo, Roo.form);
24053         this.combo.onRender(ct, position);
24054         
24055         // assigned so form know we need to do this..
24056         this.store          = this.combo.store;
24057         this.valueField     = this.combo.valueField;
24058         this.displayField   = this.combo.displayField ;
24059         
24060         
24061         this.combo.wrap.addClass('x-cbarray-grp');
24062         
24063         var cbwrap = this.combo.wrap.createChild(
24064             {tag: 'div', cls: 'x-cbarray-cb'},
24065             this.combo.el.dom
24066         );
24067         
24068              
24069         this.hiddenEl = this.combo.wrap.createChild({
24070             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24071         });
24072         this.el = this.combo.wrap.createChild({
24073             tag: 'input',  type:'hidden' , name: this.name, value : ''
24074         });
24075          //   this.el.dom.removeAttribute("name");
24076         
24077         
24078         this.outerWrap = this.combo.wrap;
24079         this.wrap = cbwrap;
24080         
24081         this.outerWrap.setWidth(this.width);
24082         this.outerWrap.dom.removeChild(this.el.dom);
24083         
24084         this.wrap.dom.appendChild(this.el.dom);
24085         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24086         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24087         
24088         this.combo.trigger.setStyle('position','relative');
24089         this.combo.trigger.setStyle('left', '0px');
24090         this.combo.trigger.setStyle('top', '2px');
24091         
24092         this.combo.el.setStyle('vertical-align', 'text-bottom');
24093         
24094         //this.trigger.setStyle('vertical-align', 'top');
24095         
24096         // this should use the code from combo really... on('add' ....)
24097         if (this.adder) {
24098             
24099         
24100             this.adder = this.outerWrap.createChild(
24101                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24102             var _t = this;
24103             this.adder.on('click', function(e) {
24104                 _t.fireEvent('adderclick', this, e);
24105             }, _t);
24106         }
24107         //var _t = this;
24108         //this.adder.on('click', this.onAddClick, _t);
24109         
24110         
24111         this.combo.on('select', function(cb, rec, ix) {
24112             this.addItem(rec.data);
24113             
24114             cb.setValue('');
24115             cb.el.dom.value = '';
24116             //cb.lastData = rec.data;
24117             // add to list
24118             
24119         }, this);
24120         
24121         
24122     },
24123     
24124     
24125     getName: function()
24126     {
24127         // returns hidden if it's set..
24128         if (!this.rendered) {return ''};
24129         return  this.hiddenName ? this.hiddenName : this.name;
24130         
24131     },
24132     
24133     
24134     onResize: function(w, h){
24135         
24136         return;
24137         // not sure if this is needed..
24138         //this.combo.onResize(w,h);
24139         
24140         if(typeof w != 'number'){
24141             // we do not handle it!?!?
24142             return;
24143         }
24144         var tw = this.combo.trigger.getWidth();
24145         tw += this.addicon ? this.addicon.getWidth() : 0;
24146         tw += this.editicon ? this.editicon.getWidth() : 0;
24147         var x = w - tw;
24148         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24149             
24150         this.combo.trigger.setStyle('left', '0px');
24151         
24152         if(this.list && this.listWidth === undefined){
24153             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24154             this.list.setWidth(lw);
24155             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24156         }
24157         
24158     
24159         
24160     },
24161     
24162     addItem: function(rec)
24163     {
24164         var valueField = this.combo.valueField;
24165         var displayField = this.combo.displayField;
24166         if (this.items.indexOfKey(rec[valueField]) > -1) {
24167             //console.log("GOT " + rec.data.id);
24168             return;
24169         }
24170         
24171         var x = new Roo.form.ComboBoxArray.Item({
24172             //id : rec[this.idField],
24173             data : rec,
24174             displayField : displayField ,
24175             tipField : displayField ,
24176             cb : this
24177         });
24178         // use the 
24179         this.items.add(rec[valueField],x);
24180         // add it before the element..
24181         this.updateHiddenEl();
24182         x.render(this.outerWrap, this.wrap.dom);
24183         // add the image handler..
24184     },
24185     
24186     updateHiddenEl : function()
24187     {
24188         this.validate();
24189         if (!this.hiddenEl) {
24190             return;
24191         }
24192         var ar = [];
24193         var idField = this.combo.valueField;
24194         
24195         this.items.each(function(f) {
24196             ar.push(f.data[idField]);
24197            
24198         });
24199         this.hiddenEl.dom.value = ar.join(',');
24200         this.validate();
24201     },
24202     
24203     reset : function()
24204     {
24205         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24206         this.items.each(function(f) {
24207            f.remove(); 
24208         });
24209         this.el.dom.value = '';
24210         if (this.hiddenEl) {
24211             this.hiddenEl.dom.value = '';
24212         }
24213         
24214     },
24215     getValue: function()
24216     {
24217         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24218     },
24219     setValue: function(v) // not a valid action - must use addItems..
24220     {
24221          
24222         this.reset();
24223         
24224         
24225         
24226         if (this.store.isLocal && (typeof(v) == 'string')) {
24227             // then we can use the store to find the values..
24228             // comma seperated at present.. this needs to allow JSON based encoding..
24229             this.hiddenEl.value  = v;
24230             var v_ar = [];
24231             Roo.each(v.split(','), function(k) {
24232                 Roo.log("CHECK " + this.valueField + ',' + k);
24233                 var li = this.store.query(this.valueField, k);
24234                 if (!li.length) {
24235                     return;
24236                 }
24237                 add = {};
24238                 add[this.valueField] = k;
24239                 add[this.displayField] = li.item(0).data[this.displayField];
24240                 
24241                 this.addItem(add);
24242             }, this) 
24243              
24244         }
24245         if (typeof(v) == 'object') {
24246             // then let's assume it's an array of objects..
24247             Roo.each(v, function(l) {
24248                 this.addItem(l);
24249             }, this);
24250              
24251         }
24252         
24253         
24254     },
24255     setFromData: function(v)
24256     {
24257         // this recieves an object, if setValues is called.
24258         this.reset();
24259         this.el.dom.value = v[this.displayField];
24260         this.hiddenEl.dom.value = v[this.valueField];
24261         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24262             return;
24263         }
24264         var kv = v[this.valueField];
24265         var dv = v[this.displayField];
24266         kv = typeof(kv) != 'string' ? '' : kv;
24267         dv = typeof(dv) != 'string' ? '' : dv;
24268         
24269         
24270         var keys = kv.split(',');
24271         var display = dv.split(',');
24272         for (var i = 0 ; i < keys.length; i++) {
24273             
24274             add = {};
24275             add[this.valueField] = keys[i];
24276             add[this.displayField] = display[i];
24277             this.addItem(add);
24278         }
24279       
24280         
24281     },
24282     
24283     
24284     validateValue : function(value){
24285         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24286         
24287     }
24288     
24289 });
24290
24291
24292
24293 /**
24294  * @class Roo.form.ComboBoxArray.Item
24295  * @extends Roo.BoxComponent
24296  * A selected item in the list
24297  *  Fred [x]  Brian [x]  [Pick another |v]
24298  * 
24299  * @constructor
24300  * Create a new item.
24301  * @param {Object} config Configuration options
24302  */
24303  
24304 Roo.form.ComboBoxArray.Item = function(config) {
24305     config.id = Roo.id();
24306     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24307 }
24308
24309 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24310     data : {},
24311     cb: false,
24312     displayField : false,
24313     tipField : false,
24314     
24315     
24316     defaultAutoCreate : {
24317         tag: 'div',
24318         cls: 'x-cbarray-item',
24319         cn : [ 
24320             { tag: 'div' },
24321             {
24322                 tag: 'img',
24323                 width:16,
24324                 height : 16,
24325                 src : Roo.BLANK_IMAGE_URL ,
24326                 align: 'center'
24327             }
24328         ]
24329         
24330     },
24331     
24332  
24333     onRender : function(ct, position)
24334     {
24335         Roo.form.Field.superclass.onRender.call(this, ct, position);
24336         
24337         if(!this.el){
24338             var cfg = this.getAutoCreate();
24339             this.el = ct.createChild(cfg, position);
24340         }
24341         
24342         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24343         
24344         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24345             this.cb.renderer(this.data) :
24346             String.format('{0}',this.data[this.displayField]);
24347         
24348             
24349         this.el.child('div').dom.setAttribute('qtip',
24350                         String.format('{0}',this.data[this.tipField])
24351         );
24352         
24353         this.el.child('img').on('click', this.remove, this);
24354         
24355     },
24356    
24357     remove : function()
24358     {
24359         
24360         this.cb.items.remove(this);
24361         this.el.child('img').un('click', this.remove, this);
24362         this.el.remove();
24363         this.cb.updateHiddenEl();
24364     }
24365     
24366     
24367 });/*
24368  * Based on:
24369  * Ext JS Library 1.1.1
24370  * Copyright(c) 2006-2007, Ext JS, LLC.
24371  *
24372  * Originally Released Under LGPL - original licence link has changed is not relivant.
24373  *
24374  * Fork - LGPL
24375  * <script type="text/javascript">
24376  */
24377 /**
24378  * @class Roo.form.Checkbox
24379  * @extends Roo.form.Field
24380  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24381  * @constructor
24382  * Creates a new Checkbox
24383  * @param {Object} config Configuration options
24384  */
24385 Roo.form.Checkbox = function(config){
24386     Roo.form.Checkbox.superclass.constructor.call(this, config);
24387     this.addEvents({
24388         /**
24389          * @event check
24390          * Fires when the checkbox is checked or unchecked.
24391              * @param {Roo.form.Checkbox} this This checkbox
24392              * @param {Boolean} checked The new checked value
24393              */
24394         check : true
24395     });
24396 };
24397
24398 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24399     /**
24400      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24401      */
24402     focusClass : undefined,
24403     /**
24404      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24405      */
24406     fieldClass: "x-form-field",
24407     /**
24408      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24409      */
24410     checked: false,
24411     /**
24412      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24413      * {tag: "input", type: "checkbox", autocomplete: "off"})
24414      */
24415     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24416     /**
24417      * @cfg {String} boxLabel The text that appears beside the checkbox
24418      */
24419     boxLabel : "",
24420     /**
24421      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24422      */  
24423     inputValue : '1',
24424     /**
24425      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24426      */
24427      valueOff: '0', // value when not checked..
24428
24429     actionMode : 'viewEl', 
24430     //
24431     // private
24432     itemCls : 'x-menu-check-item x-form-item',
24433     groupClass : 'x-menu-group-item',
24434     inputType : 'hidden',
24435     
24436     
24437     inSetChecked: false, // check that we are not calling self...
24438     
24439     inputElement: false, // real input element?
24440     basedOn: false, // ????
24441     
24442     isFormField: true, // not sure where this is needed!!!!
24443
24444     onResize : function(){
24445         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24446         if(!this.boxLabel){
24447             this.el.alignTo(this.wrap, 'c-c');
24448         }
24449     },
24450
24451     initEvents : function(){
24452         Roo.form.Checkbox.superclass.initEvents.call(this);
24453         this.el.on("click", this.onClick,  this);
24454         this.el.on("change", this.onClick,  this);
24455     },
24456
24457
24458     getResizeEl : function(){
24459         return this.wrap;
24460     },
24461
24462     getPositionEl : function(){
24463         return this.wrap;
24464     },
24465
24466     // private
24467     onRender : function(ct, position){
24468         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24469         /*
24470         if(this.inputValue !== undefined){
24471             this.el.dom.value = this.inputValue;
24472         }
24473         */
24474         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24475         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24476         var viewEl = this.wrap.createChild({ 
24477             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24478         this.viewEl = viewEl;   
24479         this.wrap.on('click', this.onClick,  this); 
24480         
24481         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24482         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24483         
24484         
24485         
24486         if(this.boxLabel){
24487             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24488         //    viewEl.on('click', this.onClick,  this); 
24489         }
24490         //if(this.checked){
24491             this.setChecked(this.checked);
24492         //}else{
24493             //this.checked = this.el.dom;
24494         //}
24495
24496     },
24497
24498     // private
24499     initValue : Roo.emptyFn,
24500
24501     /**
24502      * Returns the checked state of the checkbox.
24503      * @return {Boolean} True if checked, else false
24504      */
24505     getValue : function(){
24506         if(this.el){
24507             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24508         }
24509         return this.valueOff;
24510         
24511     },
24512
24513         // private
24514     onClick : function(){ 
24515         this.setChecked(!this.checked);
24516
24517         //if(this.el.dom.checked != this.checked){
24518         //    this.setValue(this.el.dom.checked);
24519        // }
24520     },
24521
24522     /**
24523      * Sets the checked state of the checkbox.
24524      * On is always based on a string comparison between inputValue and the param.
24525      * @param {Boolean/String} value - the value to set 
24526      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24527      */
24528     setValue : function(v,suppressEvent){
24529         
24530         
24531         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24532         //if(this.el && this.el.dom){
24533         //    this.el.dom.checked = this.checked;
24534         //    this.el.dom.defaultChecked = this.checked;
24535         //}
24536         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24537         //this.fireEvent("check", this, this.checked);
24538     },
24539     // private..
24540     setChecked : function(state,suppressEvent)
24541     {
24542         if (this.inSetChecked) {
24543             this.checked = state;
24544             return;
24545         }
24546         
24547     
24548         if(this.wrap){
24549             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24550         }
24551         this.checked = state;
24552         if(suppressEvent !== true){
24553             this.fireEvent('check', this, state);
24554         }
24555         this.inSetChecked = true;
24556         this.el.dom.value = state ? this.inputValue : this.valueOff;
24557         this.inSetChecked = false;
24558         
24559     },
24560     // handle setting of hidden value by some other method!!?!?
24561     setFromHidden: function()
24562     {
24563         if(!this.el){
24564             return;
24565         }
24566         //console.log("SET FROM HIDDEN");
24567         //alert('setFrom hidden');
24568         this.setValue(this.el.dom.value);
24569     },
24570     
24571     onDestroy : function()
24572     {
24573         if(this.viewEl){
24574             Roo.get(this.viewEl).remove();
24575         }
24576          
24577         Roo.form.Checkbox.superclass.onDestroy.call(this);
24578     }
24579
24580 });/*
24581  * Based on:
24582  * Ext JS Library 1.1.1
24583  * Copyright(c) 2006-2007, Ext JS, LLC.
24584  *
24585  * Originally Released Under LGPL - original licence link has changed is not relivant.
24586  *
24587  * Fork - LGPL
24588  * <script type="text/javascript">
24589  */
24590  
24591 /**
24592  * @class Roo.form.Radio
24593  * @extends Roo.form.Checkbox
24594  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24595  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24596  * @constructor
24597  * Creates a new Radio
24598  * @param {Object} config Configuration options
24599  */
24600 Roo.form.Radio = function(){
24601     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24602 };
24603 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24604     inputType: 'radio',
24605
24606     /**
24607      * If this radio is part of a group, it will return the selected value
24608      * @return {String}
24609      */
24610     getGroupValue : function(){
24611         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24612     }
24613 });//<script type="text/javascript">
24614
24615 /*
24616  * Ext JS Library 1.1.1
24617  * Copyright(c) 2006-2007, Ext JS, LLC.
24618  * licensing@extjs.com
24619  * 
24620  * http://www.extjs.com/license
24621  */
24622  
24623  /*
24624   * 
24625   * Known bugs:
24626   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
24627   * - IE ? - no idea how much works there.
24628   * 
24629   * 
24630   * 
24631   */
24632  
24633
24634 /**
24635  * @class Ext.form.HtmlEditor
24636  * @extends Ext.form.Field
24637  * Provides a lightweight HTML Editor component.
24638  *
24639  * This has been tested on Fireforx / Chrome.. IE may not be so great..
24640  * 
24641  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
24642  * supported by this editor.</b><br/><br/>
24643  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
24644  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24645  */
24646 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
24647       /**
24648      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
24649      */
24650     toolbars : false,
24651     /**
24652      * @cfg {String} createLinkText The default text for the create link prompt
24653      */
24654     createLinkText : 'Please enter the URL for the link:',
24655     /**
24656      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
24657      */
24658     defaultLinkValue : 'http:/'+'/',
24659    
24660      /**
24661      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24662      *                        Roo.resizable.
24663      */
24664     resizable : false,
24665      /**
24666      * @cfg {Number} height (in pixels)
24667      */   
24668     height: 300,
24669    /**
24670      * @cfg {Number} width (in pixels)
24671      */   
24672     width: 500,
24673     
24674     /**
24675      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24676      * 
24677      */
24678     stylesheets: false,
24679     
24680     // id of frame..
24681     frameId: false,
24682     
24683     // private properties
24684     validationEvent : false,
24685     deferHeight: true,
24686     initialized : false,
24687     activated : false,
24688     sourceEditMode : false,
24689     onFocus : Roo.emptyFn,
24690     iframePad:3,
24691     hideMode:'offsets',
24692     
24693     defaultAutoCreate : { // modified by initCompnoent..
24694         tag: "textarea",
24695         style:"width:500px;height:300px;",
24696         autocomplete: "off"
24697     },
24698
24699     // private
24700     initComponent : function(){
24701         this.addEvents({
24702             /**
24703              * @event initialize
24704              * Fires when the editor is fully initialized (including the iframe)
24705              * @param {HtmlEditor} this
24706              */
24707             initialize: true,
24708             /**
24709              * @event activate
24710              * Fires when the editor is first receives the focus. Any insertion must wait
24711              * until after this event.
24712              * @param {HtmlEditor} this
24713              */
24714             activate: true,
24715              /**
24716              * @event beforesync
24717              * Fires before the textarea is updated with content from the editor iframe. Return false
24718              * to cancel the sync.
24719              * @param {HtmlEditor} this
24720              * @param {String} html
24721              */
24722             beforesync: true,
24723              /**
24724              * @event beforepush
24725              * Fires before the iframe editor is updated with content from the textarea. Return false
24726              * to cancel the push.
24727              * @param {HtmlEditor} this
24728              * @param {String} html
24729              */
24730             beforepush: true,
24731              /**
24732              * @event sync
24733              * Fires when the textarea is updated with content from the editor iframe.
24734              * @param {HtmlEditor} this
24735              * @param {String} html
24736              */
24737             sync: true,
24738              /**
24739              * @event push
24740              * Fires when the iframe editor is updated with content from the textarea.
24741              * @param {HtmlEditor} this
24742              * @param {String} html
24743              */
24744             push: true,
24745              /**
24746              * @event editmodechange
24747              * Fires when the editor switches edit modes
24748              * @param {HtmlEditor} this
24749              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
24750              */
24751             editmodechange: true,
24752             /**
24753              * @event editorevent
24754              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24755              * @param {HtmlEditor} this
24756              */
24757             editorevent: true
24758         });
24759         this.defaultAutoCreate =  {
24760             tag: "textarea",
24761             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
24762             autocomplete: "off"
24763         };
24764     },
24765
24766     /**
24767      * Protected method that will not generally be called directly. It
24768      * is called when the editor creates its toolbar. Override this method if you need to
24769      * add custom toolbar buttons.
24770      * @param {HtmlEditor} editor
24771      */
24772     createToolbar : function(editor){
24773         if (!editor.toolbars || !editor.toolbars.length) {
24774             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
24775         }
24776         
24777         for (var i =0 ; i < editor.toolbars.length;i++) {
24778             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
24779             editor.toolbars[i].init(editor);
24780         }
24781          
24782         
24783     },
24784
24785     /**
24786      * Protected method that will not generally be called directly. It
24787      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24788      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24789      */
24790     getDocMarkup : function(){
24791         // body styles..
24792         var st = '';
24793         if (this.stylesheets === false) {
24794             
24795             Roo.get(document.head).select('style').each(function(node) {
24796                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24797             });
24798             
24799             Roo.get(document.head).select('link').each(function(node) { 
24800                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24801             });
24802             
24803         } else if (!this.stylesheets.length) {
24804                 // simple..
24805                 st = '<style type="text/css">' +
24806                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24807                    '</style>';
24808         } else {
24809             Roo.each(this.stylesheets, function(s) {
24810                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
24811             });
24812             
24813         }
24814         
24815         st +=  '<style type="text/css">' +
24816             'IMG { cursor: pointer } ' +
24817         '</style>';
24818
24819         
24820         return '<html><head>' + st  +
24821             //<style type="text/css">' +
24822             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24823             //'</style>' +
24824             ' </head><body class="roo-htmleditor-body"></body></html>';
24825     },
24826
24827     // private
24828     onRender : function(ct, position)
24829     {
24830         var _t = this;
24831         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
24832         this.el.dom.style.border = '0 none';
24833         this.el.dom.setAttribute('tabIndex', -1);
24834         this.el.addClass('x-hidden');
24835         if(Roo.isIE){ // fix IE 1px bogus margin
24836             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24837         }
24838         this.wrap = this.el.wrap({
24839             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
24840         });
24841         
24842         if (this.resizable) {
24843             this.resizeEl = new Roo.Resizable(this.wrap, {
24844                 pinned : true,
24845                 wrap: true,
24846                 dynamic : true,
24847                 minHeight : this.height,
24848                 height: this.height,
24849                 handles : this.resizable,
24850                 width: this.width,
24851                 listeners : {
24852                     resize : function(r, w, h) {
24853                         _t.onResize(w,h); // -something
24854                     }
24855                 }
24856             });
24857             
24858         }
24859
24860         this.frameId = Roo.id();
24861         
24862         this.createToolbar(this);
24863         
24864       
24865         
24866         var iframe = this.wrap.createChild({
24867             tag: 'iframe',
24868             id: this.frameId,
24869             name: this.frameId,
24870             frameBorder : 'no',
24871             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24872         }, this.el
24873         );
24874         
24875        // console.log(iframe);
24876         //this.wrap.dom.appendChild(iframe);
24877
24878         this.iframe = iframe.dom;
24879
24880          this.assignDocWin();
24881         
24882         this.doc.designMode = 'on';
24883        
24884         this.doc.open();
24885         this.doc.write(this.getDocMarkup());
24886         this.doc.close();
24887
24888         
24889         var task = { // must defer to wait for browser to be ready
24890             run : function(){
24891                 //console.log("run task?" + this.doc.readyState);
24892                 this.assignDocWin();
24893                 if(this.doc.body || this.doc.readyState == 'complete'){
24894                     try {
24895                         this.doc.designMode="on";
24896                     } catch (e) {
24897                         return;
24898                     }
24899                     Roo.TaskMgr.stop(task);
24900                     this.initEditor.defer(10, this);
24901                 }
24902             },
24903             interval : 10,
24904             duration:10000,
24905             scope: this
24906         };
24907         Roo.TaskMgr.start(task);
24908
24909         if(!this.width){
24910             this.setSize(this.wrap.getSize());
24911         }
24912         if (this.resizeEl) {
24913             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
24914             // should trigger onReize..
24915         }
24916     },
24917
24918     // private
24919     onResize : function(w, h)
24920     {
24921         //Roo.log('resize: ' +w + ',' + h );
24922         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
24923         if(this.el && this.iframe){
24924             if(typeof w == 'number'){
24925                 var aw = w - this.wrap.getFrameWidth('lr');
24926                 this.el.setWidth(this.adjustWidth('textarea', aw));
24927                 this.iframe.style.width = aw + 'px';
24928             }
24929             if(typeof h == 'number'){
24930                 var tbh = 0;
24931                 for (var i =0; i < this.toolbars.length;i++) {
24932                     // fixme - ask toolbars for heights?
24933                     tbh += this.toolbars[i].tb.el.getHeight();
24934                     if (this.toolbars[i].footer) {
24935                         tbh += this.toolbars[i].footer.el.getHeight();
24936                     }
24937                 }
24938                 
24939                 
24940                 
24941                 
24942                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
24943                 ah -= 5; // knock a few pixes off for look..
24944                 this.el.setHeight(this.adjustWidth('textarea', ah));
24945                 this.iframe.style.height = ah + 'px';
24946                 if(this.doc){
24947                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
24948                 }
24949             }
24950         }
24951     },
24952
24953     /**
24954      * Toggles the editor between standard and source edit mode.
24955      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24956      */
24957     toggleSourceEdit : function(sourceEditMode){
24958         
24959         this.sourceEditMode = sourceEditMode === true;
24960         
24961         if(this.sourceEditMode){
24962           
24963             this.syncValue();
24964             this.iframe.className = 'x-hidden';
24965             this.el.removeClass('x-hidden');
24966             this.el.dom.removeAttribute('tabIndex');
24967             this.el.focus();
24968         }else{
24969              
24970             this.pushValue();
24971             this.iframe.className = '';
24972             this.el.addClass('x-hidden');
24973             this.el.dom.setAttribute('tabIndex', -1);
24974             this.deferFocus();
24975         }
24976         this.setSize(this.wrap.getSize());
24977         this.fireEvent('editmodechange', this, this.sourceEditMode);
24978     },
24979
24980     // private used internally
24981     createLink : function(){
24982         var url = prompt(this.createLinkText, this.defaultLinkValue);
24983         if(url && url != 'http:/'+'/'){
24984             this.relayCmd('createlink', url);
24985         }
24986     },
24987
24988     // private (for BoxComponent)
24989     adjustSize : Roo.BoxComponent.prototype.adjustSize,
24990
24991     // private (for BoxComponent)
24992     getResizeEl : function(){
24993         return this.wrap;
24994     },
24995
24996     // private (for BoxComponent)
24997     getPositionEl : function(){
24998         return this.wrap;
24999     },
25000
25001     // private
25002     initEvents : function(){
25003         this.originalValue = this.getValue();
25004     },
25005
25006     /**
25007      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25008      * @method
25009      */
25010     markInvalid : Roo.emptyFn,
25011     /**
25012      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25013      * @method
25014      */
25015     clearInvalid : Roo.emptyFn,
25016
25017     setValue : function(v){
25018         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25019         this.pushValue();
25020     },
25021
25022     /**
25023      * Protected method that will not generally be called directly. If you need/want
25024      * custom HTML cleanup, this is the method you should override.
25025      * @param {String} html The HTML to be cleaned
25026      * return {String} The cleaned HTML
25027      */
25028     cleanHtml : function(html){
25029         html = String(html);
25030         if(html.length > 5){
25031             if(Roo.isSafari){ // strip safari nonsense
25032                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25033             }
25034         }
25035         if(html == '&nbsp;'){
25036             html = '';
25037         }
25038         return html;
25039     },
25040
25041     /**
25042      * Protected method that will not generally be called directly. Syncs the contents
25043      * of the editor iframe with the textarea.
25044      */
25045     syncValue : function(){
25046         if(this.initialized){
25047             var bd = (this.doc.body || this.doc.documentElement);
25048             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25049             var html = bd.innerHTML;
25050             if(Roo.isSafari){
25051                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25052                 var m = bs.match(/text-align:(.*?);/i);
25053                 if(m && m[1]){
25054                     html = '<div style="'+m[0]+'">' + html + '</div>';
25055                 }
25056             }
25057             html = this.cleanHtml(html);
25058             // fix up the special chars..
25059             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25060                 return "&#"+b.charCodeAt()+";" 
25061             });
25062             if(this.fireEvent('beforesync', this, html) !== false){
25063                 this.el.dom.value = html;
25064                 this.fireEvent('sync', this, html);
25065             }
25066         }
25067     },
25068
25069     /**
25070      * Protected method that will not generally be called directly. Pushes the value of the textarea
25071      * into the iframe editor.
25072      */
25073     pushValue : function(){
25074         if(this.initialized){
25075             var v = this.el.dom.value;
25076             if(v.length < 1){
25077                 v = '&#160;';
25078             }
25079             
25080             if(this.fireEvent('beforepush', this, v) !== false){
25081                 var d = (this.doc.body || this.doc.documentElement);
25082                 d.innerHTML = v;
25083                 this.cleanUpPaste();
25084                 this.el.dom.value = d.innerHTML;
25085                 this.fireEvent('push', this, v);
25086             }
25087         }
25088     },
25089
25090     // private
25091     deferFocus : function(){
25092         this.focus.defer(10, this);
25093     },
25094
25095     // doc'ed in Field
25096     focus : function(){
25097         if(this.win && !this.sourceEditMode){
25098             this.win.focus();
25099         }else{
25100             this.el.focus();
25101         }
25102     },
25103     
25104     assignDocWin: function()
25105     {
25106         var iframe = this.iframe;
25107         
25108          if(Roo.isIE){
25109             this.doc = iframe.contentWindow.document;
25110             this.win = iframe.contentWindow;
25111         } else {
25112             if (!Roo.get(this.frameId)) {
25113                 return;
25114             }
25115             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25116             this.win = Roo.get(this.frameId).dom.contentWindow;
25117         }
25118     },
25119     
25120     // private
25121     initEditor : function(){
25122         //console.log("INIT EDITOR");
25123         this.assignDocWin();
25124         
25125         
25126         
25127         this.doc.designMode="on";
25128         this.doc.open();
25129         this.doc.write(this.getDocMarkup());
25130         this.doc.close();
25131         
25132         var dbody = (this.doc.body || this.doc.documentElement);
25133         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25134         // this copies styles from the containing element into thsi one..
25135         // not sure why we need all of this..
25136         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25137         ss['background-attachment'] = 'fixed'; // w3c
25138         dbody.bgProperties = 'fixed'; // ie
25139         Roo.DomHelper.applyStyles(dbody, ss);
25140         Roo.EventManager.on(this.doc, {
25141             //'mousedown': this.onEditorEvent,
25142             'mouseup': this.onEditorEvent,
25143             'dblclick': this.onEditorEvent,
25144             'click': this.onEditorEvent,
25145             'keyup': this.onEditorEvent,
25146             buffer:100,
25147             scope: this
25148         });
25149         if(Roo.isGecko){
25150             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25151         }
25152         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25153             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25154         }
25155         this.initialized = true;
25156
25157         this.fireEvent('initialize', this);
25158         this.pushValue();
25159     },
25160
25161     // private
25162     onDestroy : function(){
25163         
25164         
25165         
25166         if(this.rendered){
25167             
25168             for (var i =0; i < this.toolbars.length;i++) {
25169                 // fixme - ask toolbars for heights?
25170                 this.toolbars[i].onDestroy();
25171             }
25172             
25173             this.wrap.dom.innerHTML = '';
25174             this.wrap.remove();
25175         }
25176     },
25177
25178     // private
25179     onFirstFocus : function(){
25180         
25181         this.assignDocWin();
25182         
25183         
25184         this.activated = true;
25185         for (var i =0; i < this.toolbars.length;i++) {
25186             this.toolbars[i].onFirstFocus();
25187         }
25188        
25189         if(Roo.isGecko){ // prevent silly gecko errors
25190             this.win.focus();
25191             var s = this.win.getSelection();
25192             if(!s.focusNode || s.focusNode.nodeType != 3){
25193                 var r = s.getRangeAt(0);
25194                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25195                 r.collapse(true);
25196                 this.deferFocus();
25197             }
25198             try{
25199                 this.execCmd('useCSS', true);
25200                 this.execCmd('styleWithCSS', false);
25201             }catch(e){}
25202         }
25203         this.fireEvent('activate', this);
25204     },
25205
25206     // private
25207     adjustFont: function(btn){
25208         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25209         //if(Roo.isSafari){ // safari
25210         //    adjust *= 2;
25211        // }
25212         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25213         if(Roo.isSafari){ // safari
25214             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25215             v =  (v < 10) ? 10 : v;
25216             v =  (v > 48) ? 48 : v;
25217             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25218             
25219         }
25220         
25221         
25222         v = Math.max(1, v+adjust);
25223         
25224         this.execCmd('FontSize', v  );
25225     },
25226
25227     onEditorEvent : function(e){
25228         this.fireEvent('editorevent', this, e);
25229       //  this.updateToolbar();
25230         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25231     },
25232
25233     insertTag : function(tg)
25234     {
25235         // could be a bit smarter... -> wrap the current selected tRoo..
25236         
25237         this.execCmd("formatblock",   tg);
25238         
25239     },
25240     
25241     insertText : function(txt)
25242     {
25243         
25244         
25245         range = this.createRange();
25246         range.deleteContents();
25247                //alert(Sender.getAttribute('label'));
25248                
25249         range.insertNode(this.doc.createTextNode(txt));
25250     } ,
25251     
25252     // private
25253     relayBtnCmd : function(btn){
25254         this.relayCmd(btn.cmd);
25255     },
25256
25257     /**
25258      * Executes a Midas editor command on the editor document and performs necessary focus and
25259      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25260      * @param {String} cmd The Midas command
25261      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25262      */
25263     relayCmd : function(cmd, value){
25264         this.win.focus();
25265         this.execCmd(cmd, value);
25266         this.fireEvent('editorevent', this);
25267         //this.updateToolbar();
25268         this.deferFocus();
25269     },
25270
25271     /**
25272      * Executes a Midas editor command directly on the editor document.
25273      * For visual commands, you should use {@link #relayCmd} instead.
25274      * <b>This should only be called after the editor is initialized.</b>
25275      * @param {String} cmd The Midas command
25276      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25277      */
25278     execCmd : function(cmd, value){
25279         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25280         this.syncValue();
25281     },
25282  
25283  
25284    
25285     /**
25286      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25287      * to insert tRoo.
25288      * @param {String} text | dom node.. 
25289      */
25290     insertAtCursor : function(text)
25291     {
25292         
25293         
25294         
25295         if(!this.activated){
25296             return;
25297         }
25298         /*
25299         if(Roo.isIE){
25300             this.win.focus();
25301             var r = this.doc.selection.createRange();
25302             if(r){
25303                 r.collapse(true);
25304                 r.pasteHTML(text);
25305                 this.syncValue();
25306                 this.deferFocus();
25307             
25308             }
25309             return;
25310         }
25311         */
25312         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25313             this.win.focus();
25314             
25315             
25316             // from jquery ui (MIT licenced)
25317             var range, node;
25318             var win = this.win;
25319             
25320             if (win.getSelection && win.getSelection().getRangeAt) {
25321                 range = win.getSelection().getRangeAt(0);
25322                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25323                 range.insertNode(node);
25324             } else if (win.document.selection && win.document.selection.createRange) {
25325                 // no firefox support
25326                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25327                 win.document.selection.createRange().pasteHTML(txt);
25328             } else {
25329                 // no firefox support
25330                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25331                 this.execCmd('InsertHTML', txt);
25332             } 
25333             
25334             this.syncValue();
25335             
25336             this.deferFocus();
25337         }
25338     },
25339  // private
25340     mozKeyPress : function(e){
25341         if(e.ctrlKey){
25342             var c = e.getCharCode(), cmd;
25343           
25344             if(c > 0){
25345                 c = String.fromCharCode(c).toLowerCase();
25346                 switch(c){
25347                     case 'b':
25348                         cmd = 'bold';
25349                         break;
25350                     case 'i':
25351                         cmd = 'italic';
25352                         break;
25353                     
25354                     case 'u':
25355                         cmd = 'underline';
25356                         break;
25357                     
25358                     case 'v':
25359                         this.cleanUpPaste.defer(100, this);
25360                         return;
25361                         
25362                 }
25363                 if(cmd){
25364                     this.win.focus();
25365                     this.execCmd(cmd);
25366                     this.deferFocus();
25367                     e.preventDefault();
25368                 }
25369                 
25370             }
25371         }
25372     },
25373
25374     // private
25375     fixKeys : function(){ // load time branching for fastest keydown performance
25376         if(Roo.isIE){
25377             return function(e){
25378                 var k = e.getKey(), r;
25379                 if(k == e.TAB){
25380                     e.stopEvent();
25381                     r = this.doc.selection.createRange();
25382                     if(r){
25383                         r.collapse(true);
25384                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25385                         this.deferFocus();
25386                     }
25387                     return;
25388                 }
25389                 
25390                 if(k == e.ENTER){
25391                     r = this.doc.selection.createRange();
25392                     if(r){
25393                         var target = r.parentElement();
25394                         if(!target || target.tagName.toLowerCase() != 'li'){
25395                             e.stopEvent();
25396                             r.pasteHTML('<br />');
25397                             r.collapse(false);
25398                             r.select();
25399                         }
25400                     }
25401                 }
25402                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25403                     this.cleanUpPaste.defer(100, this);
25404                     return;
25405                 }
25406                 
25407                 
25408             };
25409         }else if(Roo.isOpera){
25410             return function(e){
25411                 var k = e.getKey();
25412                 if(k == e.TAB){
25413                     e.stopEvent();
25414                     this.win.focus();
25415                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25416                     this.deferFocus();
25417                 }
25418                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25419                     this.cleanUpPaste.defer(100, this);
25420                     return;
25421                 }
25422                 
25423             };
25424         }else if(Roo.isSafari){
25425             return function(e){
25426                 var k = e.getKey();
25427                 
25428                 if(k == e.TAB){
25429                     e.stopEvent();
25430                     this.execCmd('InsertText','\t');
25431                     this.deferFocus();
25432                     return;
25433                 }
25434                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25435                     this.cleanUpPaste.defer(100, this);
25436                     return;
25437                 }
25438                 
25439              };
25440         }
25441     }(),
25442     
25443     getAllAncestors: function()
25444     {
25445         var p = this.getSelectedNode();
25446         var a = [];
25447         if (!p) {
25448             a.push(p); // push blank onto stack..
25449             p = this.getParentElement();
25450         }
25451         
25452         
25453         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25454             a.push(p);
25455             p = p.parentNode;
25456         }
25457         a.push(this.doc.body);
25458         return a;
25459     },
25460     lastSel : false,
25461     lastSelNode : false,
25462     
25463     
25464     getSelection : function() 
25465     {
25466         this.assignDocWin();
25467         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25468     },
25469     
25470     getSelectedNode: function() 
25471     {
25472         // this may only work on Gecko!!!
25473         
25474         // should we cache this!!!!
25475         
25476         
25477         
25478          
25479         var range = this.createRange(this.getSelection()).cloneRange();
25480         
25481         if (Roo.isIE) {
25482             var parent = range.parentElement();
25483             while (true) {
25484                 var testRange = range.duplicate();
25485                 testRange.moveToElementText(parent);
25486                 if (testRange.inRange(range)) {
25487                     break;
25488                 }
25489                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25490                     break;
25491                 }
25492                 parent = parent.parentElement;
25493             }
25494             return parent;
25495         }
25496         
25497         // is ancestor a text element.
25498         var ac =  range.commonAncestorContainer;
25499         if (ac.nodeType == 3) {
25500             ac = ac.parentNode;
25501         }
25502         
25503         var ar = ac.childNodes;
25504          
25505         var nodes = [];
25506         var other_nodes = [];
25507         var has_other_nodes = false;
25508         for (var i=0;i<ar.length;i++) {
25509             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25510                 continue;
25511             }
25512             // fullly contained node.
25513             
25514             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25515                 nodes.push(ar[i]);
25516                 continue;
25517             }
25518             
25519             // probably selected..
25520             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25521                 other_nodes.push(ar[i]);
25522                 continue;
25523             }
25524             // outer..
25525             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25526                 continue;
25527             }
25528             
25529             
25530             has_other_nodes = true;
25531         }
25532         if (!nodes.length && other_nodes.length) {
25533             nodes= other_nodes;
25534         }
25535         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25536             return false;
25537         }
25538         
25539         return nodes[0];
25540     },
25541     createRange: function(sel)
25542     {
25543         // this has strange effects when using with 
25544         // top toolbar - not sure if it's a great idea.
25545         //this.editor.contentWindow.focus();
25546         if (typeof sel != "undefined") {
25547             try {
25548                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25549             } catch(e) {
25550                 return this.doc.createRange();
25551             }
25552         } else {
25553             return this.doc.createRange();
25554         }
25555     },
25556     getParentElement: function()
25557     {
25558         
25559         this.assignDocWin();
25560         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25561         
25562         var range = this.createRange(sel);
25563          
25564         try {
25565             var p = range.commonAncestorContainer;
25566             while (p.nodeType == 3) { // text node
25567                 p = p.parentNode;
25568             }
25569             return p;
25570         } catch (e) {
25571             return null;
25572         }
25573     
25574     },
25575     /***
25576      *
25577      * Range intersection.. the hard stuff...
25578      *  '-1' = before
25579      *  '0' = hits..
25580      *  '1' = after.
25581      *         [ -- selected range --- ]
25582      *   [fail]                        [fail]
25583      *
25584      *    basically..
25585      *      if end is before start or  hits it. fail.
25586      *      if start is after end or hits it fail.
25587      *
25588      *   if either hits (but other is outside. - then it's not 
25589      *   
25590      *    
25591      **/
25592     
25593     
25594     // @see http://www.thismuchiknow.co.uk/?p=64.
25595     rangeIntersectsNode : function(range, node)
25596     {
25597         var nodeRange = node.ownerDocument.createRange();
25598         try {
25599             nodeRange.selectNode(node);
25600         } catch (e) {
25601             nodeRange.selectNodeContents(node);
25602         }
25603     
25604         var rangeStartRange = range.cloneRange();
25605         rangeStartRange.collapse(true);
25606     
25607         var rangeEndRange = range.cloneRange();
25608         rangeEndRange.collapse(false);
25609     
25610         var nodeStartRange = nodeRange.cloneRange();
25611         nodeStartRange.collapse(true);
25612     
25613         var nodeEndRange = nodeRange.cloneRange();
25614         nodeEndRange.collapse(false);
25615     
25616         return rangeStartRange.compareBoundaryPoints(
25617                  Range.START_TO_START, nodeEndRange) == -1 &&
25618                rangeEndRange.compareBoundaryPoints(
25619                  Range.START_TO_START, nodeStartRange) == 1;
25620         
25621          
25622     },
25623     rangeCompareNode : function(range, node)
25624     {
25625         var nodeRange = node.ownerDocument.createRange();
25626         try {
25627             nodeRange.selectNode(node);
25628         } catch (e) {
25629             nodeRange.selectNodeContents(node);
25630         }
25631         
25632         
25633         range.collapse(true);
25634     
25635         nodeRange.collapse(true);
25636      
25637         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25638         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25639          
25640         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25641         
25642         var nodeIsBefore   =  ss == 1;
25643         var nodeIsAfter    = ee == -1;
25644         
25645         if (nodeIsBefore && nodeIsAfter)
25646             return 0; // outer
25647         if (!nodeIsBefore && nodeIsAfter)
25648             return 1; //right trailed.
25649         
25650         if (nodeIsBefore && !nodeIsAfter)
25651             return 2;  // left trailed.
25652         // fully contined.
25653         return 3;
25654     },
25655
25656     // private? - in a new class?
25657     cleanUpPaste :  function()
25658     {
25659         // cleans up the whole document..
25660          Roo.log('cleanuppaste');
25661         this.cleanUpChildren(this.doc.body);
25662         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25663         if (clean != this.doc.body.innerHTML) {
25664             this.doc.body.innerHTML = clean;
25665         }
25666         
25667     },
25668     
25669     cleanWordChars : function(input) {
25670         var he = Roo.form.HtmlEditor;
25671     
25672         var output = input;
25673         Roo.each(he.swapCodes, function(sw) { 
25674         
25675             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25676             output = output.replace(swapper, sw[1]);
25677         });
25678         return output;
25679     },
25680     
25681     
25682     cleanUpChildren : function (n)
25683     {
25684         if (!n.childNodes.length) {
25685             return;
25686         }
25687         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25688            this.cleanUpChild(n.childNodes[i]);
25689         }
25690     },
25691     
25692     
25693         
25694     
25695     cleanUpChild : function (node)
25696     {
25697         //console.log(node);
25698         if (node.nodeName == "#text") {
25699             // clean up silly Windows -- stuff?
25700             return; 
25701         }
25702         if (node.nodeName == "#comment") {
25703             node.parentNode.removeChild(node);
25704             // clean up silly Windows -- stuff?
25705             return; 
25706         }
25707         
25708         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
25709             // remove node.
25710             node.parentNode.removeChild(node);
25711             return;
25712             
25713         }
25714         
25715         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
25716         
25717         // remove <a name=....> as rendering on yahoo mailer is bored with this.
25718         
25719         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25720             remove_keep_children = true;
25721         }
25722         
25723         if (remove_keep_children) {
25724             this.cleanUpChildren(node);
25725             // inserts everything just before this node...
25726             while (node.childNodes.length) {
25727                 var cn = node.childNodes[0];
25728                 node.removeChild(cn);
25729                 node.parentNode.insertBefore(cn, node);
25730             }
25731             node.parentNode.removeChild(node);
25732             return;
25733         }
25734         
25735         if (!node.attributes || !node.attributes.length) {
25736             this.cleanUpChildren(node);
25737             return;
25738         }
25739         
25740         function cleanAttr(n,v)
25741         {
25742             
25743             if (v.match(/^\./) || v.match(/^\//)) {
25744                 return;
25745             }
25746             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
25747                 return;
25748             }
25749             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
25750             node.removeAttribute(n);
25751             
25752         }
25753         
25754         function cleanStyle(n,v)
25755         {
25756             if (v.match(/expression/)) { //XSS?? should we even bother..
25757                 node.removeAttribute(n);
25758                 return;
25759             }
25760             
25761             
25762             var parts = v.split(/;/);
25763             Roo.each(parts, function(p) {
25764                 p = p.replace(/\s+/g,'');
25765                 if (!p.length) {
25766                     return true;
25767                 }
25768                 var l = p.split(':').shift().replace(/\s+/g,'');
25769                 
25770                 // only allow 'c whitelisted system attributes'
25771                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
25772                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
25773                     node.removeAttribute(n);
25774                     return false;
25775                 }
25776                 return true;
25777             });
25778             
25779             
25780         }
25781         
25782         
25783         for (var i = node.attributes.length-1; i > -1 ; i--) {
25784             var a = node.attributes[i];
25785             //console.log(a);
25786             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
25787                 node.removeAttribute(a.name);
25788                 return;
25789             }
25790             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
25791                 cleanAttr(a.name,a.value); // fixme..
25792                 return;
25793             }
25794             if (a.name == 'style') {
25795                 cleanStyle(a.name,a.value);
25796             }
25797             /// clean up MS crap..
25798             // tecnically this should be a list of valid class'es..
25799             
25800             
25801             if (a.name == 'class') {
25802                 if (a.value.match(/^Mso/)) {
25803                     node.className = '';
25804                 }
25805                 
25806                 if (a.value.match(/body/)) {
25807                     node.className = '';
25808                 }
25809             }
25810             
25811             // style cleanup!?
25812             // class cleanup?
25813             
25814         }
25815         
25816         
25817         this.cleanUpChildren(node);
25818         
25819         
25820     }
25821     
25822     
25823     // hide stuff that is not compatible
25824     /**
25825      * @event blur
25826      * @hide
25827      */
25828     /**
25829      * @event change
25830      * @hide
25831      */
25832     /**
25833      * @event focus
25834      * @hide
25835      */
25836     /**
25837      * @event specialkey
25838      * @hide
25839      */
25840     /**
25841      * @cfg {String} fieldClass @hide
25842      */
25843     /**
25844      * @cfg {String} focusClass @hide
25845      */
25846     /**
25847      * @cfg {String} autoCreate @hide
25848      */
25849     /**
25850      * @cfg {String} inputType @hide
25851      */
25852     /**
25853      * @cfg {String} invalidClass @hide
25854      */
25855     /**
25856      * @cfg {String} invalidText @hide
25857      */
25858     /**
25859      * @cfg {String} msgFx @hide
25860      */
25861     /**
25862      * @cfg {String} validateOnBlur @hide
25863      */
25864 });
25865
25866 Roo.form.HtmlEditor.white = [
25867         'area', 'br', 'img', 'input', 'hr', 'wbr',
25868         
25869        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25870        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25871        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25872        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25873        'table',   'ul',         'xmp', 
25874        
25875        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25876       'thead',   'tr', 
25877      
25878       'dir', 'menu', 'ol', 'ul', 'dl',
25879        
25880       'embed',  'object'
25881 ];
25882
25883
25884 Roo.form.HtmlEditor.black = [
25885     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25886         'applet', // 
25887         'base',   'basefont', 'bgsound', 'blink',  'body', 
25888         'frame',  'frameset', 'head',    'html',   'ilayer', 
25889         'iframe', 'layer',  'link',     'meta',    'object',   
25890         'script', 'style' ,'title',  'xml' // clean later..
25891 ];
25892 Roo.form.HtmlEditor.clean = [
25893     'script', 'style', 'title', 'xml'
25894 ];
25895 Roo.form.HtmlEditor.remove = [
25896     'font'
25897 ];
25898 // attributes..
25899
25900 Roo.form.HtmlEditor.ablack = [
25901     'on'
25902 ];
25903     
25904 Roo.form.HtmlEditor.aclean = [ 
25905     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
25906 ];
25907
25908 // protocols..
25909 Roo.form.HtmlEditor.pwhite= [
25910         'http',  'https',  'mailto'
25911 ];
25912
25913 // white listed style attributes.
25914 Roo.form.HtmlEditor.cwhite= [
25915         'text-align',
25916         'font-size'
25917 ];
25918
25919
25920 Roo.form.HtmlEditor.swapCodes   =[ 
25921     [    8211, "--" ], 
25922     [    8212, "--" ], 
25923     [    8216,  "'" ],  
25924     [    8217, "'" ],  
25925     [    8220, '"' ],  
25926     [    8221, '"' ],  
25927     [    8226, "*" ],  
25928     [    8230, "..." ]
25929 ]; 
25930
25931     // <script type="text/javascript">
25932 /*
25933  * Based on
25934  * Ext JS Library 1.1.1
25935  * Copyright(c) 2006-2007, Ext JS, LLC.
25936  *  
25937  
25938  */
25939
25940 /**
25941  * @class Roo.form.HtmlEditorToolbar1
25942  * Basic Toolbar
25943  * 
25944  * Usage:
25945  *
25946  new Roo.form.HtmlEditor({
25947     ....
25948     toolbars : [
25949         new Roo.form.HtmlEditorToolbar1({
25950             disable : { fonts: 1 , format: 1, ..., ... , ...],
25951             btns : [ .... ]
25952         })
25953     }
25954      
25955  * 
25956  * @cfg {Object} disable List of elements to disable..
25957  * @cfg {Array} btns List of additional buttons.
25958  * 
25959  * 
25960  * NEEDS Extra CSS? 
25961  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
25962  */
25963  
25964 Roo.form.HtmlEditor.ToolbarStandard = function(config)
25965 {
25966     
25967     Roo.apply(this, config);
25968     
25969     // default disabled, based on 'good practice'..
25970     this.disable = this.disable || {};
25971     Roo.applyIf(this.disable, {
25972         fontSize : true,
25973         colors : true,
25974         specialElements : true
25975     });
25976     
25977     
25978     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
25979     // dont call parent... till later.
25980 }
25981
25982 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
25983     
25984     tb: false,
25985     
25986     rendered: false,
25987     
25988     editor : false,
25989     /**
25990      * @cfg {Object} disable  List of toolbar elements to disable
25991          
25992      */
25993     disable : false,
25994       /**
25995      * @cfg {Array} fontFamilies An array of available font families
25996      */
25997     fontFamilies : [
25998         'Arial',
25999         'Courier New',
26000         'Tahoma',
26001         'Times New Roman',
26002         'Verdana'
26003     ],
26004     
26005     specialChars : [
26006            "&#169;",
26007           "&#174;",     
26008           "&#8482;",    
26009           "&#163;" ,    
26010          // "&#8212;",    
26011           "&#8230;",    
26012           "&#247;" ,    
26013         //  "&#225;" ,     ?? a acute?
26014            "&#8364;"    , //Euro
26015        //   "&#8220;"    ,
26016         //  "&#8221;"    ,
26017         //  "&#8226;"    ,
26018           "&#176;"  //   , // degrees
26019
26020          // "&#233;"     , // e ecute
26021          // "&#250;"     , // u ecute?
26022     ],
26023     
26024     specialElements : [
26025         {
26026             text: "Insert Table",
26027             xtype: 'MenuItem',
26028             xns : Roo.Menu,
26029             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26030                 
26031         },
26032         {    
26033             text: "Insert Image",
26034             xtype: 'MenuItem',
26035             xns : Roo.Menu,
26036             ihtml : '<img src="about:blank"/>'
26037             
26038         }
26039         
26040          
26041     ],
26042     
26043     
26044     inputElements : [ 
26045             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26046             "input:submit", "input:button", "select", "textarea", "label" ],
26047     formats : [
26048         ["p"] ,  
26049         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26050         ["pre"],[ "code"], 
26051         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26052     ],
26053      /**
26054      * @cfg {String} defaultFont default font to use.
26055      */
26056     defaultFont: 'tahoma',
26057    
26058     fontSelect : false,
26059     
26060     
26061     formatCombo : false,
26062     
26063     init : function(editor)
26064     {
26065         this.editor = editor;
26066         
26067         
26068         var fid = editor.frameId;
26069         var etb = this;
26070         function btn(id, toggle, handler){
26071             var xid = fid + '-'+ id ;
26072             return {
26073                 id : xid,
26074                 cmd : id,
26075                 cls : 'x-btn-icon x-edit-'+id,
26076                 enableToggle:toggle !== false,
26077                 scope: editor, // was editor...
26078                 handler:handler||editor.relayBtnCmd,
26079                 clickEvent:'mousedown',
26080                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26081                 tabIndex:-1
26082             };
26083         }
26084         
26085         
26086         
26087         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26088         this.tb = tb;
26089          // stop form submits
26090         tb.el.on('click', function(e){
26091             e.preventDefault(); // what does this do?
26092         });
26093
26094         if(!this.disable.font && !Roo.isSafari){
26095             /* why no safari for fonts
26096             editor.fontSelect = tb.el.createChild({
26097                 tag:'select',
26098                 tabIndex: -1,
26099                 cls:'x-font-select',
26100                 html: editor.createFontOptions()
26101             });
26102             editor.fontSelect.on('change', function(){
26103                 var font = editor.fontSelect.dom.value;
26104                 editor.relayCmd('fontname', font);
26105                 editor.deferFocus();
26106             }, editor);
26107             tb.add(
26108                 editor.fontSelect.dom,
26109                 '-'
26110             );
26111             */
26112         };
26113         if(!this.disable.formats){
26114             this.formatCombo = new Roo.form.ComboBox({
26115                 store: new Roo.data.SimpleStore({
26116                     id : 'tag',
26117                     fields: ['tag'],
26118                     data : this.formats // from states.js
26119                 }),
26120                 blockFocus : true,
26121                 //autoCreate : {tag: "div",  size: "20"},
26122                 displayField:'tag',
26123                 typeAhead: false,
26124                 mode: 'local',
26125                 editable : false,
26126                 triggerAction: 'all',
26127                 emptyText:'Add tag',
26128                 selectOnFocus:true,
26129                 width:135,
26130                 listeners : {
26131                     'select': function(c, r, i) {
26132                         editor.insertTag(r.get('tag'));
26133                         editor.focus();
26134                     }
26135                 }
26136
26137             });
26138             tb.addField(this.formatCombo);
26139             
26140         }
26141         
26142         if(!this.disable.format){
26143             tb.add(
26144                 btn('bold'),
26145                 btn('italic'),
26146                 btn('underline')
26147             );
26148         };
26149         if(!this.disable.fontSize){
26150             tb.add(
26151                 '-',
26152                 
26153                 
26154                 btn('increasefontsize', false, editor.adjustFont),
26155                 btn('decreasefontsize', false, editor.adjustFont)
26156             );
26157         };
26158         
26159         
26160         if(!this.disable.colors){
26161             tb.add(
26162                 '-', {
26163                     id:editor.frameId +'-forecolor',
26164                     cls:'x-btn-icon x-edit-forecolor',
26165                     clickEvent:'mousedown',
26166                     tooltip: this.buttonTips['forecolor'] || undefined,
26167                     tabIndex:-1,
26168                     menu : new Roo.menu.ColorMenu({
26169                         allowReselect: true,
26170                         focus: Roo.emptyFn,
26171                         value:'000000',
26172                         plain:true,
26173                         selectHandler: function(cp, color){
26174                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26175                             editor.deferFocus();
26176                         },
26177                         scope: editor,
26178                         clickEvent:'mousedown'
26179                     })
26180                 }, {
26181                     id:editor.frameId +'backcolor',
26182                     cls:'x-btn-icon x-edit-backcolor',
26183                     clickEvent:'mousedown',
26184                     tooltip: this.buttonTips['backcolor'] || undefined,
26185                     tabIndex:-1,
26186                     menu : new Roo.menu.ColorMenu({
26187                         focus: Roo.emptyFn,
26188                         value:'FFFFFF',
26189                         plain:true,
26190                         allowReselect: true,
26191                         selectHandler: function(cp, color){
26192                             if(Roo.isGecko){
26193                                 editor.execCmd('useCSS', false);
26194                                 editor.execCmd('hilitecolor', color);
26195                                 editor.execCmd('useCSS', true);
26196                                 editor.deferFocus();
26197                             }else{
26198                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26199                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26200                                 editor.deferFocus();
26201                             }
26202                         },
26203                         scope:editor,
26204                         clickEvent:'mousedown'
26205                     })
26206                 }
26207             );
26208         };
26209         // now add all the items...
26210         
26211
26212         if(!this.disable.alignments){
26213             tb.add(
26214                 '-',
26215                 btn('justifyleft'),
26216                 btn('justifycenter'),
26217                 btn('justifyright')
26218             );
26219         };
26220
26221         //if(!Roo.isSafari){
26222             if(!this.disable.links){
26223                 tb.add(
26224                     '-',
26225                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26226                 );
26227             };
26228
26229             if(!this.disable.lists){
26230                 tb.add(
26231                     '-',
26232                     btn('insertorderedlist'),
26233                     btn('insertunorderedlist')
26234                 );
26235             }
26236             if(!this.disable.sourceEdit){
26237                 tb.add(
26238                     '-',
26239                     btn('sourceedit', true, function(btn){
26240                         this.toggleSourceEdit(btn.pressed);
26241                     })
26242                 );
26243             }
26244         //}
26245         
26246         var smenu = { };
26247         // special menu.. - needs to be tidied up..
26248         if (!this.disable.special) {
26249             smenu = {
26250                 text: "&#169;",
26251                 cls: 'x-edit-none',
26252                 
26253                 menu : {
26254                     items : []
26255                 }
26256             };
26257             for (var i =0; i < this.specialChars.length; i++) {
26258                 smenu.menu.items.push({
26259                     
26260                     html: this.specialChars[i],
26261                     handler: function(a,b) {
26262                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26263                         //editor.insertAtCursor(a.html);
26264                         
26265                     },
26266                     tabIndex:-1
26267                 });
26268             }
26269             
26270             
26271             tb.add(smenu);
26272             
26273             
26274         }
26275          
26276         if (!this.disable.specialElements) {
26277             var semenu = {
26278                 text: "Other;",
26279                 cls: 'x-edit-none',
26280                 menu : {
26281                     items : []
26282                 }
26283             };
26284             for (var i =0; i < this.specialElements.length; i++) {
26285                 semenu.menu.items.push(
26286                     Roo.apply({ 
26287                         handler: function(a,b) {
26288                             editor.insertAtCursor(this.ihtml);
26289                         }
26290                     }, this.specialElements[i])
26291                 );
26292                     
26293             }
26294             
26295             tb.add(semenu);
26296             
26297             
26298         }
26299          
26300         
26301         if (this.btns) {
26302             for(var i =0; i< this.btns.length;i++) {
26303                 var b = Roo.factory(this.btns[i],Roo.form);
26304                 b.cls =  'x-edit-none';
26305                 b.scope = editor;
26306                 tb.add(b);
26307             }
26308         
26309         }
26310         
26311         
26312         
26313         // disable everything...
26314         
26315         this.tb.items.each(function(item){
26316            if(item.id != editor.frameId+ '-sourceedit'){
26317                 item.disable();
26318             }
26319         });
26320         this.rendered = true;
26321         
26322         // the all the btns;
26323         editor.on('editorevent', this.updateToolbar, this);
26324         // other toolbars need to implement this..
26325         //editor.on('editmodechange', this.updateToolbar, this);
26326     },
26327     
26328     
26329     
26330     /**
26331      * Protected method that will not generally be called directly. It triggers
26332      * a toolbar update by reading the markup state of the current selection in the editor.
26333      */
26334     updateToolbar: function(){
26335
26336         if(!this.editor.activated){
26337             this.editor.onFirstFocus();
26338             return;
26339         }
26340
26341         var btns = this.tb.items.map, 
26342             doc = this.editor.doc,
26343             frameId = this.editor.frameId;
26344
26345         if(!this.disable.font && !Roo.isSafari){
26346             /*
26347             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26348             if(name != this.fontSelect.dom.value){
26349                 this.fontSelect.dom.value = name;
26350             }
26351             */
26352         }
26353         if(!this.disable.format){
26354             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26355             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26356             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26357         }
26358         if(!this.disable.alignments){
26359             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26360             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26361             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26362         }
26363         if(!Roo.isSafari && !this.disable.lists){
26364             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26365             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26366         }
26367         
26368         var ans = this.editor.getAllAncestors();
26369         if (this.formatCombo) {
26370             
26371             
26372             var store = this.formatCombo.store;
26373             this.formatCombo.setValue("");
26374             for (var i =0; i < ans.length;i++) {
26375                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26376                     // select it..
26377                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26378                     break;
26379                 }
26380             }
26381         }
26382         
26383         
26384         
26385         // hides menus... - so this cant be on a menu...
26386         Roo.menu.MenuMgr.hideAll();
26387
26388         //this.editorsyncValue();
26389     },
26390    
26391     
26392     createFontOptions : function(){
26393         var buf = [], fs = this.fontFamilies, ff, lc;
26394         for(var i = 0, len = fs.length; i< len; i++){
26395             ff = fs[i];
26396             lc = ff.toLowerCase();
26397             buf.push(
26398                 '<option value="',lc,'" style="font-family:',ff,';"',
26399                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26400                     ff,
26401                 '</option>'
26402             );
26403         }
26404         return buf.join('');
26405     },
26406     
26407     toggleSourceEdit : function(sourceEditMode){
26408         if(sourceEditMode === undefined){
26409             sourceEditMode = !this.sourceEditMode;
26410         }
26411         this.sourceEditMode = sourceEditMode === true;
26412         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26413         // just toggle the button?
26414         if(btn.pressed !== this.editor.sourceEditMode){
26415             btn.toggle(this.editor.sourceEditMode);
26416             return;
26417         }
26418         
26419         if(this.sourceEditMode){
26420             this.tb.items.each(function(item){
26421                 if(item.cmd != 'sourceedit'){
26422                     item.disable();
26423                 }
26424             });
26425           
26426         }else{
26427             if(this.initialized){
26428                 this.tb.items.each(function(item){
26429                     item.enable();
26430                 });
26431             }
26432             
26433         }
26434         // tell the editor that it's been pressed..
26435         this.editor.toggleSourceEdit(sourceEditMode);
26436        
26437     },
26438      /**
26439      * Object collection of toolbar tooltips for the buttons in the editor. The key
26440      * is the command id associated with that button and the value is a valid QuickTips object.
26441      * For example:
26442 <pre><code>
26443 {
26444     bold : {
26445         title: 'Bold (Ctrl+B)',
26446         text: 'Make the selected text bold.',
26447         cls: 'x-html-editor-tip'
26448     },
26449     italic : {
26450         title: 'Italic (Ctrl+I)',
26451         text: 'Make the selected text italic.',
26452         cls: 'x-html-editor-tip'
26453     },
26454     ...
26455 </code></pre>
26456     * @type Object
26457      */
26458     buttonTips : {
26459         bold : {
26460             title: 'Bold (Ctrl+B)',
26461             text: 'Make the selected text bold.',
26462             cls: 'x-html-editor-tip'
26463         },
26464         italic : {
26465             title: 'Italic (Ctrl+I)',
26466             text: 'Make the selected text italic.',
26467             cls: 'x-html-editor-tip'
26468         },
26469         underline : {
26470             title: 'Underline (Ctrl+U)',
26471             text: 'Underline the selected text.',
26472             cls: 'x-html-editor-tip'
26473         },
26474         increasefontsize : {
26475             title: 'Grow Text',
26476             text: 'Increase the font size.',
26477             cls: 'x-html-editor-tip'
26478         },
26479         decreasefontsize : {
26480             title: 'Shrink Text',
26481             text: 'Decrease the font size.',
26482             cls: 'x-html-editor-tip'
26483         },
26484         backcolor : {
26485             title: 'Text Highlight Color',
26486             text: 'Change the background color of the selected text.',
26487             cls: 'x-html-editor-tip'
26488         },
26489         forecolor : {
26490             title: 'Font Color',
26491             text: 'Change the color of the selected text.',
26492             cls: 'x-html-editor-tip'
26493         },
26494         justifyleft : {
26495             title: 'Align Text Left',
26496             text: 'Align text to the left.',
26497             cls: 'x-html-editor-tip'
26498         },
26499         justifycenter : {
26500             title: 'Center Text',
26501             text: 'Center text in the editor.',
26502             cls: 'x-html-editor-tip'
26503         },
26504         justifyright : {
26505             title: 'Align Text Right',
26506             text: 'Align text to the right.',
26507             cls: 'x-html-editor-tip'
26508         },
26509         insertunorderedlist : {
26510             title: 'Bullet List',
26511             text: 'Start a bulleted list.',
26512             cls: 'x-html-editor-tip'
26513         },
26514         insertorderedlist : {
26515             title: 'Numbered List',
26516             text: 'Start a numbered list.',
26517             cls: 'x-html-editor-tip'
26518         },
26519         createlink : {
26520             title: 'Hyperlink',
26521             text: 'Make the selected text a hyperlink.',
26522             cls: 'x-html-editor-tip'
26523         },
26524         sourceedit : {
26525             title: 'Source Edit',
26526             text: 'Switch to source editing mode.',
26527             cls: 'x-html-editor-tip'
26528         }
26529     },
26530     // private
26531     onDestroy : function(){
26532         if(this.rendered){
26533             
26534             this.tb.items.each(function(item){
26535                 if(item.menu){
26536                     item.menu.removeAll();
26537                     if(item.menu.el){
26538                         item.menu.el.destroy();
26539                     }
26540                 }
26541                 item.destroy();
26542             });
26543              
26544         }
26545     },
26546     onFirstFocus: function() {
26547         this.tb.items.each(function(item){
26548            item.enable();
26549         });
26550     }
26551 });
26552
26553
26554
26555
26556 // <script type="text/javascript">
26557 /*
26558  * Based on
26559  * Ext JS Library 1.1.1
26560  * Copyright(c) 2006-2007, Ext JS, LLC.
26561  *  
26562  
26563  */
26564
26565  
26566 /**
26567  * @class Roo.form.HtmlEditor.ToolbarContext
26568  * Context Toolbar
26569  * 
26570  * Usage:
26571  *
26572  new Roo.form.HtmlEditor({
26573     ....
26574     toolbars : [
26575         { xtype: 'ToolbarStandard', styles : {} }
26576         { xtype: 'ToolbarContext', disable : {} }
26577     ]
26578 })
26579
26580      
26581  * 
26582  * @config : {Object} disable List of elements to disable.. (not done yet.)
26583  * @config : {Object} styles  Map of styles available.
26584  * 
26585  */
26586
26587 Roo.form.HtmlEditor.ToolbarContext = function(config)
26588 {
26589     
26590     Roo.apply(this, config);
26591     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26592     // dont call parent... till later.
26593     this.styles = this.styles || {};
26594 }
26595 Roo.form.HtmlEditor.ToolbarContext.types = {
26596     'IMG' : {
26597         width : {
26598             title: "Width",
26599             width: 40
26600         },
26601         height:  {
26602             title: "Height",
26603             width: 40
26604         },
26605         align: {
26606             title: "Align",
26607             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26608             width : 80
26609             
26610         },
26611         border: {
26612             title: "Border",
26613             width: 40
26614         },
26615         alt: {
26616             title: "Alt",
26617             width: 120
26618         },
26619         src : {
26620             title: "Src",
26621             width: 220
26622         }
26623         
26624     },
26625     'A' : {
26626         name : {
26627             title: "Name",
26628             width: 50
26629         },
26630         href:  {
26631             title: "Href",
26632             width: 220
26633         } // border?
26634         
26635     },
26636     'TABLE' : {
26637         rows : {
26638             title: "Rows",
26639             width: 20
26640         },
26641         cols : {
26642             title: "Cols",
26643             width: 20
26644         },
26645         width : {
26646             title: "Width",
26647             width: 40
26648         },
26649         height : {
26650             title: "Height",
26651             width: 40
26652         },
26653         border : {
26654             title: "Border",
26655             width: 20
26656         }
26657     },
26658     'TD' : {
26659         width : {
26660             title: "Width",
26661             width: 40
26662         },
26663         height : {
26664             title: "Height",
26665             width: 40
26666         },   
26667         align: {
26668             title: "Align",
26669             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
26670             width: 80
26671         },
26672         valign: {
26673             title: "Valign",
26674             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
26675             width: 80
26676         },
26677         colspan: {
26678             title: "Colspan",
26679             width: 20
26680             
26681         }
26682     },
26683     'INPUT' : {
26684         name : {
26685             title: "name",
26686             width: 120
26687         },
26688         value : {
26689             title: "Value",
26690             width: 120
26691         },
26692         width : {
26693             title: "Width",
26694             width: 40
26695         }
26696     },
26697     'LABEL' : {
26698         'for' : {
26699             title: "For",
26700             width: 120
26701         }
26702     },
26703     'TEXTAREA' : {
26704           name : {
26705             title: "name",
26706             width: 120
26707         },
26708         rows : {
26709             title: "Rows",
26710             width: 20
26711         },
26712         cols : {
26713             title: "Cols",
26714             width: 20
26715         }
26716     },
26717     'SELECT' : {
26718         name : {
26719             title: "name",
26720             width: 120
26721         },
26722         selectoptions : {
26723             title: "Options",
26724             width: 200
26725         }
26726     },
26727     
26728     // should we really allow this??
26729     // should this just be 
26730     'BODY' : {
26731         title : {
26732             title: "title",
26733             width: 200,
26734             disabled : true
26735         }
26736     },
26737     '*' : {
26738         // empty..
26739     }
26740 };
26741
26742
26743
26744 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
26745     
26746     tb: false,
26747     
26748     rendered: false,
26749     
26750     editor : false,
26751     /**
26752      * @cfg {Object} disable  List of toolbar elements to disable
26753          
26754      */
26755     disable : false,
26756     /**
26757      * @cfg {Object} styles List of styles 
26758      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
26759      *
26760      * These must be defined in the page, so they get rendered correctly..
26761      * .headline { }
26762      * TD.underline { }
26763      * 
26764      */
26765     styles : false,
26766     
26767     
26768     
26769     toolbars : false,
26770     
26771     init : function(editor)
26772     {
26773         this.editor = editor;
26774         
26775         
26776         var fid = editor.frameId;
26777         var etb = this;
26778         function btn(id, toggle, handler){
26779             var xid = fid + '-'+ id ;
26780             return {
26781                 id : xid,
26782                 cmd : id,
26783                 cls : 'x-btn-icon x-edit-'+id,
26784                 enableToggle:toggle !== false,
26785                 scope: editor, // was editor...
26786                 handler:handler||editor.relayBtnCmd,
26787                 clickEvent:'mousedown',
26788                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26789                 tabIndex:-1
26790             };
26791         }
26792         // create a new element.
26793         var wdiv = editor.wrap.createChild({
26794                 tag: 'div'
26795             }, editor.wrap.dom.firstChild.nextSibling, true);
26796         
26797         // can we do this more than once??
26798         
26799          // stop form submits
26800       
26801  
26802         // disable everything...
26803         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26804         this.toolbars = {};
26805            
26806         for (var i in  ty) {
26807           
26808             this.toolbars[i] = this.buildToolbar(ty[i],i);
26809         }
26810         this.tb = this.toolbars.BODY;
26811         this.tb.el.show();
26812         this.buildFooter();
26813         this.footer.show();
26814         editor.on('hide', function( ) { this.footer.hide() }, this);
26815         editor.on('show', function( ) { this.footer.show() }, this);
26816         
26817          
26818         this.rendered = true;
26819         
26820         // the all the btns;
26821         editor.on('editorevent', this.updateToolbar, this);
26822         // other toolbars need to implement this..
26823         //editor.on('editmodechange', this.updateToolbar, this);
26824     },
26825     
26826     
26827     
26828     /**
26829      * Protected method that will not generally be called directly. It triggers
26830      * a toolbar update by reading the markup state of the current selection in the editor.
26831      */
26832     updateToolbar: function(editor,ev,sel){
26833
26834         //Roo.log(ev);
26835         // capture mouse up - this is handy for selecting images..
26836         // perhaps should go somewhere else...
26837         if(!this.editor.activated){
26838              this.editor.onFirstFocus();
26839             return;
26840         }
26841         
26842         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
26843         // selectNode - might want to handle IE?
26844         if (ev &&
26845             (ev.type == 'mouseup' || ev.type == 'click' ) &&
26846             ev.target && ev.target.tagName == 'IMG') {
26847             // they have click on an image...
26848             // let's see if we can change the selection...
26849             sel = ev.target;
26850          
26851               var nodeRange = sel.ownerDocument.createRange();
26852             try {
26853                 nodeRange.selectNode(sel);
26854             } catch (e) {
26855                 nodeRange.selectNodeContents(sel);
26856             }
26857             //nodeRange.collapse(true);
26858             var s = editor.win.getSelection();
26859             s.removeAllRanges();
26860             s.addRange(nodeRange);
26861         }  
26862         
26863       
26864         var updateFooter = sel ? false : true;
26865         
26866         
26867         var ans = this.editor.getAllAncestors();
26868         
26869         // pick
26870         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
26871         
26872         if (!sel) { 
26873             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
26874             sel = sel ? sel : this.editor.doc.body;
26875             sel = sel.tagName.length ? sel : this.editor.doc.body;
26876             
26877         }
26878         // pick a menu that exists..
26879         var tn = sel.tagName.toUpperCase();
26880         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
26881         
26882         tn = sel.tagName.toUpperCase();
26883         
26884         var lastSel = this.tb.selectedNode
26885         
26886         this.tb.selectedNode = sel;
26887         
26888         // if current menu does not match..
26889         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
26890                 
26891             this.tb.el.hide();
26892             ///console.log("show: " + tn);
26893             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
26894             this.tb.el.show();
26895             // update name
26896             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
26897             
26898             
26899             // update attributes
26900             if (this.tb.fields) {
26901                 this.tb.fields.each(function(e) {
26902                    e.setValue(sel.getAttribute(e.attrname));
26903                 });
26904             }
26905             
26906             var hasStyles = false;
26907             for(var i in this.styles) {
26908                 hasStyles = true;
26909                 break;
26910             }
26911             
26912             // update styles
26913             if (hasStyles) { 
26914                 var st = this.tb.fields.item(0);
26915                 
26916                 st.store.removeAll();
26917                
26918                 
26919                 var cn = sel.className.split(/\s+/);
26920                 
26921                 var avs = [];
26922                 if (this.styles['*']) {
26923                     
26924                     Roo.each(this.styles['*'], function(v) {
26925                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26926                     });
26927                 }
26928                 if (this.styles[tn]) { 
26929                     Roo.each(this.styles[tn], function(v) {
26930                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
26931                     });
26932                 }
26933                 
26934                 st.store.loadData(avs);
26935                 st.collapse();
26936                 st.setValue(cn);
26937             }
26938             // flag our selected Node.
26939             this.tb.selectedNode = sel;
26940            
26941            
26942             Roo.menu.MenuMgr.hideAll();
26943
26944         }
26945         
26946         if (!updateFooter) {
26947             return;
26948         }
26949         // update the footer
26950         //
26951         var html = '';
26952         
26953         this.footerEls = ans.reverse();
26954         Roo.each(this.footerEls, function(a,i) {
26955             if (!a) { return; }
26956             html += html.length ? ' &gt; '  :  '';
26957             
26958             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
26959             
26960         });
26961        
26962         // 
26963         var sz = this.footDisp.up('td').getSize();
26964         this.footDisp.dom.style.width = (sz.width -10) + 'px';
26965         this.footDisp.dom.style.marginLeft = '5px';
26966         
26967         this.footDisp.dom.style.overflow = 'hidden';
26968         
26969         this.footDisp.dom.innerHTML = html;
26970             
26971         //this.editorsyncValue();
26972     },
26973    
26974        
26975     // private
26976     onDestroy : function(){
26977         if(this.rendered){
26978             
26979             this.tb.items.each(function(item){
26980                 if(item.menu){
26981                     item.menu.removeAll();
26982                     if(item.menu.el){
26983                         item.menu.el.destroy();
26984                     }
26985                 }
26986                 item.destroy();
26987             });
26988              
26989         }
26990     },
26991     onFirstFocus: function() {
26992         // need to do this for all the toolbars..
26993         this.tb.items.each(function(item){
26994            item.enable();
26995         });
26996     },
26997     buildToolbar: function(tlist, nm)
26998     {
26999         var editor = this.editor;
27000          // create a new element.
27001         var wdiv = editor.wrap.createChild({
27002                 tag: 'div'
27003             }, editor.wrap.dom.firstChild.nextSibling, true);
27004         
27005        
27006         var tb = new Roo.Toolbar(wdiv);
27007         // add the name..
27008         
27009         tb.add(nm+ ":&nbsp;");
27010         
27011         var styles = [];
27012         for(var i in this.styles) {
27013             styles.push(i);
27014         }
27015         
27016         // styles...
27017         if (styles && styles.length) {
27018             
27019             // this needs a multi-select checkbox...
27020             tb.addField( new Roo.form.ComboBox({
27021                 store: new Roo.data.SimpleStore({
27022                     id : 'val',
27023                     fields: ['val', 'selected'],
27024                     data : [] 
27025                 }),
27026                 name : '-roo-edit-className',
27027                 attrname : 'className',
27028                 displayField:'val',
27029                 typeAhead: false,
27030                 mode: 'local',
27031                 editable : false,
27032                 triggerAction: 'all',
27033                 emptyText:'Select Style',
27034                 selectOnFocus:true,
27035                 width: 130,
27036                 listeners : {
27037                     'select': function(c, r, i) {
27038                         // initial support only for on class per el..
27039                         tb.selectedNode.className =  r ? r.get('val') : '';
27040                         editor.syncValue();
27041                     }
27042                 }
27043     
27044             }));
27045         }
27046             
27047         
27048         
27049         for (var i in tlist) {
27050             
27051             var item = tlist[i];
27052             tb.add(item.title + ":&nbsp;");
27053             
27054             
27055             
27056             
27057             if (item.opts) {
27058                 // opts == pulldown..
27059                 tb.addField( new Roo.form.ComboBox({
27060                     store: new Roo.data.SimpleStore({
27061                         id : 'val',
27062                         fields: ['val'],
27063                         data : item.opts  
27064                     }),
27065                     name : '-roo-edit-' + i,
27066                     attrname : i,
27067                     displayField:'val',
27068                     typeAhead: false,
27069                     mode: 'local',
27070                     editable : false,
27071                     triggerAction: 'all',
27072                     emptyText:'Select',
27073                     selectOnFocus:true,
27074                     width: item.width ? item.width  : 130,
27075                     listeners : {
27076                         'select': function(c, r, i) {
27077                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27078                         }
27079                     }
27080
27081                 }));
27082                 continue;
27083                     
27084                  
27085                 
27086                 tb.addField( new Roo.form.TextField({
27087                     name: i,
27088                     width: 100,
27089                     //allowBlank:false,
27090                     value: ''
27091                 }));
27092                 continue;
27093             }
27094             tb.addField( new Roo.form.TextField({
27095                 name: '-roo-edit-' + i,
27096                 attrname : i,
27097                 
27098                 width: item.width,
27099                 //allowBlank:true,
27100                 value: '',
27101                 listeners: {
27102                     'change' : function(f, nv, ov) {
27103                         tb.selectedNode.setAttribute(f.attrname, nv);
27104                     }
27105                 }
27106             }));
27107              
27108         }
27109         tb.el.on('click', function(e){
27110             e.preventDefault(); // what does this do?
27111         });
27112         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27113         tb.el.hide();
27114         tb.name = nm;
27115         // dont need to disable them... as they will get hidden
27116         return tb;
27117          
27118         
27119     },
27120     buildFooter : function()
27121     {
27122         
27123         var fel = this.editor.wrap.createChild();
27124         this.footer = new Roo.Toolbar(fel);
27125         // toolbar has scrolly on left / right?
27126         var footDisp= new Roo.Toolbar.Fill();
27127         var _t = this;
27128         this.footer.add(
27129             {
27130                 text : '&lt;',
27131                 xtype: 'Button',
27132                 handler : function() {
27133                     _t.footDisp.scrollTo('left',0,true)
27134                 }
27135             }
27136         );
27137         this.footer.add( footDisp );
27138         this.footer.add( 
27139             {
27140                 text : '&gt;',
27141                 xtype: 'Button',
27142                 handler : function() {
27143                     // no animation..
27144                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27145                 }
27146             }
27147         );
27148         var fel = Roo.get(footDisp.el);
27149         fel.addClass('x-editor-context');
27150         this.footDispWrap = fel; 
27151         this.footDispWrap.overflow  = 'hidden';
27152         
27153         this.footDisp = fel.createChild();
27154         this.footDispWrap.on('click', this.onContextClick, this)
27155         
27156         
27157     },
27158     onContextClick : function (ev,dom)
27159     {
27160         ev.preventDefault();
27161         var  cn = dom.className;
27162         Roo.log(cn);
27163         if (!cn.match(/x-ed-loc-/)) {
27164             return;
27165         }
27166         var n = cn.split('-').pop();
27167         var ans = this.footerEls;
27168         var sel = ans[n];
27169         
27170          // pick
27171         var range = this.editor.createRange();
27172         
27173         range.selectNodeContents(sel);
27174         //range.selectNode(sel);
27175         
27176         
27177         var selection = this.editor.getSelection();
27178         selection.removeAllRanges();
27179         selection.addRange(range);
27180         
27181         
27182         
27183         this.updateToolbar(null, null, sel);
27184         
27185         
27186     }
27187     
27188     
27189     
27190     
27191     
27192 });
27193
27194
27195
27196
27197
27198 /*
27199  * Based on:
27200  * Ext JS Library 1.1.1
27201  * Copyright(c) 2006-2007, Ext JS, LLC.
27202  *
27203  * Originally Released Under LGPL - original licence link has changed is not relivant.
27204  *
27205  * Fork - LGPL
27206  * <script type="text/javascript">
27207  */
27208  
27209 /**
27210  * @class Roo.form.BasicForm
27211  * @extends Roo.util.Observable
27212  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27213  * @constructor
27214  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27215  * @param {Object} config Configuration options
27216  */
27217 Roo.form.BasicForm = function(el, config){
27218     this.allItems = [];
27219     this.childForms = [];
27220     Roo.apply(this, config);
27221     /*
27222      * The Roo.form.Field items in this form.
27223      * @type MixedCollection
27224      */
27225      
27226      
27227     this.items = new Roo.util.MixedCollection(false, function(o){
27228         return o.id || (o.id = Roo.id());
27229     });
27230     this.addEvents({
27231         /**
27232          * @event beforeaction
27233          * Fires before any action is performed. Return false to cancel the action.
27234          * @param {Form} this
27235          * @param {Action} action The action to be performed
27236          */
27237         beforeaction: true,
27238         /**
27239          * @event actionfailed
27240          * Fires when an action fails.
27241          * @param {Form} this
27242          * @param {Action} action The action that failed
27243          */
27244         actionfailed : true,
27245         /**
27246          * @event actioncomplete
27247          * Fires when an action is completed.
27248          * @param {Form} this
27249          * @param {Action} action The action that completed
27250          */
27251         actioncomplete : true
27252     });
27253     if(el){
27254         this.initEl(el);
27255     }
27256     Roo.form.BasicForm.superclass.constructor.call(this);
27257 };
27258
27259 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27260     /**
27261      * @cfg {String} method
27262      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27263      */
27264     /**
27265      * @cfg {DataReader} reader
27266      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27267      * This is optional as there is built-in support for processing JSON.
27268      */
27269     /**
27270      * @cfg {DataReader} errorReader
27271      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27272      * This is completely optional as there is built-in support for processing JSON.
27273      */
27274     /**
27275      * @cfg {String} url
27276      * The URL to use for form actions if one isn't supplied in the action options.
27277      */
27278     /**
27279      * @cfg {Boolean} fileUpload
27280      * Set to true if this form is a file upload.
27281      */
27282      
27283     /**
27284      * @cfg {Object} baseParams
27285      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27286      */
27287      /**
27288      
27289     /**
27290      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27291      */
27292     timeout: 30,
27293
27294     // private
27295     activeAction : null,
27296
27297     /**
27298      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27299      * or setValues() data instead of when the form was first created.
27300      */
27301     trackResetOnLoad : false,
27302     
27303     
27304     /**
27305      * childForms - used for multi-tab forms
27306      * @type {Array}
27307      */
27308     childForms : false,
27309     
27310     /**
27311      * allItems - full list of fields.
27312      * @type {Array}
27313      */
27314     allItems : false,
27315     
27316     /**
27317      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27318      * element by passing it or its id or mask the form itself by passing in true.
27319      * @type Mixed
27320      */
27321     waitMsgTarget : false,
27322
27323     // private
27324     initEl : function(el){
27325         this.el = Roo.get(el);
27326         this.id = this.el.id || Roo.id();
27327         this.el.on('submit', this.onSubmit, this);
27328         this.el.addClass('x-form');
27329     },
27330
27331     // private
27332     onSubmit : function(e){
27333         e.stopEvent();
27334     },
27335
27336     /**
27337      * Returns true if client-side validation on the form is successful.
27338      * @return Boolean
27339      */
27340     isValid : function(){
27341         var valid = true;
27342         this.items.each(function(f){
27343            if(!f.validate()){
27344                valid = false;
27345            }
27346         });
27347         return valid;
27348     },
27349
27350     /**
27351      * Returns true if any fields in this form have changed since their original load.
27352      * @return Boolean
27353      */
27354     isDirty : function(){
27355         var dirty = false;
27356         this.items.each(function(f){
27357            if(f.isDirty()){
27358                dirty = true;
27359                return false;
27360            }
27361         });
27362         return dirty;
27363     },
27364
27365     /**
27366      * Performs a predefined action (submit or load) or custom actions you define on this form.
27367      * @param {String} actionName The name of the action type
27368      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27369      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27370      * accept other config options):
27371      * <pre>
27372 Property          Type             Description
27373 ----------------  ---------------  ----------------------------------------------------------------------------------
27374 url               String           The url for the action (defaults to the form's url)
27375 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27376 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27377 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27378                                    validate the form on the client (defaults to false)
27379      * </pre>
27380      * @return {BasicForm} this
27381      */
27382     doAction : function(action, options){
27383         if(typeof action == 'string'){
27384             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27385         }
27386         if(this.fireEvent('beforeaction', this, action) !== false){
27387             this.beforeAction(action);
27388             action.run.defer(100, action);
27389         }
27390         return this;
27391     },
27392
27393     /**
27394      * Shortcut to do a submit action.
27395      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27396      * @return {BasicForm} this
27397      */
27398     submit : function(options){
27399         this.doAction('submit', options);
27400         return this;
27401     },
27402
27403     /**
27404      * Shortcut to do a load action.
27405      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27406      * @return {BasicForm} this
27407      */
27408     load : function(options){
27409         this.doAction('load', options);
27410         return this;
27411     },
27412
27413     /**
27414      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27415      * @param {Record} record The record to edit
27416      * @return {BasicForm} this
27417      */
27418     updateRecord : function(record){
27419         record.beginEdit();
27420         var fs = record.fields;
27421         fs.each(function(f){
27422             var field = this.findField(f.name);
27423             if(field){
27424                 record.set(f.name, field.getValue());
27425             }
27426         }, this);
27427         record.endEdit();
27428         return this;
27429     },
27430
27431     /**
27432      * Loads an Roo.data.Record into this form.
27433      * @param {Record} record The record to load
27434      * @return {BasicForm} this
27435      */
27436     loadRecord : function(record){
27437         this.setValues(record.data);
27438         return this;
27439     },
27440
27441     // private
27442     beforeAction : function(action){
27443         var o = action.options;
27444         
27445        
27446         if(this.waitMsgTarget === true){
27447             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27448         }else if(this.waitMsgTarget){
27449             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27450             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27451         }else {
27452             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27453         }
27454          
27455     },
27456
27457     // private
27458     afterAction : function(action, success){
27459         this.activeAction = null;
27460         var o = action.options;
27461         
27462         if(this.waitMsgTarget === true){
27463             this.el.unmask();
27464         }else if(this.waitMsgTarget){
27465             this.waitMsgTarget.unmask();
27466         }else{
27467             Roo.MessageBox.updateProgress(1);
27468             Roo.MessageBox.hide();
27469         }
27470          
27471         if(success){
27472             if(o.reset){
27473                 this.reset();
27474             }
27475             Roo.callback(o.success, o.scope, [this, action]);
27476             this.fireEvent('actioncomplete', this, action);
27477             
27478         }else{
27479             
27480             // failure condition..
27481             // we have a scenario where updates need confirming.
27482             // eg. if a locking scenario exists..
27483             // we look for { errors : { needs_confirm : true }} in the response.
27484             if (
27485                 (typeof(action.result) != 'undefined')  &&
27486                 (typeof(action.result.errors) != 'undefined')  &&
27487                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27488           ){
27489                 var _t = this;
27490                 Roo.MessageBox.confirm(
27491                     "Change requires confirmation",
27492                     action.result.errorMsg,
27493                     function(r) {
27494                         if (r != 'yes') {
27495                             return;
27496                         }
27497                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27498                     }
27499                     
27500                 );
27501                 
27502                 
27503                 
27504                 return;
27505             }
27506             
27507             Roo.callback(o.failure, o.scope, [this, action]);
27508             // show an error message if no failed handler is set..
27509             if (!this.hasListener('actionfailed')) {
27510                 Roo.MessageBox.alert("Error",
27511                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27512                         action.result.errorMsg :
27513                         "Saving Failed, please check your entries or try again"
27514                 );
27515             }
27516             
27517             this.fireEvent('actionfailed', this, action);
27518         }
27519         
27520     },
27521
27522     /**
27523      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27524      * @param {String} id The value to search for
27525      * @return Field
27526      */
27527     findField : function(id){
27528         var field = this.items.get(id);
27529         if(!field){
27530             this.items.each(function(f){
27531                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27532                     field = f;
27533                     return false;
27534                 }
27535             });
27536         }
27537         return field || null;
27538     },
27539
27540     /**
27541      * Add a secondary form to this one, 
27542      * Used to provide tabbed forms. One form is primary, with hidden values 
27543      * which mirror the elements from the other forms.
27544      * 
27545      * @param {Roo.form.Form} form to add.
27546      * 
27547      */
27548     addForm : function(form)
27549     {
27550        
27551         if (this.childForms.indexOf(form) > -1) {
27552             // already added..
27553             return;
27554         }
27555         this.childForms.push(form);
27556         var n = '';
27557         Roo.each(form.allItems, function (fe) {
27558             
27559             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27560             if (this.findField(n)) { // already added..
27561                 return;
27562             }
27563             var add = new Roo.form.Hidden({
27564                 name : n
27565             });
27566             add.render(this.el);
27567             
27568             this.add( add );
27569         }, this);
27570         
27571     },
27572     /**
27573      * Mark fields in this form invalid in bulk.
27574      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27575      * @return {BasicForm} this
27576      */
27577     markInvalid : function(errors){
27578         if(errors instanceof Array){
27579             for(var i = 0, len = errors.length; i < len; i++){
27580                 var fieldError = errors[i];
27581                 var f = this.findField(fieldError.id);
27582                 if(f){
27583                     f.markInvalid(fieldError.msg);
27584                 }
27585             }
27586         }else{
27587             var field, id;
27588             for(id in errors){
27589                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27590                     field.markInvalid(errors[id]);
27591                 }
27592             }
27593         }
27594         Roo.each(this.childForms || [], function (f) {
27595             f.markInvalid(errors);
27596         });
27597         
27598         return this;
27599     },
27600
27601     /**
27602      * Set values for fields in this form in bulk.
27603      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27604      * @return {BasicForm} this
27605      */
27606     setValues : function(values){
27607         if(values instanceof Array){ // array of objects
27608             for(var i = 0, len = values.length; i < len; i++){
27609                 var v = values[i];
27610                 var f = this.findField(v.id);
27611                 if(f){
27612                     f.setValue(v.value);
27613                     if(this.trackResetOnLoad){
27614                         f.originalValue = f.getValue();
27615                     }
27616                 }
27617             }
27618         }else{ // object hash
27619             var field, id;
27620             for(id in values){
27621                 if(typeof values[id] != 'function' && (field = this.findField(id))){
27622                     
27623                     if (field.setFromData && 
27624                         field.valueField && 
27625                         field.displayField &&
27626                         // combos' with local stores can 
27627                         // be queried via setValue()
27628                         // to set their value..
27629                         (field.store && !field.store.isLocal)
27630                         ) {
27631                         // it's a combo
27632                         var sd = { };
27633                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
27634                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
27635                         field.setFromData(sd);
27636                         
27637                     } else {
27638                         field.setValue(values[id]);
27639                     }
27640                     
27641                     
27642                     if(this.trackResetOnLoad){
27643                         field.originalValue = field.getValue();
27644                     }
27645                 }
27646             }
27647         }
27648          
27649         Roo.each(this.childForms || [], function (f) {
27650             f.setValues(values);
27651         });
27652                 
27653         return this;
27654     },
27655
27656     /**
27657      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
27658      * they are returned as an array.
27659      * @param {Boolean} asString
27660      * @return {Object}
27661      */
27662     getValues : function(asString){
27663         if (this.childForms) {
27664             // copy values from the child forms
27665             Roo.each(this.childForms, function (f) {
27666                 this.setValues(f.getValues());
27667             }, this);
27668         }
27669         
27670         
27671         
27672         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
27673         if(asString === true){
27674             return fs;
27675         }
27676         return Roo.urlDecode(fs);
27677     },
27678     
27679     /**
27680      * Returns the fields in this form as an object with key/value pairs. 
27681      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
27682      * @return {Object}
27683      */
27684     getFieldValues : function(with_hidden)
27685     {
27686         if (this.childForms) {
27687             // copy values from the child forms
27688             // should this call getFieldValues - probably not as we do not currently copy
27689             // hidden fields when we generate..
27690             Roo.each(this.childForms, function (f) {
27691                 this.setValues(f.getValues());
27692             }, this);
27693         }
27694         
27695         var ret = {};
27696         this.items.each(function(f){
27697             if (!f.getName()) {
27698                 return;
27699             }
27700             var v = f.getValue();
27701             // not sure if this supported any more..
27702             if ((typeof(v) == 'object') && f.getRawValue) {
27703                 v = f.getRawValue() ; // dates..
27704             }
27705             // combo boxes where name != hiddenName...
27706             if (f.name != f.getName()) {
27707                 ret[f.name] = f.getRawValue();
27708             }
27709             ret[f.getName()] = v;
27710         });
27711         
27712         return ret;
27713     },
27714
27715     /**
27716      * Clears all invalid messages in this form.
27717      * @return {BasicForm} this
27718      */
27719     clearInvalid : function(){
27720         this.items.each(function(f){
27721            f.clearInvalid();
27722         });
27723         
27724         Roo.each(this.childForms || [], function (f) {
27725             f.clearInvalid();
27726         });
27727         
27728         
27729         return this;
27730     },
27731
27732     /**
27733      * Resets this form.
27734      * @return {BasicForm} this
27735      */
27736     reset : function(){
27737         this.items.each(function(f){
27738             f.reset();
27739         });
27740         
27741         Roo.each(this.childForms || [], function (f) {
27742             f.reset();
27743         });
27744        
27745         
27746         return this;
27747     },
27748
27749     /**
27750      * Add Roo.form components to this form.
27751      * @param {Field} field1
27752      * @param {Field} field2 (optional)
27753      * @param {Field} etc (optional)
27754      * @return {BasicForm} this
27755      */
27756     add : function(){
27757         this.items.addAll(Array.prototype.slice.call(arguments, 0));
27758         return this;
27759     },
27760
27761
27762     /**
27763      * Removes a field from the items collection (does NOT remove its markup).
27764      * @param {Field} field
27765      * @return {BasicForm} this
27766      */
27767     remove : function(field){
27768         this.items.remove(field);
27769         return this;
27770     },
27771
27772     /**
27773      * Looks at the fields in this form, checks them for an id attribute,
27774      * and calls applyTo on the existing dom element with that id.
27775      * @return {BasicForm} this
27776      */
27777     render : function(){
27778         this.items.each(function(f){
27779             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
27780                 f.applyTo(f.id);
27781             }
27782         });
27783         return this;
27784     },
27785
27786     /**
27787      * Calls {@link Ext#apply} for all fields in this form with the passed object.
27788      * @param {Object} values
27789      * @return {BasicForm} this
27790      */
27791     applyToFields : function(o){
27792         this.items.each(function(f){
27793            Roo.apply(f, o);
27794         });
27795         return this;
27796     },
27797
27798     /**
27799      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
27800      * @param {Object} values
27801      * @return {BasicForm} this
27802      */
27803     applyIfToFields : function(o){
27804         this.items.each(function(f){
27805            Roo.applyIf(f, o);
27806         });
27807         return this;
27808     }
27809 });
27810
27811 // back compat
27812 Roo.BasicForm = Roo.form.BasicForm;/*
27813  * Based on:
27814  * Ext JS Library 1.1.1
27815  * Copyright(c) 2006-2007, Ext JS, LLC.
27816  *
27817  * Originally Released Under LGPL - original licence link has changed is not relivant.
27818  *
27819  * Fork - LGPL
27820  * <script type="text/javascript">
27821  */
27822
27823 /**
27824  * @class Roo.form.Form
27825  * @extends Roo.form.BasicForm
27826  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
27827  * @constructor
27828  * @param {Object} config Configuration options
27829  */
27830 Roo.form.Form = function(config){
27831     var xitems =  [];
27832     if (config.items) {
27833         xitems = config.items;
27834         delete config.items;
27835     }
27836    
27837     
27838     Roo.form.Form.superclass.constructor.call(this, null, config);
27839     this.url = this.url || this.action;
27840     if(!this.root){
27841         this.root = new Roo.form.Layout(Roo.applyIf({
27842             id: Roo.id()
27843         }, config));
27844     }
27845     this.active = this.root;
27846     /**
27847      * Array of all the buttons that have been added to this form via {@link addButton}
27848      * @type Array
27849      */
27850     this.buttons = [];
27851     this.allItems = [];
27852     this.addEvents({
27853         /**
27854          * @event clientvalidation
27855          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
27856          * @param {Form} this
27857          * @param {Boolean} valid true if the form has passed client-side validation
27858          */
27859         clientvalidation: true,
27860         /**
27861          * @event rendered
27862          * Fires when the form is rendered
27863          * @param {Roo.form.Form} form
27864          */
27865         rendered : true
27866     });
27867     
27868     if (this.progressUrl) {
27869             // push a hidden field onto the list of fields..
27870             this.addxtype( {
27871                     xns: Roo.form, 
27872                     xtype : 'Hidden', 
27873                     name : 'UPLOAD_IDENTIFIER' 
27874             });
27875         }
27876         
27877     
27878     Roo.each(xitems, this.addxtype, this);
27879     
27880     
27881     
27882 };
27883
27884 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
27885     /**
27886      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
27887      */
27888     /**
27889      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
27890      */
27891     /**
27892      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
27893      */
27894     buttonAlign:'center',
27895
27896     /**
27897      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
27898      */
27899     minButtonWidth:75,
27900
27901     /**
27902      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
27903      * This property cascades to child containers if not set.
27904      */
27905     labelAlign:'left',
27906
27907     /**
27908      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
27909      * fires a looping event with that state. This is required to bind buttons to the valid
27910      * state using the config value formBind:true on the button.
27911      */
27912     monitorValid : false,
27913
27914     /**
27915      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
27916      */
27917     monitorPoll : 200,
27918     
27919     /**
27920      * @cfg {String} progressUrl - Url to return progress data 
27921      */
27922     
27923     progressUrl : false,
27924   
27925     /**
27926      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
27927      * fields are added and the column is closed. If no fields are passed the column remains open
27928      * until end() is called.
27929      * @param {Object} config The config to pass to the column
27930      * @param {Field} field1 (optional)
27931      * @param {Field} field2 (optional)
27932      * @param {Field} etc (optional)
27933      * @return Column The column container object
27934      */
27935     column : function(c){
27936         var col = new Roo.form.Column(c);
27937         this.start(col);
27938         if(arguments.length > 1){ // duplicate code required because of Opera
27939             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27940             this.end();
27941         }
27942         return col;
27943     },
27944
27945     /**
27946      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
27947      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
27948      * until end() is called.
27949      * @param {Object} config The config to pass to the fieldset
27950      * @param {Field} field1 (optional)
27951      * @param {Field} field2 (optional)
27952      * @param {Field} etc (optional)
27953      * @return FieldSet The fieldset container object
27954      */
27955     fieldset : function(c){
27956         var fs = new Roo.form.FieldSet(c);
27957         this.start(fs);
27958         if(arguments.length > 1){ // duplicate code required because of Opera
27959             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27960             this.end();
27961         }
27962         return fs;
27963     },
27964
27965     /**
27966      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
27967      * fields are added and the container is closed. If no fields are passed the container remains open
27968      * until end() is called.
27969      * @param {Object} config The config to pass to the Layout
27970      * @param {Field} field1 (optional)
27971      * @param {Field} field2 (optional)
27972      * @param {Field} etc (optional)
27973      * @return Layout The container object
27974      */
27975     container : function(c){
27976         var l = new Roo.form.Layout(c);
27977         this.start(l);
27978         if(arguments.length > 1){ // duplicate code required because of Opera
27979             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
27980             this.end();
27981         }
27982         return l;
27983     },
27984
27985     /**
27986      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
27987      * @param {Object} container A Roo.form.Layout or subclass of Layout
27988      * @return {Form} this
27989      */
27990     start : function(c){
27991         // cascade label info
27992         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
27993         this.active.stack.push(c);
27994         c.ownerCt = this.active;
27995         this.active = c;
27996         return this;
27997     },
27998
27999     /**
28000      * Closes the current open container
28001      * @return {Form} this
28002      */
28003     end : function(){
28004         if(this.active == this.root){
28005             return this;
28006         }
28007         this.active = this.active.ownerCt;
28008         return this;
28009     },
28010
28011     /**
28012      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28013      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28014      * as the label of the field.
28015      * @param {Field} field1
28016      * @param {Field} field2 (optional)
28017      * @param {Field} etc. (optional)
28018      * @return {Form} this
28019      */
28020     add : function(){
28021         this.active.stack.push.apply(this.active.stack, arguments);
28022         this.allItems.push.apply(this.allItems,arguments);
28023         var r = [];
28024         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28025             if(a[i].isFormField){
28026                 r.push(a[i]);
28027             }
28028         }
28029         if(r.length > 0){
28030             Roo.form.Form.superclass.add.apply(this, r);
28031         }
28032         return this;
28033     },
28034     
28035
28036     
28037     
28038     
28039      /**
28040      * Find any element that has been added to a form, using it's ID or name
28041      * This can include framesets, columns etc. along with regular fields..
28042      * @param {String} id - id or name to find.
28043      
28044      * @return {Element} e - or false if nothing found.
28045      */
28046     findbyId : function(id)
28047     {
28048         var ret = false;
28049         if (!id) {
28050             return ret;
28051         }
28052         Roo.each(this.allItems, function(f){
28053             if (f.id == id || f.name == id ){
28054                 ret = f;
28055                 return false;
28056             }
28057         });
28058         return ret;
28059     },
28060
28061     
28062     
28063     /**
28064      * Render this form into the passed container. This should only be called once!
28065      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28066      * @return {Form} this
28067      */
28068     render : function(ct)
28069     {
28070         
28071         
28072         
28073         ct = Roo.get(ct);
28074         var o = this.autoCreate || {
28075             tag: 'form',
28076             method : this.method || 'POST',
28077             id : this.id || Roo.id()
28078         };
28079         this.initEl(ct.createChild(o));
28080
28081         this.root.render(this.el);
28082         
28083        
28084              
28085         this.items.each(function(f){
28086             f.render('x-form-el-'+f.id);
28087         });
28088
28089         if(this.buttons.length > 0){
28090             // tables are required to maintain order and for correct IE layout
28091             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28092                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28093                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28094             }}, null, true);
28095             var tr = tb.getElementsByTagName('tr')[0];
28096             for(var i = 0, len = this.buttons.length; i < len; i++) {
28097                 var b = this.buttons[i];
28098                 var td = document.createElement('td');
28099                 td.className = 'x-form-btn-td';
28100                 b.render(tr.appendChild(td));
28101             }
28102         }
28103         if(this.monitorValid){ // initialize after render
28104             this.startMonitoring();
28105         }
28106         this.fireEvent('rendered', this);
28107         return this;
28108     },
28109
28110     /**
28111      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28112      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28113      * object or a valid Roo.DomHelper element config
28114      * @param {Function} handler The function called when the button is clicked
28115      * @param {Object} scope (optional) The scope of the handler function
28116      * @return {Roo.Button}
28117      */
28118     addButton : function(config, handler, scope){
28119         var bc = {
28120             handler: handler,
28121             scope: scope,
28122             minWidth: this.minButtonWidth,
28123             hideParent:true
28124         };
28125         if(typeof config == "string"){
28126             bc.text = config;
28127         }else{
28128             Roo.apply(bc, config);
28129         }
28130         var btn = new Roo.Button(null, bc);
28131         this.buttons.push(btn);
28132         return btn;
28133     },
28134
28135      /**
28136      * Adds a series of form elements (using the xtype property as the factory method.
28137      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28138      * @param {Object} config 
28139      */
28140     
28141     addxtype : function()
28142     {
28143         var ar = Array.prototype.slice.call(arguments, 0);
28144         var ret = false;
28145         for(var i = 0; i < ar.length; i++) {
28146             if (!ar[i]) {
28147                 continue; // skip -- if this happends something invalid got sent, we 
28148                 // should ignore it, as basically that interface element will not show up
28149                 // and that should be pretty obvious!!
28150             }
28151             
28152             if (Roo.form[ar[i].xtype]) {
28153                 ar[i].form = this;
28154                 var fe = Roo.factory(ar[i], Roo.form);
28155                 if (!ret) {
28156                     ret = fe;
28157                 }
28158                 fe.form = this;
28159                 if (fe.store) {
28160                     fe.store.form = this;
28161                 }
28162                 if (fe.isLayout) {  
28163                          
28164                     this.start(fe);
28165                     this.allItems.push(fe);
28166                     if (fe.items && fe.addxtype) {
28167                         fe.addxtype.apply(fe, fe.items);
28168                         delete fe.items;
28169                     }
28170                      this.end();
28171                     continue;
28172                 }
28173                 
28174                 
28175                  
28176                 this.add(fe);
28177               //  console.log('adding ' + ar[i].xtype);
28178             }
28179             if (ar[i].xtype == 'Button') {  
28180                 //console.log('adding button');
28181                 //console.log(ar[i]);
28182                 this.addButton(ar[i]);
28183                 this.allItems.push(fe);
28184                 continue;
28185             }
28186             
28187             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28188                 alert('end is not supported on xtype any more, use items');
28189             //    this.end();
28190             //    //console.log('adding end');
28191             }
28192             
28193         }
28194         return ret;
28195     },
28196     
28197     /**
28198      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28199      * option "monitorValid"
28200      */
28201     startMonitoring : function(){
28202         if(!this.bound){
28203             this.bound = true;
28204             Roo.TaskMgr.start({
28205                 run : this.bindHandler,
28206                 interval : this.monitorPoll || 200,
28207                 scope: this
28208             });
28209         }
28210     },
28211
28212     /**
28213      * Stops monitoring of the valid state of this form
28214      */
28215     stopMonitoring : function(){
28216         this.bound = false;
28217     },
28218
28219     // private
28220     bindHandler : function(){
28221         if(!this.bound){
28222             return false; // stops binding
28223         }
28224         var valid = true;
28225         this.items.each(function(f){
28226             if(!f.isValid(true)){
28227                 valid = false;
28228                 return false;
28229             }
28230         });
28231         for(var i = 0, len = this.buttons.length; i < len; i++){
28232             var btn = this.buttons[i];
28233             if(btn.formBind === true && btn.disabled === valid){
28234                 btn.setDisabled(!valid);
28235             }
28236         }
28237         this.fireEvent('clientvalidation', this, valid);
28238     }
28239     
28240     
28241     
28242     
28243     
28244     
28245     
28246     
28247 });
28248
28249
28250 // back compat
28251 Roo.Form = Roo.form.Form;
28252 /*
28253  * Based on:
28254  * Ext JS Library 1.1.1
28255  * Copyright(c) 2006-2007, Ext JS, LLC.
28256  *
28257  * Originally Released Under LGPL - original licence link has changed is not relivant.
28258  *
28259  * Fork - LGPL
28260  * <script type="text/javascript">
28261  */
28262  
28263  /**
28264  * @class Roo.form.Action
28265  * Internal Class used to handle form actions
28266  * @constructor
28267  * @param {Roo.form.BasicForm} el The form element or its id
28268  * @param {Object} config Configuration options
28269  */
28270  
28271  
28272 // define the action interface
28273 Roo.form.Action = function(form, options){
28274     this.form = form;
28275     this.options = options || {};
28276 };
28277 /**
28278  * Client Validation Failed
28279  * @const 
28280  */
28281 Roo.form.Action.CLIENT_INVALID = 'client';
28282 /**
28283  * Server Validation Failed
28284  * @const 
28285  */
28286  Roo.form.Action.SERVER_INVALID = 'server';
28287  /**
28288  * Connect to Server Failed
28289  * @const 
28290  */
28291 Roo.form.Action.CONNECT_FAILURE = 'connect';
28292 /**
28293  * Reading Data from Server Failed
28294  * @const 
28295  */
28296 Roo.form.Action.LOAD_FAILURE = 'load';
28297
28298 Roo.form.Action.prototype = {
28299     type : 'default',
28300     failureType : undefined,
28301     response : undefined,
28302     result : undefined,
28303
28304     // interface method
28305     run : function(options){
28306
28307     },
28308
28309     // interface method
28310     success : function(response){
28311
28312     },
28313
28314     // interface method
28315     handleResponse : function(response){
28316
28317     },
28318
28319     // default connection failure
28320     failure : function(response){
28321         
28322         this.response = response;
28323         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28324         this.form.afterAction(this, false);
28325     },
28326
28327     processResponse : function(response){
28328         this.response = response;
28329         if(!response.responseText){
28330             return true;
28331         }
28332         this.result = this.handleResponse(response);
28333         return this.result;
28334     },
28335
28336     // utility functions used internally
28337     getUrl : function(appendParams){
28338         var url = this.options.url || this.form.url || this.form.el.dom.action;
28339         if(appendParams){
28340             var p = this.getParams();
28341             if(p){
28342                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28343             }
28344         }
28345         return url;
28346     },
28347
28348     getMethod : function(){
28349         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28350     },
28351
28352     getParams : function(){
28353         var bp = this.form.baseParams;
28354         var p = this.options.params;
28355         if(p){
28356             if(typeof p == "object"){
28357                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28358             }else if(typeof p == 'string' && bp){
28359                 p += '&' + Roo.urlEncode(bp);
28360             }
28361         }else if(bp){
28362             p = Roo.urlEncode(bp);
28363         }
28364         return p;
28365     },
28366
28367     createCallback : function(){
28368         return {
28369             success: this.success,
28370             failure: this.failure,
28371             scope: this,
28372             timeout: (this.form.timeout*1000),
28373             upload: this.form.fileUpload ? this.success : undefined
28374         };
28375     }
28376 };
28377
28378 Roo.form.Action.Submit = function(form, options){
28379     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28380 };
28381
28382 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28383     type : 'submit',
28384
28385     haveProgress : false,
28386     uploadComplete : false,
28387     
28388     // uploadProgress indicator.
28389     uploadProgress : function()
28390     {
28391         if (!this.form.progressUrl) {
28392             return;
28393         }
28394         
28395         if (!this.haveProgress) {
28396             Roo.MessageBox.progress("Uploading", "Uploading");
28397         }
28398         if (this.uploadComplete) {
28399            Roo.MessageBox.hide();
28400            return;
28401         }
28402         
28403         this.haveProgress = true;
28404    
28405         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28406         
28407         var c = new Roo.data.Connection();
28408         c.request({
28409             url : this.form.progressUrl,
28410             params: {
28411                 id : uid
28412             },
28413             method: 'GET',
28414             success : function(req){
28415                //console.log(data);
28416                 var rdata = false;
28417                 var edata;
28418                 try  {
28419                    rdata = Roo.decode(req.responseText)
28420                 } catch (e) {
28421                     Roo.log("Invalid data from server..");
28422                     Roo.log(edata);
28423                     return;
28424                 }
28425                 if (!rdata || !rdata.success) {
28426                     Roo.log(rdata);
28427                     return;
28428                 }
28429                 var data = rdata.data;
28430                 
28431                 if (this.uploadComplete) {
28432                    Roo.MessageBox.hide();
28433                    return;
28434                 }
28435                    
28436                 if (data){
28437                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28438                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28439                     );
28440                 }
28441                 this.uploadProgress.defer(2000,this);
28442             },
28443        
28444             failure: function(data) {
28445                 Roo.log('progress url failed ');
28446                 Roo.log(data);
28447             },
28448             scope : this
28449         });
28450            
28451     },
28452     
28453     
28454     run : function()
28455     {
28456         // run get Values on the form, so it syncs any secondary forms.
28457         this.form.getValues();
28458         
28459         var o = this.options;
28460         var method = this.getMethod();
28461         var isPost = method == 'POST';
28462         if(o.clientValidation === false || this.form.isValid()){
28463             
28464             if (this.form.progressUrl) {
28465                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28466                     (new Date() * 1) + '' + Math.random());
28467                     
28468             } 
28469             
28470             
28471             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28472                 form:this.form.el.dom,
28473                 url:this.getUrl(!isPost),
28474                 method: method,
28475                 params:isPost ? this.getParams() : null,
28476                 isUpload: this.form.fileUpload
28477             }));
28478             
28479             this.uploadProgress();
28480
28481         }else if (o.clientValidation !== false){ // client validation failed
28482             this.failureType = Roo.form.Action.CLIENT_INVALID;
28483             this.form.afterAction(this, false);
28484         }
28485     },
28486
28487     success : function(response)
28488     {
28489         this.uploadComplete= true;
28490         if (this.haveProgress) {
28491             Roo.MessageBox.hide();
28492         }
28493         
28494         
28495         var result = this.processResponse(response);
28496         if(result === true || result.success){
28497             this.form.afterAction(this, true);
28498             return;
28499         }
28500         if(result.errors){
28501             this.form.markInvalid(result.errors);
28502             this.failureType = Roo.form.Action.SERVER_INVALID;
28503         }
28504         this.form.afterAction(this, false);
28505     },
28506     failure : function(response)
28507     {
28508         this.uploadComplete= true;
28509         if (this.haveProgress) {
28510             Roo.MessageBox.hide();
28511         }
28512         
28513         this.response = response;
28514         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28515         this.form.afterAction(this, false);
28516     },
28517     
28518     handleResponse : function(response){
28519         if(this.form.errorReader){
28520             var rs = this.form.errorReader.read(response);
28521             var errors = [];
28522             if(rs.records){
28523                 for(var i = 0, len = rs.records.length; i < len; i++) {
28524                     var r = rs.records[i];
28525                     errors[i] = r.data;
28526                 }
28527             }
28528             if(errors.length < 1){
28529                 errors = null;
28530             }
28531             return {
28532                 success : rs.success,
28533                 errors : errors
28534             };
28535         }
28536         var ret = false;
28537         try {
28538             ret = Roo.decode(response.responseText);
28539         } catch (e) {
28540             ret = {
28541                 success: false,
28542                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28543                 errors : []
28544             };
28545         }
28546         return ret;
28547         
28548     }
28549 });
28550
28551
28552 Roo.form.Action.Load = function(form, options){
28553     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28554     this.reader = this.form.reader;
28555 };
28556
28557 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28558     type : 'load',
28559
28560     run : function(){
28561         
28562         Roo.Ajax.request(Roo.apply(
28563                 this.createCallback(), {
28564                     method:this.getMethod(),
28565                     url:this.getUrl(false),
28566                     params:this.getParams()
28567         }));
28568     },
28569
28570     success : function(response){
28571         
28572         var result = this.processResponse(response);
28573         if(result === true || !result.success || !result.data){
28574             this.failureType = Roo.form.Action.LOAD_FAILURE;
28575             this.form.afterAction(this, false);
28576             return;
28577         }
28578         this.form.clearInvalid();
28579         this.form.setValues(result.data);
28580         this.form.afterAction(this, true);
28581     },
28582
28583     handleResponse : function(response){
28584         if(this.form.reader){
28585             var rs = this.form.reader.read(response);
28586             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28587             return {
28588                 success : rs.success,
28589                 data : data
28590             };
28591         }
28592         return Roo.decode(response.responseText);
28593     }
28594 });
28595
28596 Roo.form.Action.ACTION_TYPES = {
28597     'load' : Roo.form.Action.Load,
28598     'submit' : Roo.form.Action.Submit
28599 };/*
28600  * Based on:
28601  * Ext JS Library 1.1.1
28602  * Copyright(c) 2006-2007, Ext JS, LLC.
28603  *
28604  * Originally Released Under LGPL - original licence link has changed is not relivant.
28605  *
28606  * Fork - LGPL
28607  * <script type="text/javascript">
28608  */
28609  
28610 /**
28611  * @class Roo.form.Layout
28612  * @extends Roo.Component
28613  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
28614  * @constructor
28615  * @param {Object} config Configuration options
28616  */
28617 Roo.form.Layout = function(config){
28618     var xitems = [];
28619     if (config.items) {
28620         xitems = config.items;
28621         delete config.items;
28622     }
28623     Roo.form.Layout.superclass.constructor.call(this, config);
28624     this.stack = [];
28625     Roo.each(xitems, this.addxtype, this);
28626      
28627 };
28628
28629 Roo.extend(Roo.form.Layout, Roo.Component, {
28630     /**
28631      * @cfg {String/Object} autoCreate
28632      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
28633      */
28634     /**
28635      * @cfg {String/Object/Function} style
28636      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
28637      * a function which returns such a specification.
28638      */
28639     /**
28640      * @cfg {String} labelAlign
28641      * Valid values are "left," "top" and "right" (defaults to "left")
28642      */
28643     /**
28644      * @cfg {Number} labelWidth
28645      * Fixed width in pixels of all field labels (defaults to undefined)
28646      */
28647     /**
28648      * @cfg {Boolean} clear
28649      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
28650      */
28651     clear : true,
28652     /**
28653      * @cfg {String} labelSeparator
28654      * The separator to use after field labels (defaults to ':')
28655      */
28656     labelSeparator : ':',
28657     /**
28658      * @cfg {Boolean} hideLabels
28659      * True to suppress the display of field labels in this layout (defaults to false)
28660      */
28661     hideLabels : false,
28662
28663     // private
28664     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
28665     
28666     isLayout : true,
28667     
28668     // private
28669     onRender : function(ct, position){
28670         if(this.el){ // from markup
28671             this.el = Roo.get(this.el);
28672         }else {  // generate
28673             var cfg = this.getAutoCreate();
28674             this.el = ct.createChild(cfg, position);
28675         }
28676         if(this.style){
28677             this.el.applyStyles(this.style);
28678         }
28679         if(this.labelAlign){
28680             this.el.addClass('x-form-label-'+this.labelAlign);
28681         }
28682         if(this.hideLabels){
28683             this.labelStyle = "display:none";
28684             this.elementStyle = "padding-left:0;";
28685         }else{
28686             if(typeof this.labelWidth == 'number'){
28687                 this.labelStyle = "width:"+this.labelWidth+"px;";
28688                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
28689             }
28690             if(this.labelAlign == 'top'){
28691                 this.labelStyle = "width:auto;";
28692                 this.elementStyle = "padding-left:0;";
28693             }
28694         }
28695         var stack = this.stack;
28696         var slen = stack.length;
28697         if(slen > 0){
28698             if(!this.fieldTpl){
28699                 var t = new Roo.Template(
28700                     '<div class="x-form-item {5}">',
28701                         '<label for="{0}" style="{2}">{1}{4}</label>',
28702                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28703                         '</div>',
28704                     '</div><div class="x-form-clear-left"></div>'
28705                 );
28706                 t.disableFormats = true;
28707                 t.compile();
28708                 Roo.form.Layout.prototype.fieldTpl = t;
28709             }
28710             for(var i = 0; i < slen; i++) {
28711                 if(stack[i].isFormField){
28712                     this.renderField(stack[i]);
28713                 }else{
28714                     this.renderComponent(stack[i]);
28715                 }
28716             }
28717         }
28718         if(this.clear){
28719             this.el.createChild({cls:'x-form-clear'});
28720         }
28721     },
28722
28723     // private
28724     renderField : function(f){
28725         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
28726                f.id, //0
28727                f.fieldLabel, //1
28728                f.labelStyle||this.labelStyle||'', //2
28729                this.elementStyle||'', //3
28730                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
28731                f.itemCls||this.itemCls||''  //5
28732        ], true).getPrevSibling());
28733     },
28734
28735     // private
28736     renderComponent : function(c){
28737         c.render(c.isLayout ? this.el : this.el.createChild());    
28738     },
28739     /**
28740      * Adds a object form elements (using the xtype property as the factory method.)
28741      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
28742      * @param {Object} config 
28743      */
28744     addxtype : function(o)
28745     {
28746         // create the lement.
28747         o.form = this.form;
28748         var fe = Roo.factory(o, Roo.form);
28749         this.form.allItems.push(fe);
28750         this.stack.push(fe);
28751         
28752         if (fe.isFormField) {
28753             this.form.items.add(fe);
28754         }
28755          
28756         return fe;
28757     }
28758 });
28759
28760 /**
28761  * @class Roo.form.Column
28762  * @extends Roo.form.Layout
28763  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
28764  * @constructor
28765  * @param {Object} config Configuration options
28766  */
28767 Roo.form.Column = function(config){
28768     Roo.form.Column.superclass.constructor.call(this, config);
28769 };
28770
28771 Roo.extend(Roo.form.Column, Roo.form.Layout, {
28772     /**
28773      * @cfg {Number/String} width
28774      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28775      */
28776     /**
28777      * @cfg {String/Object} autoCreate
28778      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
28779      */
28780
28781     // private
28782     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
28783
28784     // private
28785     onRender : function(ct, position){
28786         Roo.form.Column.superclass.onRender.call(this, ct, position);
28787         if(this.width){
28788             this.el.setWidth(this.width);
28789         }
28790     }
28791 });
28792
28793
28794 /**
28795  * @class Roo.form.Row
28796  * @extends Roo.form.Layout
28797  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
28798  * @constructor
28799  * @param {Object} config Configuration options
28800  */
28801
28802  
28803 Roo.form.Row = function(config){
28804     Roo.form.Row.superclass.constructor.call(this, config);
28805 };
28806  
28807 Roo.extend(Roo.form.Row, Roo.form.Layout, {
28808       /**
28809      * @cfg {Number/String} width
28810      * The fixed width of the column in pixels or CSS value (defaults to "auto")
28811      */
28812     /**
28813      * @cfg {Number/String} height
28814      * The fixed height of the column in pixels or CSS value (defaults to "auto")
28815      */
28816     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
28817     
28818     padWidth : 20,
28819     // private
28820     onRender : function(ct, position){
28821         //console.log('row render');
28822         if(!this.rowTpl){
28823             var t = new Roo.Template(
28824                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
28825                     '<label for="{0}" style="{2}">{1}{4}</label>',
28826                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
28827                     '</div>',
28828                 '</div>'
28829             );
28830             t.disableFormats = true;
28831             t.compile();
28832             Roo.form.Layout.prototype.rowTpl = t;
28833         }
28834         this.fieldTpl = this.rowTpl;
28835         
28836         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
28837         var labelWidth = 100;
28838         
28839         if ((this.labelAlign != 'top')) {
28840             if (typeof this.labelWidth == 'number') {
28841                 labelWidth = this.labelWidth
28842             }
28843             this.padWidth =  20 + labelWidth;
28844             
28845         }
28846         
28847         Roo.form.Column.superclass.onRender.call(this, ct, position);
28848         if(this.width){
28849             this.el.setWidth(this.width);
28850         }
28851         if(this.height){
28852             this.el.setHeight(this.height);
28853         }
28854     },
28855     
28856     // private
28857     renderField : function(f){
28858         f.fieldEl = this.fieldTpl.append(this.el, [
28859                f.id, f.fieldLabel,
28860                f.labelStyle||this.labelStyle||'',
28861                this.elementStyle||'',
28862                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
28863                f.itemCls||this.itemCls||'',
28864                f.width ? f.width + this.padWidth : 160 + this.padWidth
28865        ],true);
28866     }
28867 });
28868  
28869
28870 /**
28871  * @class Roo.form.FieldSet
28872  * @extends Roo.form.Layout
28873  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
28874  * @constructor
28875  * @param {Object} config Configuration options
28876  */
28877 Roo.form.FieldSet = function(config){
28878     Roo.form.FieldSet.superclass.constructor.call(this, config);
28879 };
28880
28881 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
28882     /**
28883      * @cfg {String} legend
28884      * The text to display as the legend for the FieldSet (defaults to '')
28885      */
28886     /**
28887      * @cfg {String/Object} autoCreate
28888      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
28889      */
28890
28891     // private
28892     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
28893
28894     // private
28895     onRender : function(ct, position){
28896         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
28897         if(this.legend){
28898             this.setLegend(this.legend);
28899         }
28900     },
28901
28902     // private
28903     setLegend : function(text){
28904         if(this.rendered){
28905             this.el.child('legend').update(text);
28906         }
28907     }
28908 });/*
28909  * Based on:
28910  * Ext JS Library 1.1.1
28911  * Copyright(c) 2006-2007, Ext JS, LLC.
28912  *
28913  * Originally Released Under LGPL - original licence link has changed is not relivant.
28914  *
28915  * Fork - LGPL
28916  * <script type="text/javascript">
28917  */
28918 /**
28919  * @class Roo.form.VTypes
28920  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
28921  * @singleton
28922  */
28923 Roo.form.VTypes = function(){
28924     // closure these in so they are only created once.
28925     var alpha = /^[a-zA-Z_]+$/;
28926     var alphanum = /^[a-zA-Z0-9_]+$/;
28927     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
28928     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
28929
28930     // All these messages and functions are configurable
28931     return {
28932         /**
28933          * The function used to validate email addresses
28934          * @param {String} value The email address
28935          */
28936         'email' : function(v){
28937             return email.test(v);
28938         },
28939         /**
28940          * The error text to display when the email validation function returns false
28941          * @type String
28942          */
28943         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
28944         /**
28945          * The keystroke filter mask to be applied on email input
28946          * @type RegExp
28947          */
28948         'emailMask' : /[a-z0-9_\.\-@]/i,
28949
28950         /**
28951          * The function used to validate URLs
28952          * @param {String} value The URL
28953          */
28954         'url' : function(v){
28955             return url.test(v);
28956         },
28957         /**
28958          * The error text to display when the url validation function returns false
28959          * @type String
28960          */
28961         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
28962         
28963         /**
28964          * The function used to validate alpha values
28965          * @param {String} value The value
28966          */
28967         'alpha' : function(v){
28968             return alpha.test(v);
28969         },
28970         /**
28971          * The error text to display when the alpha validation function returns false
28972          * @type String
28973          */
28974         'alphaText' : 'This field should only contain letters and _',
28975         /**
28976          * The keystroke filter mask to be applied on alpha input
28977          * @type RegExp
28978          */
28979         'alphaMask' : /[a-z_]/i,
28980
28981         /**
28982          * The function used to validate alphanumeric values
28983          * @param {String} value The value
28984          */
28985         'alphanum' : function(v){
28986             return alphanum.test(v);
28987         },
28988         /**
28989          * The error text to display when the alphanumeric validation function returns false
28990          * @type String
28991          */
28992         'alphanumText' : 'This field should only contain letters, numbers and _',
28993         /**
28994          * The keystroke filter mask to be applied on alphanumeric input
28995          * @type RegExp
28996          */
28997         'alphanumMask' : /[a-z0-9_]/i
28998     };
28999 }();//<script type="text/javascript">
29000
29001 /**
29002  * @class Roo.form.FCKeditor
29003  * @extends Roo.form.TextArea
29004  * Wrapper around the FCKEditor http://www.fckeditor.net
29005  * @constructor
29006  * Creates a new FCKeditor
29007  * @param {Object} config Configuration options
29008  */
29009 Roo.form.FCKeditor = function(config){
29010     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29011     this.addEvents({
29012          /**
29013          * @event editorinit
29014          * Fired when the editor is initialized - you can add extra handlers here..
29015          * @param {FCKeditor} this
29016          * @param {Object} the FCK object.
29017          */
29018         editorinit : true
29019     });
29020     
29021     
29022 };
29023 Roo.form.FCKeditor.editors = { };
29024 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29025 {
29026     //defaultAutoCreate : {
29027     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29028     //},
29029     // private
29030     /**
29031      * @cfg {Object} fck options - see fck manual for details.
29032      */
29033     fckconfig : false,
29034     
29035     /**
29036      * @cfg {Object} fck toolbar set (Basic or Default)
29037      */
29038     toolbarSet : 'Basic',
29039     /**
29040      * @cfg {Object} fck BasePath
29041      */ 
29042     basePath : '/fckeditor/',
29043     
29044     
29045     frame : false,
29046     
29047     value : '',
29048     
29049    
29050     onRender : function(ct, position)
29051     {
29052         if(!this.el){
29053             this.defaultAutoCreate = {
29054                 tag: "textarea",
29055                 style:"width:300px;height:60px;",
29056                 autocomplete: "off"
29057             };
29058         }
29059         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29060         /*
29061         if(this.grow){
29062             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29063             if(this.preventScrollbars){
29064                 this.el.setStyle("overflow", "hidden");
29065             }
29066             this.el.setHeight(this.growMin);
29067         }
29068         */
29069         //console.log('onrender' + this.getId() );
29070         Roo.form.FCKeditor.editors[this.getId()] = this;
29071          
29072
29073         this.replaceTextarea() ;
29074         
29075     },
29076     
29077     getEditor : function() {
29078         return this.fckEditor;
29079     },
29080     /**
29081      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29082      * @param {Mixed} value The value to set
29083      */
29084     
29085     
29086     setValue : function(value)
29087     {
29088         //console.log('setValue: ' + value);
29089         
29090         if(typeof(value) == 'undefined') { // not sure why this is happending...
29091             return;
29092         }
29093         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29094         
29095         //if(!this.el || !this.getEditor()) {
29096         //    this.value = value;
29097             //this.setValue.defer(100,this,[value]);    
29098         //    return;
29099         //} 
29100         
29101         if(!this.getEditor()) {
29102             return;
29103         }
29104         
29105         this.getEditor().SetData(value);
29106         
29107         //
29108
29109     },
29110
29111     /**
29112      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29113      * @return {Mixed} value The field value
29114      */
29115     getValue : function()
29116     {
29117         
29118         if (this.frame && this.frame.dom.style.display == 'none') {
29119             return Roo.form.FCKeditor.superclass.getValue.call(this);
29120         }
29121         
29122         if(!this.el || !this.getEditor()) {
29123            
29124            // this.getValue.defer(100,this); 
29125             return this.value;
29126         }
29127        
29128         
29129         var value=this.getEditor().GetData();
29130         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29131         return Roo.form.FCKeditor.superclass.getValue.call(this);
29132         
29133
29134     },
29135
29136     /**
29137      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29138      * @return {Mixed} value The field value
29139      */
29140     getRawValue : function()
29141     {
29142         if (this.frame && this.frame.dom.style.display == 'none') {
29143             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29144         }
29145         
29146         if(!this.el || !this.getEditor()) {
29147             //this.getRawValue.defer(100,this); 
29148             return this.value;
29149             return;
29150         }
29151         
29152         
29153         
29154         var value=this.getEditor().GetData();
29155         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29156         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29157          
29158     },
29159     
29160     setSize : function(w,h) {
29161         
29162         
29163         
29164         //if (this.frame && this.frame.dom.style.display == 'none') {
29165         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29166         //    return;
29167         //}
29168         //if(!this.el || !this.getEditor()) {
29169         //    this.setSize.defer(100,this, [w,h]); 
29170         //    return;
29171         //}
29172         
29173         
29174         
29175         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29176         
29177         this.frame.dom.setAttribute('width', w);
29178         this.frame.dom.setAttribute('height', h);
29179         this.frame.setSize(w,h);
29180         
29181     },
29182     
29183     toggleSourceEdit : function(value) {
29184         
29185       
29186          
29187         this.el.dom.style.display = value ? '' : 'none';
29188         this.frame.dom.style.display = value ?  'none' : '';
29189         
29190     },
29191     
29192     
29193     focus: function(tag)
29194     {
29195         if (this.frame.dom.style.display == 'none') {
29196             return Roo.form.FCKeditor.superclass.focus.call(this);
29197         }
29198         if(!this.el || !this.getEditor()) {
29199             this.focus.defer(100,this, [tag]); 
29200             return;
29201         }
29202         
29203         
29204         
29205         
29206         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29207         this.getEditor().Focus();
29208         if (tgs.length) {
29209             if (!this.getEditor().Selection.GetSelection()) {
29210                 this.focus.defer(100,this, [tag]); 
29211                 return;
29212             }
29213             
29214             
29215             var r = this.getEditor().EditorDocument.createRange();
29216             r.setStart(tgs[0],0);
29217             r.setEnd(tgs[0],0);
29218             this.getEditor().Selection.GetSelection().removeAllRanges();
29219             this.getEditor().Selection.GetSelection().addRange(r);
29220             this.getEditor().Focus();
29221         }
29222         
29223     },
29224     
29225     
29226     
29227     replaceTextarea : function()
29228     {
29229         if ( document.getElementById( this.getId() + '___Frame' ) )
29230             return ;
29231         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29232         //{
29233             // We must check the elements firstly using the Id and then the name.
29234         var oTextarea = document.getElementById( this.getId() );
29235         
29236         var colElementsByName = document.getElementsByName( this.getId() ) ;
29237          
29238         oTextarea.style.display = 'none' ;
29239
29240         if ( oTextarea.tabIndex ) {            
29241             this.TabIndex = oTextarea.tabIndex ;
29242         }
29243         
29244         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29245         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29246         this.frame = Roo.get(this.getId() + '___Frame')
29247     },
29248     
29249     _getConfigHtml : function()
29250     {
29251         var sConfig = '' ;
29252
29253         for ( var o in this.fckconfig ) {
29254             sConfig += sConfig.length > 0  ? '&amp;' : '';
29255             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29256         }
29257
29258         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29259     },
29260     
29261     
29262     _getIFrameHtml : function()
29263     {
29264         var sFile = 'fckeditor.html' ;
29265         /* no idea what this is about..
29266         try
29267         {
29268             if ( (/fcksource=true/i).test( window.top.location.search ) )
29269                 sFile = 'fckeditor.original.html' ;
29270         }
29271         catch (e) { 
29272         */
29273
29274         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29275         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29276         
29277         
29278         var html = '<iframe id="' + this.getId() +
29279             '___Frame" src="' + sLink +
29280             '" width="' + this.width +
29281             '" height="' + this.height + '"' +
29282             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29283             ' frameborder="0" scrolling="no"></iframe>' ;
29284
29285         return html ;
29286     },
29287     
29288     _insertHtmlBefore : function( html, element )
29289     {
29290         if ( element.insertAdjacentHTML )       {
29291             // IE
29292             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29293         } else { // Gecko
29294             var oRange = document.createRange() ;
29295             oRange.setStartBefore( element ) ;
29296             var oFragment = oRange.createContextualFragment( html );
29297             element.parentNode.insertBefore( oFragment, element ) ;
29298         }
29299     }
29300     
29301     
29302   
29303     
29304     
29305     
29306     
29307
29308 });
29309
29310 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29311
29312 function FCKeditor_OnComplete(editorInstance){
29313     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29314     f.fckEditor = editorInstance;
29315     //console.log("loaded");
29316     f.fireEvent('editorinit', f, editorInstance);
29317
29318   
29319
29320  
29321
29322
29323
29324
29325
29326
29327
29328
29329
29330
29331
29332
29333
29334
29335
29336 //<script type="text/javascript">
29337 /**
29338  * @class Roo.form.GridField
29339  * @extends Roo.form.Field
29340  * Embed a grid (or editable grid into a form)
29341  * STATUS ALPHA
29342  * 
29343  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29344  * it needs 
29345  * xgrid.store = Roo.data.Store
29346  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29347  * xgrid.store.reader = Roo.data.JsonReader 
29348  * 
29349  * 
29350  * @constructor
29351  * Creates a new GridField
29352  * @param {Object} config Configuration options
29353  */
29354 Roo.form.GridField = function(config){
29355     Roo.form.GridField.superclass.constructor.call(this, config);
29356      
29357 };
29358
29359 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29360     /**
29361      * @cfg {Number} width  - used to restrict width of grid..
29362      */
29363     width : 100,
29364     /**
29365      * @cfg {Number} height - used to restrict height of grid..
29366      */
29367     height : 50,
29368      /**
29369      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29370          * 
29371          *}
29372      */
29373     xgrid : false, 
29374     /**
29375      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29376      * {tag: "input", type: "checkbox", autocomplete: "off"})
29377      */
29378    // defaultAutoCreate : { tag: 'div' },
29379     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29380     /**
29381      * @cfg {String} addTitle Text to include for adding a title.
29382      */
29383     addTitle : false,
29384     //
29385     onResize : function(){
29386         Roo.form.Field.superclass.onResize.apply(this, arguments);
29387     },
29388
29389     initEvents : function(){
29390         // Roo.form.Checkbox.superclass.initEvents.call(this);
29391         // has no events...
29392        
29393     },
29394
29395
29396     getResizeEl : function(){
29397         return this.wrap;
29398     },
29399
29400     getPositionEl : function(){
29401         return this.wrap;
29402     },
29403
29404     // private
29405     onRender : function(ct, position){
29406         
29407         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29408         var style = this.style;
29409         delete this.style;
29410         
29411         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29412         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29413         this.viewEl = this.wrap.createChild({ tag: 'div' });
29414         if (style) {
29415             this.viewEl.applyStyles(style);
29416         }
29417         if (this.width) {
29418             this.viewEl.setWidth(this.width);
29419         }
29420         if (this.height) {
29421             this.viewEl.setHeight(this.height);
29422         }
29423         //if(this.inputValue !== undefined){
29424         //this.setValue(this.value);
29425         
29426         
29427         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29428         
29429         
29430         this.grid.render();
29431         this.grid.getDataSource().on('remove', this.refreshValue, this);
29432         this.grid.getDataSource().on('update', this.refreshValue, this);
29433         this.grid.on('afteredit', this.refreshValue, this);
29434  
29435     },
29436      
29437     
29438     /**
29439      * Sets the value of the item. 
29440      * @param {String} either an object  or a string..
29441      */
29442     setValue : function(v){
29443         //this.value = v;
29444         v = v || []; // empty set..
29445         // this does not seem smart - it really only affects memoryproxy grids..
29446         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29447             var ds = this.grid.getDataSource();
29448             // assumes a json reader..
29449             var data = {}
29450             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29451             ds.loadData( data);
29452         }
29453         // clear selection so it does not get stale.
29454         if (this.grid.sm) { 
29455             this.grid.sm.clearSelections();
29456         }
29457         
29458         Roo.form.GridField.superclass.setValue.call(this, v);
29459         this.refreshValue();
29460         // should load data in the grid really....
29461     },
29462     
29463     // private
29464     refreshValue: function() {
29465          var val = [];
29466         this.grid.getDataSource().each(function(r) {
29467             val.push(r.data);
29468         });
29469         this.el.dom.value = Roo.encode(val);
29470     }
29471     
29472      
29473     
29474     
29475 });/*
29476  * Based on:
29477  * Ext JS Library 1.1.1
29478  * Copyright(c) 2006-2007, Ext JS, LLC.
29479  *
29480  * Originally Released Under LGPL - original licence link has changed is not relivant.
29481  *
29482  * Fork - LGPL
29483  * <script type="text/javascript">
29484  */
29485 /**
29486  * @class Roo.form.DisplayField
29487  * @extends Roo.form.Field
29488  * A generic Field to display non-editable data.
29489  * @constructor
29490  * Creates a new Display Field item.
29491  * @param {Object} config Configuration options
29492  */
29493 Roo.form.DisplayField = function(config){
29494     Roo.form.DisplayField.superclass.constructor.call(this, config);
29495     
29496 };
29497
29498 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29499     inputType:      'hidden',
29500     allowBlank:     true,
29501     readOnly:         true,
29502     
29503  
29504     /**
29505      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29506      */
29507     focusClass : undefined,
29508     /**
29509      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29510      */
29511     fieldClass: 'x-form-field',
29512     
29513      /**
29514      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29515      */
29516     valueRenderer: undefined,
29517     
29518     width: 100,
29519     /**
29520      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29521      * {tag: "input", type: "checkbox", autocomplete: "off"})
29522      */
29523      
29524  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29525
29526     onResize : function(){
29527         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29528         
29529     },
29530
29531     initEvents : function(){
29532         // Roo.form.Checkbox.superclass.initEvents.call(this);
29533         // has no events...
29534        
29535     },
29536
29537
29538     getResizeEl : function(){
29539         return this.wrap;
29540     },
29541
29542     getPositionEl : function(){
29543         return this.wrap;
29544     },
29545
29546     // private
29547     onRender : function(ct, position){
29548         
29549         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29550         //if(this.inputValue !== undefined){
29551         this.wrap = this.el.wrap();
29552         
29553         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29554         
29555         if (this.bodyStyle) {
29556             this.viewEl.applyStyles(this.bodyStyle);
29557         }
29558         //this.viewEl.setStyle('padding', '2px');
29559         
29560         this.setValue(this.value);
29561         
29562     },
29563 /*
29564     // private
29565     initValue : Roo.emptyFn,
29566
29567   */
29568
29569         // private
29570     onClick : function(){
29571         
29572     },
29573
29574     /**
29575      * Sets the checked state of the checkbox.
29576      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29577      */
29578     setValue : function(v){
29579         this.value = v;
29580         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29581         // this might be called before we have a dom element..
29582         if (!this.viewEl) {
29583             return;
29584         }
29585         this.viewEl.dom.innerHTML = html;
29586         Roo.form.DisplayField.superclass.setValue.call(this, v);
29587
29588     }
29589 });/*
29590  * 
29591  * Licence- LGPL
29592  * 
29593  */
29594
29595 /**
29596  * @class Roo.form.DayPicker
29597  * @extends Roo.form.Field
29598  * A Day picker show [M] [T] [W] ....
29599  * @constructor
29600  * Creates a new Day Picker
29601  * @param {Object} config Configuration options
29602  */
29603 Roo.form.DayPicker= function(config){
29604     Roo.form.DayPicker.superclass.constructor.call(this, config);
29605      
29606 };
29607
29608 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
29609     /**
29610      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29611      */
29612     focusClass : undefined,
29613     /**
29614      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29615      */
29616     fieldClass: "x-form-field",
29617    
29618     /**
29619      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29620      * {tag: "input", type: "checkbox", autocomplete: "off"})
29621      */
29622     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
29623     
29624    
29625     actionMode : 'viewEl', 
29626     //
29627     // private
29628  
29629     inputType : 'hidden',
29630     
29631      
29632     inputElement: false, // real input element?
29633     basedOn: false, // ????
29634     
29635     isFormField: true, // not sure where this is needed!!!!
29636
29637     onResize : function(){
29638         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
29639         if(!this.boxLabel){
29640             this.el.alignTo(this.wrap, 'c-c');
29641         }
29642     },
29643
29644     initEvents : function(){
29645         Roo.form.Checkbox.superclass.initEvents.call(this);
29646         this.el.on("click", this.onClick,  this);
29647         this.el.on("change", this.onClick,  this);
29648     },
29649
29650
29651     getResizeEl : function(){
29652         return this.wrap;
29653     },
29654
29655     getPositionEl : function(){
29656         return this.wrap;
29657     },
29658
29659     
29660     // private
29661     onRender : function(ct, position){
29662         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
29663        
29664         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
29665         
29666         var r1 = '<table><tr>';
29667         var r2 = '<tr class="x-form-daypick-icons">';
29668         for (var i=0; i < 7; i++) {
29669             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
29670             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
29671         }
29672         
29673         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
29674         viewEl.select('img').on('click', this.onClick, this);
29675         this.viewEl = viewEl;   
29676         
29677         
29678         // this will not work on Chrome!!!
29679         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
29680         this.el.on('propertychange', this.setFromHidden,  this);  //ie
29681         
29682         
29683           
29684
29685     },
29686
29687     // private
29688     initValue : Roo.emptyFn,
29689
29690     /**
29691      * Returns the checked state of the checkbox.
29692      * @return {Boolean} True if checked, else false
29693      */
29694     getValue : function(){
29695         return this.el.dom.value;
29696         
29697     },
29698
29699         // private
29700     onClick : function(e){ 
29701         //this.setChecked(!this.checked);
29702         Roo.get(e.target).toggleClass('x-menu-item-checked');
29703         this.refreshValue();
29704         //if(this.el.dom.checked != this.checked){
29705         //    this.setValue(this.el.dom.checked);
29706        // }
29707     },
29708     
29709     // private
29710     refreshValue : function()
29711     {
29712         var val = '';
29713         this.viewEl.select('img',true).each(function(e,i,n)  {
29714             val += e.is(".x-menu-item-checked") ? String(n) : '';
29715         });
29716         this.setValue(val, true);
29717     },
29718
29719     /**
29720      * Sets the checked state of the checkbox.
29721      * On is always based on a string comparison between inputValue and the param.
29722      * @param {Boolean/String} value - the value to set 
29723      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
29724      */
29725     setValue : function(v,suppressEvent){
29726         if (!this.el.dom) {
29727             return;
29728         }
29729         var old = this.el.dom.value ;
29730         this.el.dom.value = v;
29731         if (suppressEvent) {
29732             return ;
29733         }
29734          
29735         // update display..
29736         this.viewEl.select('img',true).each(function(e,i,n)  {
29737             
29738             var on = e.is(".x-menu-item-checked");
29739             var newv = v.indexOf(String(n)) > -1;
29740             if (on != newv) {
29741                 e.toggleClass('x-menu-item-checked');
29742             }
29743             
29744         });
29745         
29746         
29747         this.fireEvent('change', this, v, old);
29748         
29749         
29750     },
29751    
29752     // handle setting of hidden value by some other method!!?!?
29753     setFromHidden: function()
29754     {
29755         if(!this.el){
29756             return;
29757         }
29758         //console.log("SET FROM HIDDEN");
29759         //alert('setFrom hidden');
29760         this.setValue(this.el.dom.value);
29761     },
29762     
29763     onDestroy : function()
29764     {
29765         if(this.viewEl){
29766             Roo.get(this.viewEl).remove();
29767         }
29768          
29769         Roo.form.DayPicker.superclass.onDestroy.call(this);
29770     }
29771
29772 });/*
29773  * RooJS Library 1.1.1
29774  * Copyright(c) 2008-2011  Alan Knowles
29775  *
29776  * License - LGPL
29777  */
29778  
29779
29780 /**
29781  * @class Roo.form.ComboCheck
29782  * @extends Roo.form.ComboBox
29783  * A combobox for multiple select items.
29784  *
29785  * FIXME - could do with a reset button..
29786  * 
29787  * @constructor
29788  * Create a new ComboCheck
29789  * @param {Object} config Configuration options
29790  */
29791 Roo.form.ComboCheck = function(config){
29792     Roo.form.ComboCheck.superclass.constructor.call(this, config);
29793     // should verify some data...
29794     // like
29795     // hiddenName = required..
29796     // displayField = required
29797     // valudField == required
29798     var req= [ 'hiddenName', 'displayField', 'valueField' ];
29799     var _t = this;
29800     Roo.each(req, function(e) {
29801         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
29802             throw "Roo.form.ComboCheck : missing value for: " + e;
29803         }
29804     });
29805     
29806     
29807 };
29808
29809 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
29810      
29811      
29812     editable : false,
29813      
29814     selectedClass: 'x-menu-item-checked', 
29815     
29816     // private
29817     onRender : function(ct, position){
29818         var _t = this;
29819         
29820         
29821         
29822         if(!this.tpl){
29823             var cls = 'x-combo-list';
29824
29825             
29826             this.tpl =  new Roo.Template({
29827                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
29828                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
29829                    '<span>{' + this.displayField + '}</span>' +
29830                     '</div>' 
29831                 
29832             });
29833         }
29834  
29835         
29836         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
29837         this.view.singleSelect = false;
29838         this.view.multiSelect = true;
29839         this.view.toggleSelect = true;
29840         this.pageTb.add(new Roo.Toolbar.Fill(), {
29841             
29842             text: 'Done',
29843             handler: function()
29844             {
29845                 _t.collapse();
29846             }
29847         });
29848     },
29849     
29850     onViewOver : function(e, t){
29851         // do nothing...
29852         return;
29853         
29854     },
29855     
29856     onViewClick : function(doFocus,index){
29857         return;
29858         
29859     },
29860     select: function () {
29861         //Roo.log("SELECT CALLED");
29862     },
29863      
29864     selectByValue : function(xv, scrollIntoView){
29865         var ar = this.getValueArray();
29866         var sels = [];
29867         
29868         Roo.each(ar, function(v) {
29869             if(v === undefined || v === null){
29870                 return;
29871             }
29872             var r = this.findRecord(this.valueField, v);
29873             if(r){
29874                 sels.push(this.store.indexOf(r))
29875                 
29876             }
29877         },this);
29878         this.view.select(sels);
29879         return false;
29880     },
29881     
29882     
29883     
29884     onSelect : function(record, index){
29885        // Roo.log("onselect Called");
29886        // this is only called by the clear button now..
29887         this.view.clearSelections();
29888         this.setValue('[]');
29889         if (this.value != this.valueBefore) {
29890             this.fireEvent('change', this, this.value, this.valueBefore);
29891         }
29892     },
29893     getValueArray : function()
29894     {
29895         var ar = [] ;
29896         
29897         try {
29898             //Roo.log(this.value);
29899             if (typeof(this.value) == 'undefined') {
29900                 return [];
29901             }
29902             var ar = Roo.decode(this.value);
29903             return  ar instanceof Array ? ar : []; //?? valid?
29904             
29905         } catch(e) {
29906             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
29907             return [];
29908         }
29909          
29910     },
29911     expand : function ()
29912     {
29913         Roo.form.ComboCheck.superclass.expand.call(this);
29914         this.valueBefore = this.value;
29915         
29916
29917     },
29918     
29919     collapse : function(){
29920         Roo.form.ComboCheck.superclass.collapse.call(this);
29921         var sl = this.view.getSelectedIndexes();
29922         var st = this.store;
29923         var nv = [];
29924         var tv = [];
29925         var r;
29926         Roo.each(sl, function(i) {
29927             r = st.getAt(i);
29928             nv.push(r.get(this.valueField));
29929         },this);
29930         this.setValue(Roo.encode(nv));
29931         if (this.value != this.valueBefore) {
29932
29933             this.fireEvent('change', this, this.value, this.valueBefore);
29934         }
29935         
29936     },
29937     
29938     setValue : function(v){
29939         // Roo.log(v);
29940         this.value = v;
29941         
29942         var vals = this.getValueArray();
29943         var tv = [];
29944         Roo.each(vals, function(k) {
29945             var r = this.findRecord(this.valueField, k);
29946             if(r){
29947                 tv.push(r.data[this.displayField]);
29948             }else if(this.valueNotFoundText !== undefined){
29949                 tv.push( this.valueNotFoundText );
29950             }
29951         },this);
29952        // Roo.log(tv);
29953         
29954         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
29955         this.hiddenField.value = v;
29956         this.value = v;
29957     }
29958     
29959 });//<script type="text/javasscript">
29960  
29961
29962 /**
29963  * @class Roo.DDView
29964  * A DnD enabled version of Roo.View.
29965  * @param {Element/String} container The Element in which to create the View.
29966  * @param {String} tpl The template string used to create the markup for each element of the View
29967  * @param {Object} config The configuration properties. These include all the config options of
29968  * {@link Roo.View} plus some specific to this class.<br>
29969  * <p>
29970  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
29971  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
29972  * <p>
29973  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
29974 .x-view-drag-insert-above {
29975         border-top:1px dotted #3366cc;
29976 }
29977 .x-view-drag-insert-below {
29978         border-bottom:1px dotted #3366cc;
29979 }
29980 </code></pre>
29981  * 
29982  */
29983  
29984 Roo.DDView = function(container, tpl, config) {
29985     Roo.DDView.superclass.constructor.apply(this, arguments);
29986     this.getEl().setStyle("outline", "0px none");
29987     this.getEl().unselectable();
29988     if (this.dragGroup) {
29989                 this.setDraggable(this.dragGroup.split(","));
29990     }
29991     if (this.dropGroup) {
29992                 this.setDroppable(this.dropGroup.split(","));
29993     }
29994     if (this.deletable) {
29995         this.setDeletable();
29996     }
29997     this.isDirtyFlag = false;
29998         this.addEvents({
29999                 "drop" : true
30000         });
30001 };
30002
30003 Roo.extend(Roo.DDView, Roo.View, {
30004 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30005 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30006 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30007 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30008
30009         isFormField: true,
30010
30011         reset: Roo.emptyFn,
30012         
30013         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30014
30015         validate: function() {
30016                 return true;
30017         },
30018         
30019         destroy: function() {
30020                 this.purgeListeners();
30021                 this.getEl.removeAllListeners();
30022                 this.getEl().remove();
30023                 if (this.dragZone) {
30024                         if (this.dragZone.destroy) {
30025                                 this.dragZone.destroy();
30026                         }
30027                 }
30028                 if (this.dropZone) {
30029                         if (this.dropZone.destroy) {
30030                                 this.dropZone.destroy();
30031                         }
30032                 }
30033         },
30034
30035 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30036         getName: function() {
30037                 return this.name;
30038         },
30039
30040 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30041         setValue: function(v) {
30042                 if (!this.store) {
30043                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30044                 }
30045                 var data = {};
30046                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30047                 this.store.proxy = new Roo.data.MemoryProxy(data);
30048                 this.store.load();
30049         },
30050
30051 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30052         getValue: function() {
30053                 var result = '(';
30054                 this.store.each(function(rec) {
30055                         result += rec.id + ',';
30056                 });
30057                 return result.substr(0, result.length - 1) + ')';
30058         },
30059         
30060         getIds: function() {
30061                 var i = 0, result = new Array(this.store.getCount());
30062                 this.store.each(function(rec) {
30063                         result[i++] = rec.id;
30064                 });
30065                 return result;
30066         },
30067         
30068         isDirty: function() {
30069                 return this.isDirtyFlag;
30070         },
30071
30072 /**
30073  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30074  *      whole Element becomes the target, and this causes the drop gesture to append.
30075  */
30076     getTargetFromEvent : function(e) {
30077                 var target = e.getTarget();
30078                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30079                 target = target.parentNode;
30080                 }
30081                 if (!target) {
30082                         target = this.el.dom.lastChild || this.el.dom;
30083                 }
30084                 return target;
30085     },
30086
30087 /**
30088  *      Create the drag data which consists of an object which has the property "ddel" as
30089  *      the drag proxy element. 
30090  */
30091     getDragData : function(e) {
30092         var target = this.findItemFromChild(e.getTarget());
30093                 if(target) {
30094                         this.handleSelection(e);
30095                         var selNodes = this.getSelectedNodes();
30096             var dragData = {
30097                 source: this,
30098                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30099                 nodes: selNodes,
30100                 records: []
30101                         };
30102                         var selectedIndices = this.getSelectedIndexes();
30103                         for (var i = 0; i < selectedIndices.length; i++) {
30104                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30105                         }
30106                         if (selNodes.length == 1) {
30107                                 dragData.ddel = target.cloneNode(true); // the div element
30108                         } else {
30109                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30110                                 div.className = 'multi-proxy';
30111                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30112                                         div.appendChild(selNodes[i].cloneNode(true));
30113                                 }
30114                                 dragData.ddel = div;
30115                         }
30116             //console.log(dragData)
30117             //console.log(dragData.ddel.innerHTML)
30118                         return dragData;
30119                 }
30120         //console.log('nodragData')
30121                 return false;
30122     },
30123     
30124 /**     Specify to which ddGroup items in this DDView may be dragged. */
30125     setDraggable: function(ddGroup) {
30126         if (ddGroup instanceof Array) {
30127                 Roo.each(ddGroup, this.setDraggable, this);
30128                 return;
30129         }
30130         if (this.dragZone) {
30131                 this.dragZone.addToGroup(ddGroup);
30132         } else {
30133                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30134                                 containerScroll: true,
30135                                 ddGroup: ddGroup 
30136
30137                         });
30138 //                      Draggability implies selection. DragZone's mousedown selects the element.
30139                         if (!this.multiSelect) { this.singleSelect = true; }
30140
30141 //                      Wire the DragZone's handlers up to methods in *this*
30142                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30143                 }
30144     },
30145
30146 /**     Specify from which ddGroup this DDView accepts drops. */
30147     setDroppable: function(ddGroup) {
30148         if (ddGroup instanceof Array) {
30149                 Roo.each(ddGroup, this.setDroppable, this);
30150                 return;
30151         }
30152         if (this.dropZone) {
30153                 this.dropZone.addToGroup(ddGroup);
30154         } else {
30155                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30156                                 containerScroll: true,
30157                                 ddGroup: ddGroup
30158                         });
30159
30160 //                      Wire the DropZone's handlers up to methods in *this*
30161                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30162                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30163                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30164                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30165                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30166                 }
30167     },
30168
30169 /**     Decide whether to drop above or below a View node. */
30170     getDropPoint : function(e, n, dd){
30171         if (n == this.el.dom) { return "above"; }
30172                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30173                 var c = t + (b - t) / 2;
30174                 var y = Roo.lib.Event.getPageY(e);
30175                 if(y <= c) {
30176                         return "above";
30177                 }else{
30178                         return "below";
30179                 }
30180     },
30181
30182     onNodeEnter : function(n, dd, e, data){
30183                 return false;
30184     },
30185     
30186     onNodeOver : function(n, dd, e, data){
30187                 var pt = this.getDropPoint(e, n, dd);
30188                 // set the insert point style on the target node
30189                 var dragElClass = this.dropNotAllowed;
30190                 if (pt) {
30191                         var targetElClass;
30192                         if (pt == "above"){
30193                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30194                                 targetElClass = "x-view-drag-insert-above";
30195                         } else {
30196                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30197                                 targetElClass = "x-view-drag-insert-below";
30198                         }
30199                         if (this.lastInsertClass != targetElClass){
30200                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30201                                 this.lastInsertClass = targetElClass;
30202                         }
30203                 }
30204                 return dragElClass;
30205         },
30206
30207     onNodeOut : function(n, dd, e, data){
30208                 this.removeDropIndicators(n);
30209     },
30210
30211     onNodeDrop : function(n, dd, e, data){
30212         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30213                 return false;
30214         }
30215         var pt = this.getDropPoint(e, n, dd);
30216                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30217                 if (pt == "below") { insertAt++; }
30218                 for (var i = 0; i < data.records.length; i++) {
30219                         var r = data.records[i];
30220                         var dup = this.store.getById(r.id);
30221                         if (dup && (dd != this.dragZone)) {
30222                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30223                         } else {
30224                                 if (data.copy) {
30225                                         this.store.insert(insertAt++, r.copy());
30226                                 } else {
30227                                         data.source.isDirtyFlag = true;
30228                                         r.store.remove(r);
30229                                         this.store.insert(insertAt++, r);
30230                                 }
30231                                 this.isDirtyFlag = true;
30232                         }
30233                 }
30234                 this.dragZone.cachedTarget = null;
30235                 return true;
30236     },
30237
30238     removeDropIndicators : function(n){
30239                 if(n){
30240                         Roo.fly(n).removeClass([
30241                                 "x-view-drag-insert-above",
30242                                 "x-view-drag-insert-below"]);
30243                         this.lastInsertClass = "_noclass";
30244                 }
30245     },
30246
30247 /**
30248  *      Utility method. Add a delete option to the DDView's context menu.
30249  *      @param {String} imageUrl The URL of the "delete" icon image.
30250  */
30251         setDeletable: function(imageUrl) {
30252                 if (!this.singleSelect && !this.multiSelect) {
30253                         this.singleSelect = true;
30254                 }
30255                 var c = this.getContextMenu();
30256                 this.contextMenu.on("itemclick", function(item) {
30257                         switch (item.id) {
30258                                 case "delete":
30259                                         this.remove(this.getSelectedIndexes());
30260                                         break;
30261                         }
30262                 }, this);
30263                 this.contextMenu.add({
30264                         icon: imageUrl,
30265                         id: "delete",
30266                         text: 'Delete'
30267                 });
30268         },
30269         
30270 /**     Return the context menu for this DDView. */
30271         getContextMenu: function() {
30272                 if (!this.contextMenu) {
30273 //                      Create the View's context menu
30274                         this.contextMenu = new Roo.menu.Menu({
30275                                 id: this.id + "-contextmenu"
30276                         });
30277                         this.el.on("contextmenu", this.showContextMenu, this);
30278                 }
30279                 return this.contextMenu;
30280         },
30281         
30282         disableContextMenu: function() {
30283                 if (this.contextMenu) {
30284                         this.el.un("contextmenu", this.showContextMenu, this);
30285                 }
30286         },
30287
30288         showContextMenu: function(e, item) {
30289         item = this.findItemFromChild(e.getTarget());
30290                 if (item) {
30291                         e.stopEvent();
30292                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30293                         this.contextMenu.showAt(e.getXY());
30294             }
30295     },
30296
30297 /**
30298  *      Remove {@link Roo.data.Record}s at the specified indices.
30299  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30300  */
30301     remove: function(selectedIndices) {
30302                 selectedIndices = [].concat(selectedIndices);
30303                 for (var i = 0; i < selectedIndices.length; i++) {
30304                         var rec = this.store.getAt(selectedIndices[i]);
30305                         this.store.remove(rec);
30306                 }
30307     },
30308
30309 /**
30310  *      Double click fires the event, but also, if this is draggable, and there is only one other
30311  *      related DropZone, it transfers the selected node.
30312  */
30313     onDblClick : function(e){
30314         var item = this.findItemFromChild(e.getTarget());
30315         if(item){
30316             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30317                 return false;
30318             }
30319             if (this.dragGroup) {
30320                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30321                     while (targets.indexOf(this.dropZone) > -1) {
30322                             targets.remove(this.dropZone);
30323                                 }
30324                     if (targets.length == 1) {
30325                                         this.dragZone.cachedTarget = null;
30326                         var el = Roo.get(targets[0].getEl());
30327                         var box = el.getBox(true);
30328                         targets[0].onNodeDrop(el.dom, {
30329                                 target: el.dom,
30330                                 xy: [box.x, box.y + box.height - 1]
30331                         }, null, this.getDragData(e));
30332                     }
30333                 }
30334         }
30335     },
30336     
30337     handleSelection: function(e) {
30338                 this.dragZone.cachedTarget = null;
30339         var item = this.findItemFromChild(e.getTarget());
30340         if (!item) {
30341                 this.clearSelections(true);
30342                 return;
30343         }
30344                 if (item && (this.multiSelect || this.singleSelect)){
30345                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30346                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30347                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30348                                 this.unselect(item);
30349                         } else {
30350                                 this.select(item, this.multiSelect && e.ctrlKey);
30351                                 this.lastSelection = item;
30352                         }
30353                 }
30354     },
30355
30356     onItemClick : function(item, index, e){
30357                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30358                         return false;
30359                 }
30360                 return true;
30361     },
30362
30363     unselect : function(nodeInfo, suppressEvent){
30364                 var node = this.getNode(nodeInfo);
30365                 if(node && this.isSelected(node)){
30366                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30367                                 Roo.fly(node).removeClass(this.selectedClass);
30368                                 this.selections.remove(node);
30369                                 if(!suppressEvent){
30370                                         this.fireEvent("selectionchange", this, this.selections);
30371                                 }
30372                         }
30373                 }
30374     }
30375 });
30376 /*
30377  * Based on:
30378  * Ext JS Library 1.1.1
30379  * Copyright(c) 2006-2007, Ext JS, LLC.
30380  *
30381  * Originally Released Under LGPL - original licence link has changed is not relivant.
30382  *
30383  * Fork - LGPL
30384  * <script type="text/javascript">
30385  */
30386  
30387 /**
30388  * @class Roo.LayoutManager
30389  * @extends Roo.util.Observable
30390  * Base class for layout managers.
30391  */
30392 Roo.LayoutManager = function(container, config){
30393     Roo.LayoutManager.superclass.constructor.call(this);
30394     this.el = Roo.get(container);
30395     // ie scrollbar fix
30396     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30397         document.body.scroll = "no";
30398     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30399         this.el.position('relative');
30400     }
30401     this.id = this.el.id;
30402     this.el.addClass("x-layout-container");
30403     /** false to disable window resize monitoring @type Boolean */
30404     this.monitorWindowResize = true;
30405     this.regions = {};
30406     this.addEvents({
30407         /**
30408          * @event layout
30409          * Fires when a layout is performed. 
30410          * @param {Roo.LayoutManager} this
30411          */
30412         "layout" : true,
30413         /**
30414          * @event regionresized
30415          * Fires when the user resizes a region. 
30416          * @param {Roo.LayoutRegion} region The resized region
30417          * @param {Number} newSize The new size (width for east/west, height for north/south)
30418          */
30419         "regionresized" : true,
30420         /**
30421          * @event regioncollapsed
30422          * Fires when a region is collapsed. 
30423          * @param {Roo.LayoutRegion} region The collapsed region
30424          */
30425         "regioncollapsed" : true,
30426         /**
30427          * @event regionexpanded
30428          * Fires when a region is expanded.  
30429          * @param {Roo.LayoutRegion} region The expanded region
30430          */
30431         "regionexpanded" : true
30432     });
30433     this.updating = false;
30434     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30435 };
30436
30437 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30438     /**
30439      * Returns true if this layout is currently being updated
30440      * @return {Boolean}
30441      */
30442     isUpdating : function(){
30443         return this.updating; 
30444     },
30445     
30446     /**
30447      * Suspend the LayoutManager from doing auto-layouts while
30448      * making multiple add or remove calls
30449      */
30450     beginUpdate : function(){
30451         this.updating = true;    
30452     },
30453     
30454     /**
30455      * Restore auto-layouts and optionally disable the manager from performing a layout
30456      * @param {Boolean} noLayout true to disable a layout update 
30457      */
30458     endUpdate : function(noLayout){
30459         this.updating = false;
30460         if(!noLayout){
30461             this.layout();
30462         }    
30463     },
30464     
30465     layout: function(){
30466         
30467     },
30468     
30469     onRegionResized : function(region, newSize){
30470         this.fireEvent("regionresized", region, newSize);
30471         this.layout();
30472     },
30473     
30474     onRegionCollapsed : function(region){
30475         this.fireEvent("regioncollapsed", region);
30476     },
30477     
30478     onRegionExpanded : function(region){
30479         this.fireEvent("regionexpanded", region);
30480     },
30481         
30482     /**
30483      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30484      * performs box-model adjustments.
30485      * @return {Object} The size as an object {width: (the width), height: (the height)}
30486      */
30487     getViewSize : function(){
30488         var size;
30489         if(this.el.dom != document.body){
30490             size = this.el.getSize();
30491         }else{
30492             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30493         }
30494         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30495         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30496         return size;
30497     },
30498     
30499     /**
30500      * Returns the Element this layout is bound to.
30501      * @return {Roo.Element}
30502      */
30503     getEl : function(){
30504         return this.el;
30505     },
30506     
30507     /**
30508      * Returns the specified region.
30509      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30510      * @return {Roo.LayoutRegion}
30511      */
30512     getRegion : function(target){
30513         return this.regions[target.toLowerCase()];
30514     },
30515     
30516     onWindowResize : function(){
30517         if(this.monitorWindowResize){
30518             this.layout();
30519         }
30520     }
30521 });/*
30522  * Based on:
30523  * Ext JS Library 1.1.1
30524  * Copyright(c) 2006-2007, Ext JS, LLC.
30525  *
30526  * Originally Released Under LGPL - original licence link has changed is not relivant.
30527  *
30528  * Fork - LGPL
30529  * <script type="text/javascript">
30530  */
30531 /**
30532  * @class Roo.BorderLayout
30533  * @extends Roo.LayoutManager
30534  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30535  * please see: <br><br>
30536  * <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>
30537  * <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>
30538  * Example:
30539  <pre><code>
30540  var layout = new Roo.BorderLayout(document.body, {
30541     north: {
30542         initialSize: 25,
30543         titlebar: false
30544     },
30545     west: {
30546         split:true,
30547         initialSize: 200,
30548         minSize: 175,
30549         maxSize: 400,
30550         titlebar: true,
30551         collapsible: true
30552     },
30553     east: {
30554         split:true,
30555         initialSize: 202,
30556         minSize: 175,
30557         maxSize: 400,
30558         titlebar: true,
30559         collapsible: true
30560     },
30561     south: {
30562         split:true,
30563         initialSize: 100,
30564         minSize: 100,
30565         maxSize: 200,
30566         titlebar: true,
30567         collapsible: true
30568     },
30569     center: {
30570         titlebar: true,
30571         autoScroll:true,
30572         resizeTabs: true,
30573         minTabWidth: 50,
30574         preferredTabWidth: 150
30575     }
30576 });
30577
30578 // shorthand
30579 var CP = Roo.ContentPanel;
30580
30581 layout.beginUpdate();
30582 layout.add("north", new CP("north", "North"));
30583 layout.add("south", new CP("south", {title: "South", closable: true}));
30584 layout.add("west", new CP("west", {title: "West"}));
30585 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30586 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30587 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30588 layout.getRegion("center").showPanel("center1");
30589 layout.endUpdate();
30590 </code></pre>
30591
30592 <b>The container the layout is rendered into can be either the body element or any other element.
30593 If it is not the body element, the container needs to either be an absolute positioned element,
30594 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30595 the container size if it is not the body element.</b>
30596
30597 * @constructor
30598 * Create a new BorderLayout
30599 * @param {String/HTMLElement/Element} container The container this layout is bound to
30600 * @param {Object} config Configuration options
30601  */
30602 Roo.BorderLayout = function(container, config){
30603     config = config || {};
30604     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30605     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30606     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30607         var target = this.factory.validRegions[i];
30608         if(config[target]){
30609             this.addRegion(target, config[target]);
30610         }
30611     }
30612 };
30613
30614 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
30615     /**
30616      * Creates and adds a new region if it doesn't already exist.
30617      * @param {String} target The target region key (north, south, east, west or center).
30618      * @param {Object} config The regions config object
30619      * @return {BorderLayoutRegion} The new region
30620      */
30621     addRegion : function(target, config){
30622         if(!this.regions[target]){
30623             var r = this.factory.create(target, this, config);
30624             this.bindRegion(target, r);
30625         }
30626         return this.regions[target];
30627     },
30628
30629     // private (kinda)
30630     bindRegion : function(name, r){
30631         this.regions[name] = r;
30632         r.on("visibilitychange", this.layout, this);
30633         r.on("paneladded", this.layout, this);
30634         r.on("panelremoved", this.layout, this);
30635         r.on("invalidated", this.layout, this);
30636         r.on("resized", this.onRegionResized, this);
30637         r.on("collapsed", this.onRegionCollapsed, this);
30638         r.on("expanded", this.onRegionExpanded, this);
30639     },
30640
30641     /**
30642      * Performs a layout update.
30643      */
30644     layout : function(){
30645         if(this.updating) return;
30646         var size = this.getViewSize();
30647         var w = size.width;
30648         var h = size.height;
30649         var centerW = w;
30650         var centerH = h;
30651         var centerY = 0;
30652         var centerX = 0;
30653         //var x = 0, y = 0;
30654
30655         var rs = this.regions;
30656         var north = rs["north"];
30657         var south = rs["south"]; 
30658         var west = rs["west"];
30659         var east = rs["east"];
30660         var center = rs["center"];
30661         //if(this.hideOnLayout){ // not supported anymore
30662             //c.el.setStyle("display", "none");
30663         //}
30664         if(north && north.isVisible()){
30665             var b = north.getBox();
30666             var m = north.getMargins();
30667             b.width = w - (m.left+m.right);
30668             b.x = m.left;
30669             b.y = m.top;
30670             centerY = b.height + b.y + m.bottom;
30671             centerH -= centerY;
30672             north.updateBox(this.safeBox(b));
30673         }
30674         if(south && south.isVisible()){
30675             var b = south.getBox();
30676             var m = south.getMargins();
30677             b.width = w - (m.left+m.right);
30678             b.x = m.left;
30679             var totalHeight = (b.height + m.top + m.bottom);
30680             b.y = h - totalHeight + m.top;
30681             centerH -= totalHeight;
30682             south.updateBox(this.safeBox(b));
30683         }
30684         if(west && west.isVisible()){
30685             var b = west.getBox();
30686             var m = west.getMargins();
30687             b.height = centerH - (m.top+m.bottom);
30688             b.x = m.left;
30689             b.y = centerY + m.top;
30690             var totalWidth = (b.width + m.left + m.right);
30691             centerX += totalWidth;
30692             centerW -= totalWidth;
30693             west.updateBox(this.safeBox(b));
30694         }
30695         if(east && east.isVisible()){
30696             var b = east.getBox();
30697             var m = east.getMargins();
30698             b.height = centerH - (m.top+m.bottom);
30699             var totalWidth = (b.width + m.left + m.right);
30700             b.x = w - totalWidth + m.left;
30701             b.y = centerY + m.top;
30702             centerW -= totalWidth;
30703             east.updateBox(this.safeBox(b));
30704         }
30705         if(center){
30706             var m = center.getMargins();
30707             var centerBox = {
30708                 x: centerX + m.left,
30709                 y: centerY + m.top,
30710                 width: centerW - (m.left+m.right),
30711                 height: centerH - (m.top+m.bottom)
30712             };
30713             //if(this.hideOnLayout){
30714                 //center.el.setStyle("display", "block");
30715             //}
30716             center.updateBox(this.safeBox(centerBox));
30717         }
30718         this.el.repaint();
30719         this.fireEvent("layout", this);
30720     },
30721
30722     // private
30723     safeBox : function(box){
30724         box.width = Math.max(0, box.width);
30725         box.height = Math.max(0, box.height);
30726         return box;
30727     },
30728
30729     /**
30730      * Adds a ContentPanel (or subclass) to this layout.
30731      * @param {String} target The target region key (north, south, east, west or center).
30732      * @param {Roo.ContentPanel} panel The panel to add
30733      * @return {Roo.ContentPanel} The added panel
30734      */
30735     add : function(target, panel){
30736          
30737         target = target.toLowerCase();
30738         return this.regions[target].add(panel);
30739     },
30740
30741     /**
30742      * Remove a ContentPanel (or subclass) to this layout.
30743      * @param {String} target The target region key (north, south, east, west or center).
30744      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
30745      * @return {Roo.ContentPanel} The removed panel
30746      */
30747     remove : function(target, panel){
30748         target = target.toLowerCase();
30749         return this.regions[target].remove(panel);
30750     },
30751
30752     /**
30753      * Searches all regions for a panel with the specified id
30754      * @param {String} panelId
30755      * @return {Roo.ContentPanel} The panel or null if it wasn't found
30756      */
30757     findPanel : function(panelId){
30758         var rs = this.regions;
30759         for(var target in rs){
30760             if(typeof rs[target] != "function"){
30761                 var p = rs[target].getPanel(panelId);
30762                 if(p){
30763                     return p;
30764                 }
30765             }
30766         }
30767         return null;
30768     },
30769
30770     /**
30771      * Searches all regions for a panel with the specified id and activates (shows) it.
30772      * @param {String/ContentPanel} panelId The panels id or the panel itself
30773      * @return {Roo.ContentPanel} The shown panel or null
30774      */
30775     showPanel : function(panelId) {
30776       var rs = this.regions;
30777       for(var target in rs){
30778          var r = rs[target];
30779          if(typeof r != "function"){
30780             if(r.hasPanel(panelId)){
30781                return r.showPanel(panelId);
30782             }
30783          }
30784       }
30785       return null;
30786    },
30787
30788    /**
30789      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
30790      * @param {Roo.state.Provider} provider (optional) An alternate state provider
30791      */
30792     restoreState : function(provider){
30793         if(!provider){
30794             provider = Roo.state.Manager;
30795         }
30796         var sm = new Roo.LayoutStateManager();
30797         sm.init(this, provider);
30798     },
30799
30800     /**
30801      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
30802      * object should contain properties for each region to add ContentPanels to, and each property's value should be
30803      * a valid ContentPanel config object.  Example:
30804      * <pre><code>
30805 // Create the main layout
30806 var layout = new Roo.BorderLayout('main-ct', {
30807     west: {
30808         split:true,
30809         minSize: 175,
30810         titlebar: true
30811     },
30812     center: {
30813         title:'Components'
30814     }
30815 }, 'main-ct');
30816
30817 // Create and add multiple ContentPanels at once via configs
30818 layout.batchAdd({
30819    west: {
30820        id: 'source-files',
30821        autoCreate:true,
30822        title:'Ext Source Files',
30823        autoScroll:true,
30824        fitToFrame:true
30825    },
30826    center : {
30827        el: cview,
30828        autoScroll:true,
30829        fitToFrame:true,
30830        toolbar: tb,
30831        resizeEl:'cbody'
30832    }
30833 });
30834 </code></pre>
30835      * @param {Object} regions An object containing ContentPanel configs by region name
30836      */
30837     batchAdd : function(regions){
30838         this.beginUpdate();
30839         for(var rname in regions){
30840             var lr = this.regions[rname];
30841             if(lr){
30842                 this.addTypedPanels(lr, regions[rname]);
30843             }
30844         }
30845         this.endUpdate();
30846     },
30847
30848     // private
30849     addTypedPanels : function(lr, ps){
30850         if(typeof ps == 'string'){
30851             lr.add(new Roo.ContentPanel(ps));
30852         }
30853         else if(ps instanceof Array){
30854             for(var i =0, len = ps.length; i < len; i++){
30855                 this.addTypedPanels(lr, ps[i]);
30856             }
30857         }
30858         else if(!ps.events){ // raw config?
30859             var el = ps.el;
30860             delete ps.el; // prevent conflict
30861             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
30862         }
30863         else {  // panel object assumed!
30864             lr.add(ps);
30865         }
30866     },
30867     /**
30868      * Adds a xtype elements to the layout.
30869      * <pre><code>
30870
30871 layout.addxtype({
30872        xtype : 'ContentPanel',
30873        region: 'west',
30874        items: [ .... ]
30875    }
30876 );
30877
30878 layout.addxtype({
30879         xtype : 'NestedLayoutPanel',
30880         region: 'west',
30881         layout: {
30882            center: { },
30883            west: { }   
30884         },
30885         items : [ ... list of content panels or nested layout panels.. ]
30886    }
30887 );
30888 </code></pre>
30889      * @param {Object} cfg Xtype definition of item to add.
30890      */
30891     addxtype : function(cfg)
30892     {
30893         // basically accepts a pannel...
30894         // can accept a layout region..!?!?
30895         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
30896         
30897         if (!cfg.xtype.match(/Panel$/)) {
30898             return false;
30899         }
30900         var ret = false;
30901         
30902         if (typeof(cfg.region) == 'undefined') {
30903             Roo.log("Failed to add Panel, region was not set");
30904             Roo.log(cfg);
30905             return false;
30906         }
30907         var region = cfg.region;
30908         delete cfg.region;
30909         
30910           
30911         var xitems = [];
30912         if (cfg.items) {
30913             xitems = cfg.items;
30914             delete cfg.items;
30915         }
30916         var nb = false;
30917         
30918         switch(cfg.xtype) 
30919         {
30920             case 'ContentPanel':  // ContentPanel (el, cfg)
30921             case 'ScrollPanel':  // ContentPanel (el, cfg)
30922                 if(cfg.autoCreate) {
30923                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30924                 } else {
30925                     var el = this.el.createChild();
30926                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
30927                 }
30928                 
30929                 this.add(region, ret);
30930                 break;
30931             
30932             
30933             case 'TreePanel': // our new panel!
30934                 cfg.el = this.el.createChild();
30935                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
30936                 this.add(region, ret);
30937                 break;
30938             
30939             case 'NestedLayoutPanel': 
30940                 // create a new Layout (which is  a Border Layout...
30941                 var el = this.el.createChild();
30942                 var clayout = cfg.layout;
30943                 delete cfg.layout;
30944                 clayout.items   = clayout.items  || [];
30945                 // replace this exitems with the clayout ones..
30946                 xitems = clayout.items;
30947                  
30948                 
30949                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
30950                     cfg.background = false;
30951                 }
30952                 var layout = new Roo.BorderLayout(el, clayout);
30953                 
30954                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
30955                 //console.log('adding nested layout panel '  + cfg.toSource());
30956                 this.add(region, ret);
30957                 nb = {}; /// find first...
30958                 break;
30959                 
30960             case 'GridPanel': 
30961             
30962                 // needs grid and region
30963                 
30964                 //var el = this.getRegion(region).el.createChild();
30965                 var el = this.el.createChild();
30966                 // create the grid first...
30967                 
30968                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
30969                 delete cfg.grid;
30970                 if (region == 'center' && this.active ) {
30971                     cfg.background = false;
30972                 }
30973                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
30974                 
30975                 this.add(region, ret);
30976                 if (cfg.background) {
30977                     ret.on('activate', function(gp) {
30978                         if (!gp.grid.rendered) {
30979                             gp.grid.render();
30980                         }
30981                     });
30982                 } else {
30983                     grid.render();
30984                 }
30985                 break;
30986            
30987                
30988                 
30989                 
30990             default: 
30991                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
30992                 return null;
30993              // GridPanel (grid, cfg)
30994             
30995         }
30996         this.beginUpdate();
30997         // add children..
30998         var region = '';
30999         var abn = {};
31000         Roo.each(xitems, function(i)  {
31001             region = nb && i.region ? i.region : false;
31002             
31003             var add = ret.addxtype(i);
31004            
31005             if (region) {
31006                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31007                 if (!i.background) {
31008                     abn[region] = nb[region] ;
31009                 }
31010             }
31011             
31012         });
31013         this.endUpdate();
31014
31015         // make the last non-background panel active..
31016         //if (nb) { Roo.log(abn); }
31017         if (nb) {
31018             
31019             for(var r in abn) {
31020                 region = this.getRegion(r);
31021                 if (region) {
31022                     // tried using nb[r], but it does not work..
31023                      
31024                     region.showPanel(abn[r]);
31025                    
31026                 }
31027             }
31028         }
31029         return ret;
31030         
31031     }
31032 });
31033
31034 /**
31035  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31036  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31037  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31038  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31039  * <pre><code>
31040 // shorthand
31041 var CP = Roo.ContentPanel;
31042
31043 var layout = Roo.BorderLayout.create({
31044     north: {
31045         initialSize: 25,
31046         titlebar: false,
31047         panels: [new CP("north", "North")]
31048     },
31049     west: {
31050         split:true,
31051         initialSize: 200,
31052         minSize: 175,
31053         maxSize: 400,
31054         titlebar: true,
31055         collapsible: true,
31056         panels: [new CP("west", {title: "West"})]
31057     },
31058     east: {
31059         split:true,
31060         initialSize: 202,
31061         minSize: 175,
31062         maxSize: 400,
31063         titlebar: true,
31064         collapsible: true,
31065         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31066     },
31067     south: {
31068         split:true,
31069         initialSize: 100,
31070         minSize: 100,
31071         maxSize: 200,
31072         titlebar: true,
31073         collapsible: true,
31074         panels: [new CP("south", {title: "South", closable: true})]
31075     },
31076     center: {
31077         titlebar: true,
31078         autoScroll:true,
31079         resizeTabs: true,
31080         minTabWidth: 50,
31081         preferredTabWidth: 150,
31082         panels: [
31083             new CP("center1", {title: "Close Me", closable: true}),
31084             new CP("center2", {title: "Center Panel", closable: false})
31085         ]
31086     }
31087 }, document.body);
31088
31089 layout.getRegion("center").showPanel("center1");
31090 </code></pre>
31091  * @param config
31092  * @param targetEl
31093  */
31094 Roo.BorderLayout.create = function(config, targetEl){
31095     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31096     layout.beginUpdate();
31097     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31098     for(var j = 0, jlen = regions.length; j < jlen; j++){
31099         var lr = regions[j];
31100         if(layout.regions[lr] && config[lr].panels){
31101             var r = layout.regions[lr];
31102             var ps = config[lr].panels;
31103             layout.addTypedPanels(r, ps);
31104         }
31105     }
31106     layout.endUpdate();
31107     return layout;
31108 };
31109
31110 // private
31111 Roo.BorderLayout.RegionFactory = {
31112     // private
31113     validRegions : ["north","south","east","west","center"],
31114
31115     // private
31116     create : function(target, mgr, config){
31117         target = target.toLowerCase();
31118         if(config.lightweight || config.basic){
31119             return new Roo.BasicLayoutRegion(mgr, config, target);
31120         }
31121         switch(target){
31122             case "north":
31123                 return new Roo.NorthLayoutRegion(mgr, config);
31124             case "south":
31125                 return new Roo.SouthLayoutRegion(mgr, config);
31126             case "east":
31127                 return new Roo.EastLayoutRegion(mgr, config);
31128             case "west":
31129                 return new Roo.WestLayoutRegion(mgr, config);
31130             case "center":
31131                 return new Roo.CenterLayoutRegion(mgr, config);
31132         }
31133         throw 'Layout region "'+target+'" not supported.';
31134     }
31135 };/*
31136  * Based on:
31137  * Ext JS Library 1.1.1
31138  * Copyright(c) 2006-2007, Ext JS, LLC.
31139  *
31140  * Originally Released Under LGPL - original licence link has changed is not relivant.
31141  *
31142  * Fork - LGPL
31143  * <script type="text/javascript">
31144  */
31145  
31146 /**
31147  * @class Roo.BasicLayoutRegion
31148  * @extends Roo.util.Observable
31149  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31150  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31151  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31152  */
31153 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31154     this.mgr = mgr;
31155     this.position  = pos;
31156     this.events = {
31157         /**
31158          * @scope Roo.BasicLayoutRegion
31159          */
31160         
31161         /**
31162          * @event beforeremove
31163          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31164          * @param {Roo.LayoutRegion} this
31165          * @param {Roo.ContentPanel} panel The panel
31166          * @param {Object} e The cancel event object
31167          */
31168         "beforeremove" : true,
31169         /**
31170          * @event invalidated
31171          * Fires when the layout for this region is changed.
31172          * @param {Roo.LayoutRegion} this
31173          */
31174         "invalidated" : true,
31175         /**
31176          * @event visibilitychange
31177          * Fires when this region is shown or hidden 
31178          * @param {Roo.LayoutRegion} this
31179          * @param {Boolean} visibility true or false
31180          */
31181         "visibilitychange" : true,
31182         /**
31183          * @event paneladded
31184          * Fires when a panel is added. 
31185          * @param {Roo.LayoutRegion} this
31186          * @param {Roo.ContentPanel} panel The panel
31187          */
31188         "paneladded" : true,
31189         /**
31190          * @event panelremoved
31191          * Fires when a panel is removed. 
31192          * @param {Roo.LayoutRegion} this
31193          * @param {Roo.ContentPanel} panel The panel
31194          */
31195         "panelremoved" : true,
31196         /**
31197          * @event collapsed
31198          * Fires when this region is collapsed.
31199          * @param {Roo.LayoutRegion} this
31200          */
31201         "collapsed" : true,
31202         /**
31203          * @event expanded
31204          * Fires when this region is expanded.
31205          * @param {Roo.LayoutRegion} this
31206          */
31207         "expanded" : true,
31208         /**
31209          * @event slideshow
31210          * Fires when this region is slid into view.
31211          * @param {Roo.LayoutRegion} this
31212          */
31213         "slideshow" : true,
31214         /**
31215          * @event slidehide
31216          * Fires when this region slides out of view. 
31217          * @param {Roo.LayoutRegion} this
31218          */
31219         "slidehide" : true,
31220         /**
31221          * @event panelactivated
31222          * Fires when a panel is activated. 
31223          * @param {Roo.LayoutRegion} this
31224          * @param {Roo.ContentPanel} panel The activated panel
31225          */
31226         "panelactivated" : true,
31227         /**
31228          * @event resized
31229          * Fires when the user resizes this region. 
31230          * @param {Roo.LayoutRegion} this
31231          * @param {Number} newSize The new size (width for east/west, height for north/south)
31232          */
31233         "resized" : true
31234     };
31235     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31236     this.panels = new Roo.util.MixedCollection();
31237     this.panels.getKey = this.getPanelId.createDelegate(this);
31238     this.box = null;
31239     this.activePanel = null;
31240     // ensure listeners are added...
31241     
31242     if (config.listeners || config.events) {
31243         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31244             listeners : config.listeners || {},
31245             events : config.events || {}
31246         });
31247     }
31248     
31249     if(skipConfig !== true){
31250         this.applyConfig(config);
31251     }
31252 };
31253
31254 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31255     getPanelId : function(p){
31256         return p.getId();
31257     },
31258     
31259     applyConfig : function(config){
31260         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31261         this.config = config;
31262         
31263     },
31264     
31265     /**
31266      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31267      * the width, for horizontal (north, south) the height.
31268      * @param {Number} newSize The new width or height
31269      */
31270     resizeTo : function(newSize){
31271         var el = this.el ? this.el :
31272                  (this.activePanel ? this.activePanel.getEl() : null);
31273         if(el){
31274             switch(this.position){
31275                 case "east":
31276                 case "west":
31277                     el.setWidth(newSize);
31278                     this.fireEvent("resized", this, newSize);
31279                 break;
31280                 case "north":
31281                 case "south":
31282                     el.setHeight(newSize);
31283                     this.fireEvent("resized", this, newSize);
31284                 break;                
31285             }
31286         }
31287     },
31288     
31289     getBox : function(){
31290         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31291     },
31292     
31293     getMargins : function(){
31294         return this.margins;
31295     },
31296     
31297     updateBox : function(box){
31298         this.box = box;
31299         var el = this.activePanel.getEl();
31300         el.dom.style.left = box.x + "px";
31301         el.dom.style.top = box.y + "px";
31302         this.activePanel.setSize(box.width, box.height);
31303     },
31304     
31305     /**
31306      * Returns the container element for this region.
31307      * @return {Roo.Element}
31308      */
31309     getEl : function(){
31310         return this.activePanel;
31311     },
31312     
31313     /**
31314      * Returns true if this region is currently visible.
31315      * @return {Boolean}
31316      */
31317     isVisible : function(){
31318         return this.activePanel ? true : false;
31319     },
31320     
31321     setActivePanel : function(panel){
31322         panel = this.getPanel(panel);
31323         if(this.activePanel && this.activePanel != panel){
31324             this.activePanel.setActiveState(false);
31325             this.activePanel.getEl().setLeftTop(-10000,-10000);
31326         }
31327         this.activePanel = panel;
31328         panel.setActiveState(true);
31329         if(this.box){
31330             panel.setSize(this.box.width, this.box.height);
31331         }
31332         this.fireEvent("panelactivated", this, panel);
31333         this.fireEvent("invalidated");
31334     },
31335     
31336     /**
31337      * Show the specified panel.
31338      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31339      * @return {Roo.ContentPanel} The shown panel or null
31340      */
31341     showPanel : function(panel){
31342         if(panel = this.getPanel(panel)){
31343             this.setActivePanel(panel);
31344         }
31345         return panel;
31346     },
31347     
31348     /**
31349      * Get the active panel for this region.
31350      * @return {Roo.ContentPanel} The active panel or null
31351      */
31352     getActivePanel : function(){
31353         return this.activePanel;
31354     },
31355     
31356     /**
31357      * Add the passed ContentPanel(s)
31358      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31359      * @return {Roo.ContentPanel} The panel added (if only one was added)
31360      */
31361     add : function(panel){
31362         if(arguments.length > 1){
31363             for(var i = 0, len = arguments.length; i < len; i++) {
31364                 this.add(arguments[i]);
31365             }
31366             return null;
31367         }
31368         if(this.hasPanel(panel)){
31369             this.showPanel(panel);
31370             return panel;
31371         }
31372         var el = panel.getEl();
31373         if(el.dom.parentNode != this.mgr.el.dom){
31374             this.mgr.el.dom.appendChild(el.dom);
31375         }
31376         if(panel.setRegion){
31377             panel.setRegion(this);
31378         }
31379         this.panels.add(panel);
31380         el.setStyle("position", "absolute");
31381         if(!panel.background){
31382             this.setActivePanel(panel);
31383             if(this.config.initialSize && this.panels.getCount()==1){
31384                 this.resizeTo(this.config.initialSize);
31385             }
31386         }
31387         this.fireEvent("paneladded", this, panel);
31388         return panel;
31389     },
31390     
31391     /**
31392      * Returns true if the panel is in this region.
31393      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31394      * @return {Boolean}
31395      */
31396     hasPanel : function(panel){
31397         if(typeof panel == "object"){ // must be panel obj
31398             panel = panel.getId();
31399         }
31400         return this.getPanel(panel) ? true : false;
31401     },
31402     
31403     /**
31404      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31405      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31406      * @param {Boolean} preservePanel Overrides the config preservePanel option
31407      * @return {Roo.ContentPanel} The panel that was removed
31408      */
31409     remove : function(panel, preservePanel){
31410         panel = this.getPanel(panel);
31411         if(!panel){
31412             return null;
31413         }
31414         var e = {};
31415         this.fireEvent("beforeremove", this, panel, e);
31416         if(e.cancel === true){
31417             return null;
31418         }
31419         var panelId = panel.getId();
31420         this.panels.removeKey(panelId);
31421         return panel;
31422     },
31423     
31424     /**
31425      * Returns the panel specified or null if it's not in this region.
31426      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31427      * @return {Roo.ContentPanel}
31428      */
31429     getPanel : function(id){
31430         if(typeof id == "object"){ // must be panel obj
31431             return id;
31432         }
31433         return this.panels.get(id);
31434     },
31435     
31436     /**
31437      * Returns this regions position (north/south/east/west/center).
31438      * @return {String} 
31439      */
31440     getPosition: function(){
31441         return this.position;    
31442     }
31443 });/*
31444  * Based on:
31445  * Ext JS Library 1.1.1
31446  * Copyright(c) 2006-2007, Ext JS, LLC.
31447  *
31448  * Originally Released Under LGPL - original licence link has changed is not relivant.
31449  *
31450  * Fork - LGPL
31451  * <script type="text/javascript">
31452  */
31453  
31454 /**
31455  * @class Roo.LayoutRegion
31456  * @extends Roo.BasicLayoutRegion
31457  * This class represents a region in a layout manager.
31458  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31459  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31460  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31461  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31462  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
31463  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31464  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31465  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31466  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31467  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31468  * @cfg {String}    title           The title for the region (overrides panel titles)
31469  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31470  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31471  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31472  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31473  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31474  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31475  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31476  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31477  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31478  * @cfg {Boolean}   showPin         True to show a pin button
31479  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31480  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31481  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31482  * @cfg {Number}    width           For East/West panels
31483  * @cfg {Number}    height          For North/South panels
31484  * @cfg {Boolean}   split           To show the splitter
31485  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31486  */
31487 Roo.LayoutRegion = function(mgr, config, pos){
31488     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31489     var dh = Roo.DomHelper;
31490     /** This region's container element 
31491     * @type Roo.Element */
31492     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31493     /** This region's title element 
31494     * @type Roo.Element */
31495
31496     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31497         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31498         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31499     ]}, true);
31500     this.titleEl.enableDisplayMode();
31501     /** This region's title text element 
31502     * @type HTMLElement */
31503     this.titleTextEl = this.titleEl.dom.firstChild;
31504     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31505     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31506     this.closeBtn.enableDisplayMode();
31507     this.closeBtn.on("click", this.closeClicked, this);
31508     this.closeBtn.hide();
31509
31510     this.createBody(config);
31511     this.visible = true;
31512     this.collapsed = false;
31513
31514     if(config.hideWhenEmpty){
31515         this.hide();
31516         this.on("paneladded", this.validateVisibility, this);
31517         this.on("panelremoved", this.validateVisibility, this);
31518     }
31519     this.applyConfig(config);
31520 };
31521
31522 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31523
31524     createBody : function(){
31525         /** This region's body element 
31526         * @type Roo.Element */
31527         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31528     },
31529
31530     applyConfig : function(c){
31531         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31532             var dh = Roo.DomHelper;
31533             if(c.titlebar !== false){
31534                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31535                 this.collapseBtn.on("click", this.collapse, this);
31536                 this.collapseBtn.enableDisplayMode();
31537
31538                 if(c.showPin === true || this.showPin){
31539                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31540                     this.stickBtn.enableDisplayMode();
31541                     this.stickBtn.on("click", this.expand, this);
31542                     this.stickBtn.hide();
31543                 }
31544             }
31545             /** This region's collapsed element
31546             * @type Roo.Element */
31547             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31548                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31549             ]}, true);
31550             if(c.floatable !== false){
31551                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31552                this.collapsedEl.on("click", this.collapseClick, this);
31553             }
31554
31555             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31556                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31557                    id: "message", unselectable: "on", style:{"float":"left"}});
31558                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31559              }
31560             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31561             this.expandBtn.on("click", this.expand, this);
31562         }
31563         if(this.collapseBtn){
31564             this.collapseBtn.setVisible(c.collapsible == true);
31565         }
31566         this.cmargins = c.cmargins || this.cmargins ||
31567                          (this.position == "west" || this.position == "east" ?
31568                              {top: 0, left: 2, right:2, bottom: 0} :
31569                              {top: 2, left: 0, right:0, bottom: 2});
31570         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31571         this.bottomTabs = c.tabPosition != "top";
31572         this.autoScroll = c.autoScroll || false;
31573         if(this.autoScroll){
31574             this.bodyEl.setStyle("overflow", "auto");
31575         }else{
31576             this.bodyEl.setStyle("overflow", "hidden");
31577         }
31578         //if(c.titlebar !== false){
31579             if((!c.titlebar && !c.title) || c.titlebar === false){
31580                 this.titleEl.hide();
31581             }else{
31582                 this.titleEl.show();
31583                 if(c.title){
31584                     this.titleTextEl.innerHTML = c.title;
31585                 }
31586             }
31587         //}
31588         this.duration = c.duration || .30;
31589         this.slideDuration = c.slideDuration || .45;
31590         this.config = c;
31591         if(c.collapsed){
31592             this.collapse(true);
31593         }
31594         if(c.hidden){
31595             this.hide();
31596         }
31597     },
31598     /**
31599      * Returns true if this region is currently visible.
31600      * @return {Boolean}
31601      */
31602     isVisible : function(){
31603         return this.visible;
31604     },
31605
31606     /**
31607      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31608      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
31609      */
31610     setCollapsedTitle : function(title){
31611         title = title || "&#160;";
31612         if(this.collapsedTitleTextEl){
31613             this.collapsedTitleTextEl.innerHTML = title;
31614         }
31615     },
31616
31617     getBox : function(){
31618         var b;
31619         if(!this.collapsed){
31620             b = this.el.getBox(false, true);
31621         }else{
31622             b = this.collapsedEl.getBox(false, true);
31623         }
31624         return b;
31625     },
31626
31627     getMargins : function(){
31628         return this.collapsed ? this.cmargins : this.margins;
31629     },
31630
31631     highlight : function(){
31632         this.el.addClass("x-layout-panel-dragover");
31633     },
31634
31635     unhighlight : function(){
31636         this.el.removeClass("x-layout-panel-dragover");
31637     },
31638
31639     updateBox : function(box){
31640         this.box = box;
31641         if(!this.collapsed){
31642             this.el.dom.style.left = box.x + "px";
31643             this.el.dom.style.top = box.y + "px";
31644             this.updateBody(box.width, box.height);
31645         }else{
31646             this.collapsedEl.dom.style.left = box.x + "px";
31647             this.collapsedEl.dom.style.top = box.y + "px";
31648             this.collapsedEl.setSize(box.width, box.height);
31649         }
31650         if(this.tabs){
31651             this.tabs.autoSizeTabs();
31652         }
31653     },
31654
31655     updateBody : function(w, h){
31656         if(w !== null){
31657             this.el.setWidth(w);
31658             w -= this.el.getBorderWidth("rl");
31659             if(this.config.adjustments){
31660                 w += this.config.adjustments[0];
31661             }
31662         }
31663         if(h !== null){
31664             this.el.setHeight(h);
31665             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
31666             h -= this.el.getBorderWidth("tb");
31667             if(this.config.adjustments){
31668                 h += this.config.adjustments[1];
31669             }
31670             this.bodyEl.setHeight(h);
31671             if(this.tabs){
31672                 h = this.tabs.syncHeight(h);
31673             }
31674         }
31675         if(this.panelSize){
31676             w = w !== null ? w : this.panelSize.width;
31677             h = h !== null ? h : this.panelSize.height;
31678         }
31679         if(this.activePanel){
31680             var el = this.activePanel.getEl();
31681             w = w !== null ? w : el.getWidth();
31682             h = h !== null ? h : el.getHeight();
31683             this.panelSize = {width: w, height: h};
31684             this.activePanel.setSize(w, h);
31685         }
31686         if(Roo.isIE && this.tabs){
31687             this.tabs.el.repaint();
31688         }
31689     },
31690
31691     /**
31692      * Returns the container element for this region.
31693      * @return {Roo.Element}
31694      */
31695     getEl : function(){
31696         return this.el;
31697     },
31698
31699     /**
31700      * Hides this region.
31701      */
31702     hide : function(){
31703         if(!this.collapsed){
31704             this.el.dom.style.left = "-2000px";
31705             this.el.hide();
31706         }else{
31707             this.collapsedEl.dom.style.left = "-2000px";
31708             this.collapsedEl.hide();
31709         }
31710         this.visible = false;
31711         this.fireEvent("visibilitychange", this, false);
31712     },
31713
31714     /**
31715      * Shows this region if it was previously hidden.
31716      */
31717     show : function(){
31718         if(!this.collapsed){
31719             this.el.show();
31720         }else{
31721             this.collapsedEl.show();
31722         }
31723         this.visible = true;
31724         this.fireEvent("visibilitychange", this, true);
31725     },
31726
31727     closeClicked : function(){
31728         if(this.activePanel){
31729             this.remove(this.activePanel);
31730         }
31731     },
31732
31733     collapseClick : function(e){
31734         if(this.isSlid){
31735            e.stopPropagation();
31736            this.slideIn();
31737         }else{
31738            e.stopPropagation();
31739            this.slideOut();
31740         }
31741     },
31742
31743     /**
31744      * Collapses this region.
31745      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
31746      */
31747     collapse : function(skipAnim){
31748         if(this.collapsed) return;
31749         this.collapsed = true;
31750         if(this.split){
31751             this.split.el.hide();
31752         }
31753         if(this.config.animate && skipAnim !== true){
31754             this.fireEvent("invalidated", this);
31755             this.animateCollapse();
31756         }else{
31757             this.el.setLocation(-20000,-20000);
31758             this.el.hide();
31759             this.collapsedEl.show();
31760             this.fireEvent("collapsed", this);
31761             this.fireEvent("invalidated", this);
31762         }
31763     },
31764
31765     animateCollapse : function(){
31766         // overridden
31767     },
31768
31769     /**
31770      * Expands this region if it was previously collapsed.
31771      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
31772      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
31773      */
31774     expand : function(e, skipAnim){
31775         if(e) e.stopPropagation();
31776         if(!this.collapsed || this.el.hasActiveFx()) return;
31777         if(this.isSlid){
31778             this.afterSlideIn();
31779             skipAnim = true;
31780         }
31781         this.collapsed = false;
31782         if(this.config.animate && skipAnim !== true){
31783             this.animateExpand();
31784         }else{
31785             this.el.show();
31786             if(this.split){
31787                 this.split.el.show();
31788             }
31789             this.collapsedEl.setLocation(-2000,-2000);
31790             this.collapsedEl.hide();
31791             this.fireEvent("invalidated", this);
31792             this.fireEvent("expanded", this);
31793         }
31794     },
31795
31796     animateExpand : function(){
31797         // overridden
31798     },
31799
31800     initTabs : function()
31801     {
31802         this.bodyEl.setStyle("overflow", "hidden");
31803         var ts = new Roo.TabPanel(
31804                 this.bodyEl.dom,
31805                 {
31806                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
31807                     disableTooltips: this.config.disableTabTips,
31808                     toolbar : this.config.toolbar
31809                 }
31810         );
31811         if(this.config.hideTabs){
31812             ts.stripWrap.setDisplayed(false);
31813         }
31814         this.tabs = ts;
31815         ts.resizeTabs = this.config.resizeTabs === true;
31816         ts.minTabWidth = this.config.minTabWidth || 40;
31817         ts.maxTabWidth = this.config.maxTabWidth || 250;
31818         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
31819         ts.monitorResize = false;
31820         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
31821         ts.bodyEl.addClass('x-layout-tabs-body');
31822         this.panels.each(this.initPanelAsTab, this);
31823     },
31824
31825     initPanelAsTab : function(panel){
31826         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
31827                     this.config.closeOnTab && panel.isClosable());
31828         if(panel.tabTip !== undefined){
31829             ti.setTooltip(panel.tabTip);
31830         }
31831         ti.on("activate", function(){
31832               this.setActivePanel(panel);
31833         }, this);
31834         if(this.config.closeOnTab){
31835             ti.on("beforeclose", function(t, e){
31836                 e.cancel = true;
31837                 this.remove(panel);
31838             }, this);
31839         }
31840         return ti;
31841     },
31842
31843     updatePanelTitle : function(panel, title){
31844         if(this.activePanel == panel){
31845             this.updateTitle(title);
31846         }
31847         if(this.tabs){
31848             var ti = this.tabs.getTab(panel.getEl().id);
31849             ti.setText(title);
31850             if(panel.tabTip !== undefined){
31851                 ti.setTooltip(panel.tabTip);
31852             }
31853         }
31854     },
31855
31856     updateTitle : function(title){
31857         if(this.titleTextEl && !this.config.title){
31858             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
31859         }
31860     },
31861
31862     setActivePanel : function(panel){
31863         panel = this.getPanel(panel);
31864         if(this.activePanel && this.activePanel != panel){
31865             this.activePanel.setActiveState(false);
31866         }
31867         this.activePanel = panel;
31868         panel.setActiveState(true);
31869         if(this.panelSize){
31870             panel.setSize(this.panelSize.width, this.panelSize.height);
31871         }
31872         if(this.closeBtn){
31873             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
31874         }
31875         this.updateTitle(panel.getTitle());
31876         if(this.tabs){
31877             this.fireEvent("invalidated", this);
31878         }
31879         this.fireEvent("panelactivated", this, panel);
31880     },
31881
31882     /**
31883      * Shows the specified panel.
31884      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
31885      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
31886      */
31887     showPanel : function(panel){
31888         if(panel = this.getPanel(panel)){
31889             if(this.tabs){
31890                 var tab = this.tabs.getTab(panel.getEl().id);
31891                 if(tab.isHidden()){
31892                     this.tabs.unhideTab(tab.id);
31893                 }
31894                 tab.activate();
31895             }else{
31896                 this.setActivePanel(panel);
31897             }
31898         }
31899         return panel;
31900     },
31901
31902     /**
31903      * Get the active panel for this region.
31904      * @return {Roo.ContentPanel} The active panel or null
31905      */
31906     getActivePanel : function(){
31907         return this.activePanel;
31908     },
31909
31910     validateVisibility : function(){
31911         if(this.panels.getCount() < 1){
31912             this.updateTitle("&#160;");
31913             this.closeBtn.hide();
31914             this.hide();
31915         }else{
31916             if(!this.isVisible()){
31917                 this.show();
31918             }
31919         }
31920     },
31921
31922     /**
31923      * Adds the passed ContentPanel(s) to this region.
31924      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31925      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
31926      */
31927     add : function(panel){
31928         if(arguments.length > 1){
31929             for(var i = 0, len = arguments.length; i < len; i++) {
31930                 this.add(arguments[i]);
31931             }
31932             return null;
31933         }
31934         if(this.hasPanel(panel)){
31935             this.showPanel(panel);
31936             return panel;
31937         }
31938         panel.setRegion(this);
31939         this.panels.add(panel);
31940         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
31941             this.bodyEl.dom.appendChild(panel.getEl().dom);
31942             if(panel.background !== true){
31943                 this.setActivePanel(panel);
31944             }
31945             this.fireEvent("paneladded", this, panel);
31946             return panel;
31947         }
31948         if(!this.tabs){
31949             this.initTabs();
31950         }else{
31951             this.initPanelAsTab(panel);
31952         }
31953         if(panel.background !== true){
31954             this.tabs.activate(panel.getEl().id);
31955         }
31956         this.fireEvent("paneladded", this, panel);
31957         return panel;
31958     },
31959
31960     /**
31961      * Hides the tab for the specified panel.
31962      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31963      */
31964     hidePanel : function(panel){
31965         if(this.tabs && (panel = this.getPanel(panel))){
31966             this.tabs.hideTab(panel.getEl().id);
31967         }
31968     },
31969
31970     /**
31971      * Unhides the tab for a previously hidden panel.
31972      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31973      */
31974     unhidePanel : function(panel){
31975         if(this.tabs && (panel = this.getPanel(panel))){
31976             this.tabs.unhideTab(panel.getEl().id);
31977         }
31978     },
31979
31980     clearPanels : function(){
31981         while(this.panels.getCount() > 0){
31982              this.remove(this.panels.first());
31983         }
31984     },
31985
31986     /**
31987      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31988      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
31989      * @param {Boolean} preservePanel Overrides the config preservePanel option
31990      * @return {Roo.ContentPanel} The panel that was removed
31991      */
31992     remove : function(panel, preservePanel){
31993         panel = this.getPanel(panel);
31994         if(!panel){
31995             return null;
31996         }
31997         var e = {};
31998         this.fireEvent("beforeremove", this, panel, e);
31999         if(e.cancel === true){
32000             return null;
32001         }
32002         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32003         var panelId = panel.getId();
32004         this.panels.removeKey(panelId);
32005         if(preservePanel){
32006             document.body.appendChild(panel.getEl().dom);
32007         }
32008         if(this.tabs){
32009             this.tabs.removeTab(panel.getEl().id);
32010         }else if (!preservePanel){
32011             this.bodyEl.dom.removeChild(panel.getEl().dom);
32012         }
32013         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32014             var p = this.panels.first();
32015             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32016             tempEl.appendChild(p.getEl().dom);
32017             this.bodyEl.update("");
32018             this.bodyEl.dom.appendChild(p.getEl().dom);
32019             tempEl = null;
32020             this.updateTitle(p.getTitle());
32021             this.tabs = null;
32022             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32023             this.setActivePanel(p);
32024         }
32025         panel.setRegion(null);
32026         if(this.activePanel == panel){
32027             this.activePanel = null;
32028         }
32029         if(this.config.autoDestroy !== false && preservePanel !== true){
32030             try{panel.destroy();}catch(e){}
32031         }
32032         this.fireEvent("panelremoved", this, panel);
32033         return panel;
32034     },
32035
32036     /**
32037      * Returns the TabPanel component used by this region
32038      * @return {Roo.TabPanel}
32039      */
32040     getTabs : function(){
32041         return this.tabs;
32042     },
32043
32044     createTool : function(parentEl, className){
32045         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32046             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32047         btn.addClassOnOver("x-layout-tools-button-over");
32048         return btn;
32049     }
32050 });/*
32051  * Based on:
32052  * Ext JS Library 1.1.1
32053  * Copyright(c) 2006-2007, Ext JS, LLC.
32054  *
32055  * Originally Released Under LGPL - original licence link has changed is not relivant.
32056  *
32057  * Fork - LGPL
32058  * <script type="text/javascript">
32059  */
32060  
32061
32062
32063 /**
32064  * @class Roo.SplitLayoutRegion
32065  * @extends Roo.LayoutRegion
32066  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32067  */
32068 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32069     this.cursor = cursor;
32070     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32071 };
32072
32073 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32074     splitTip : "Drag to resize.",
32075     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32076     useSplitTips : false,
32077
32078     applyConfig : function(config){
32079         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32080         if(config.split){
32081             if(!this.split){
32082                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32083                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32084                 /** The SplitBar for this region 
32085                 * @type Roo.SplitBar */
32086                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32087                 this.split.on("moved", this.onSplitMove, this);
32088                 this.split.useShim = config.useShim === true;
32089                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32090                 if(this.useSplitTips){
32091                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32092                 }
32093                 if(config.collapsible){
32094                     this.split.el.on("dblclick", this.collapse,  this);
32095                 }
32096             }
32097             if(typeof config.minSize != "undefined"){
32098                 this.split.minSize = config.minSize;
32099             }
32100             if(typeof config.maxSize != "undefined"){
32101                 this.split.maxSize = config.maxSize;
32102             }
32103             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32104                 this.hideSplitter();
32105             }
32106         }
32107     },
32108
32109     getHMaxSize : function(){
32110          var cmax = this.config.maxSize || 10000;
32111          var center = this.mgr.getRegion("center");
32112          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32113     },
32114
32115     getVMaxSize : function(){
32116          var cmax = this.config.maxSize || 10000;
32117          var center = this.mgr.getRegion("center");
32118          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32119     },
32120
32121     onSplitMove : function(split, newSize){
32122         this.fireEvent("resized", this, newSize);
32123     },
32124     
32125     /** 
32126      * Returns the {@link Roo.SplitBar} for this region.
32127      * @return {Roo.SplitBar}
32128      */
32129     getSplitBar : function(){
32130         return this.split;
32131     },
32132     
32133     hide : function(){
32134         this.hideSplitter();
32135         Roo.SplitLayoutRegion.superclass.hide.call(this);
32136     },
32137
32138     hideSplitter : function(){
32139         if(this.split){
32140             this.split.el.setLocation(-2000,-2000);
32141             this.split.el.hide();
32142         }
32143     },
32144
32145     show : function(){
32146         if(this.split){
32147             this.split.el.show();
32148         }
32149         Roo.SplitLayoutRegion.superclass.show.call(this);
32150     },
32151     
32152     beforeSlide: function(){
32153         if(Roo.isGecko){// firefox overflow auto bug workaround
32154             this.bodyEl.clip();
32155             if(this.tabs) this.tabs.bodyEl.clip();
32156             if(this.activePanel){
32157                 this.activePanel.getEl().clip();
32158                 
32159                 if(this.activePanel.beforeSlide){
32160                     this.activePanel.beforeSlide();
32161                 }
32162             }
32163         }
32164     },
32165     
32166     afterSlide : function(){
32167         if(Roo.isGecko){// firefox overflow auto bug workaround
32168             this.bodyEl.unclip();
32169             if(this.tabs) this.tabs.bodyEl.unclip();
32170             if(this.activePanel){
32171                 this.activePanel.getEl().unclip();
32172                 if(this.activePanel.afterSlide){
32173                     this.activePanel.afterSlide();
32174                 }
32175             }
32176         }
32177     },
32178
32179     initAutoHide : function(){
32180         if(this.autoHide !== false){
32181             if(!this.autoHideHd){
32182                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32183                 this.autoHideHd = {
32184                     "mouseout": function(e){
32185                         if(!e.within(this.el, true)){
32186                             st.delay(500);
32187                         }
32188                     },
32189                     "mouseover" : function(e){
32190                         st.cancel();
32191                     },
32192                     scope : this
32193                 };
32194             }
32195             this.el.on(this.autoHideHd);
32196         }
32197     },
32198
32199     clearAutoHide : function(){
32200         if(this.autoHide !== false){
32201             this.el.un("mouseout", this.autoHideHd.mouseout);
32202             this.el.un("mouseover", this.autoHideHd.mouseover);
32203         }
32204     },
32205
32206     clearMonitor : function(){
32207         Roo.get(document).un("click", this.slideInIf, this);
32208     },
32209
32210     // these names are backwards but not changed for compat
32211     slideOut : function(){
32212         if(this.isSlid || this.el.hasActiveFx()){
32213             return;
32214         }
32215         this.isSlid = true;
32216         if(this.collapseBtn){
32217             this.collapseBtn.hide();
32218         }
32219         this.closeBtnState = this.closeBtn.getStyle('display');
32220         this.closeBtn.hide();
32221         if(this.stickBtn){
32222             this.stickBtn.show();
32223         }
32224         this.el.show();
32225         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32226         this.beforeSlide();
32227         this.el.setStyle("z-index", 10001);
32228         this.el.slideIn(this.getSlideAnchor(), {
32229             callback: function(){
32230                 this.afterSlide();
32231                 this.initAutoHide();
32232                 Roo.get(document).on("click", this.slideInIf, this);
32233                 this.fireEvent("slideshow", this);
32234             },
32235             scope: this,
32236             block: true
32237         });
32238     },
32239
32240     afterSlideIn : function(){
32241         this.clearAutoHide();
32242         this.isSlid = false;
32243         this.clearMonitor();
32244         this.el.setStyle("z-index", "");
32245         if(this.collapseBtn){
32246             this.collapseBtn.show();
32247         }
32248         this.closeBtn.setStyle('display', this.closeBtnState);
32249         if(this.stickBtn){
32250             this.stickBtn.hide();
32251         }
32252         this.fireEvent("slidehide", this);
32253     },
32254
32255     slideIn : function(cb){
32256         if(!this.isSlid || this.el.hasActiveFx()){
32257             Roo.callback(cb);
32258             return;
32259         }
32260         this.isSlid = false;
32261         this.beforeSlide();
32262         this.el.slideOut(this.getSlideAnchor(), {
32263             callback: function(){
32264                 this.el.setLeftTop(-10000, -10000);
32265                 this.afterSlide();
32266                 this.afterSlideIn();
32267                 Roo.callback(cb);
32268             },
32269             scope: this,
32270             block: true
32271         });
32272     },
32273     
32274     slideInIf : function(e){
32275         if(!e.within(this.el)){
32276             this.slideIn();
32277         }
32278     },
32279
32280     animateCollapse : function(){
32281         this.beforeSlide();
32282         this.el.setStyle("z-index", 20000);
32283         var anchor = this.getSlideAnchor();
32284         this.el.slideOut(anchor, {
32285             callback : function(){
32286                 this.el.setStyle("z-index", "");
32287                 this.collapsedEl.slideIn(anchor, {duration:.3});
32288                 this.afterSlide();
32289                 this.el.setLocation(-10000,-10000);
32290                 this.el.hide();
32291                 this.fireEvent("collapsed", this);
32292             },
32293             scope: this,
32294             block: true
32295         });
32296     },
32297
32298     animateExpand : function(){
32299         this.beforeSlide();
32300         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32301         this.el.setStyle("z-index", 20000);
32302         this.collapsedEl.hide({
32303             duration:.1
32304         });
32305         this.el.slideIn(this.getSlideAnchor(), {
32306             callback : function(){
32307                 this.el.setStyle("z-index", "");
32308                 this.afterSlide();
32309                 if(this.split){
32310                     this.split.el.show();
32311                 }
32312                 this.fireEvent("invalidated", this);
32313                 this.fireEvent("expanded", this);
32314             },
32315             scope: this,
32316             block: true
32317         });
32318     },
32319
32320     anchors : {
32321         "west" : "left",
32322         "east" : "right",
32323         "north" : "top",
32324         "south" : "bottom"
32325     },
32326
32327     sanchors : {
32328         "west" : "l",
32329         "east" : "r",
32330         "north" : "t",
32331         "south" : "b"
32332     },
32333
32334     canchors : {
32335         "west" : "tl-tr",
32336         "east" : "tr-tl",
32337         "north" : "tl-bl",
32338         "south" : "bl-tl"
32339     },
32340
32341     getAnchor : function(){
32342         return this.anchors[this.position];
32343     },
32344
32345     getCollapseAnchor : function(){
32346         return this.canchors[this.position];
32347     },
32348
32349     getSlideAnchor : function(){
32350         return this.sanchors[this.position];
32351     },
32352
32353     getAlignAdj : function(){
32354         var cm = this.cmargins;
32355         switch(this.position){
32356             case "west":
32357                 return [0, 0];
32358             break;
32359             case "east":
32360                 return [0, 0];
32361             break;
32362             case "north":
32363                 return [0, 0];
32364             break;
32365             case "south":
32366                 return [0, 0];
32367             break;
32368         }
32369     },
32370
32371     getExpandAdj : function(){
32372         var c = this.collapsedEl, cm = this.cmargins;
32373         switch(this.position){
32374             case "west":
32375                 return [-(cm.right+c.getWidth()+cm.left), 0];
32376             break;
32377             case "east":
32378                 return [cm.right+c.getWidth()+cm.left, 0];
32379             break;
32380             case "north":
32381                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32382             break;
32383             case "south":
32384                 return [0, cm.top+cm.bottom+c.getHeight()];
32385             break;
32386         }
32387     }
32388 });/*
32389  * Based on:
32390  * Ext JS Library 1.1.1
32391  * Copyright(c) 2006-2007, Ext JS, LLC.
32392  *
32393  * Originally Released Under LGPL - original licence link has changed is not relivant.
32394  *
32395  * Fork - LGPL
32396  * <script type="text/javascript">
32397  */
32398 /*
32399  * These classes are private internal classes
32400  */
32401 Roo.CenterLayoutRegion = function(mgr, config){
32402     Roo.LayoutRegion.call(this, mgr, config, "center");
32403     this.visible = true;
32404     this.minWidth = config.minWidth || 20;
32405     this.minHeight = config.minHeight || 20;
32406 };
32407
32408 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32409     hide : function(){
32410         // center panel can't be hidden
32411     },
32412     
32413     show : function(){
32414         // center panel can't be hidden
32415     },
32416     
32417     getMinWidth: function(){
32418         return this.minWidth;
32419     },
32420     
32421     getMinHeight: function(){
32422         return this.minHeight;
32423     }
32424 });
32425
32426
32427 Roo.NorthLayoutRegion = function(mgr, config){
32428     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32429     if(this.split){
32430         this.split.placement = Roo.SplitBar.TOP;
32431         this.split.orientation = Roo.SplitBar.VERTICAL;
32432         this.split.el.addClass("x-layout-split-v");
32433     }
32434     var size = config.initialSize || config.height;
32435     if(typeof size != "undefined"){
32436         this.el.setHeight(size);
32437     }
32438 };
32439 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32440     orientation: Roo.SplitBar.VERTICAL,
32441     getBox : function(){
32442         if(this.collapsed){
32443             return this.collapsedEl.getBox();
32444         }
32445         var box = this.el.getBox();
32446         if(this.split){
32447             box.height += this.split.el.getHeight();
32448         }
32449         return box;
32450     },
32451     
32452     updateBox : function(box){
32453         if(this.split && !this.collapsed){
32454             box.height -= this.split.el.getHeight();
32455             this.split.el.setLeft(box.x);
32456             this.split.el.setTop(box.y+box.height);
32457             this.split.el.setWidth(box.width);
32458         }
32459         if(this.collapsed){
32460             this.updateBody(box.width, null);
32461         }
32462         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32463     }
32464 });
32465
32466 Roo.SouthLayoutRegion = function(mgr, config){
32467     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32468     if(this.split){
32469         this.split.placement = Roo.SplitBar.BOTTOM;
32470         this.split.orientation = Roo.SplitBar.VERTICAL;
32471         this.split.el.addClass("x-layout-split-v");
32472     }
32473     var size = config.initialSize || config.height;
32474     if(typeof size != "undefined"){
32475         this.el.setHeight(size);
32476     }
32477 };
32478 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32479     orientation: Roo.SplitBar.VERTICAL,
32480     getBox : function(){
32481         if(this.collapsed){
32482             return this.collapsedEl.getBox();
32483         }
32484         var box = this.el.getBox();
32485         if(this.split){
32486             var sh = this.split.el.getHeight();
32487             box.height += sh;
32488             box.y -= sh;
32489         }
32490         return box;
32491     },
32492     
32493     updateBox : function(box){
32494         if(this.split && !this.collapsed){
32495             var sh = this.split.el.getHeight();
32496             box.height -= sh;
32497             box.y += sh;
32498             this.split.el.setLeft(box.x);
32499             this.split.el.setTop(box.y-sh);
32500             this.split.el.setWidth(box.width);
32501         }
32502         if(this.collapsed){
32503             this.updateBody(box.width, null);
32504         }
32505         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32506     }
32507 });
32508
32509 Roo.EastLayoutRegion = function(mgr, config){
32510     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32511     if(this.split){
32512         this.split.placement = Roo.SplitBar.RIGHT;
32513         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32514         this.split.el.addClass("x-layout-split-h");
32515     }
32516     var size = config.initialSize || config.width;
32517     if(typeof size != "undefined"){
32518         this.el.setWidth(size);
32519     }
32520 };
32521 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32522     orientation: Roo.SplitBar.HORIZONTAL,
32523     getBox : function(){
32524         if(this.collapsed){
32525             return this.collapsedEl.getBox();
32526         }
32527         var box = this.el.getBox();
32528         if(this.split){
32529             var sw = this.split.el.getWidth();
32530             box.width += sw;
32531             box.x -= sw;
32532         }
32533         return box;
32534     },
32535
32536     updateBox : function(box){
32537         if(this.split && !this.collapsed){
32538             var sw = this.split.el.getWidth();
32539             box.width -= sw;
32540             this.split.el.setLeft(box.x);
32541             this.split.el.setTop(box.y);
32542             this.split.el.setHeight(box.height);
32543             box.x += sw;
32544         }
32545         if(this.collapsed){
32546             this.updateBody(null, box.height);
32547         }
32548         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32549     }
32550 });
32551
32552 Roo.WestLayoutRegion = function(mgr, config){
32553     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32554     if(this.split){
32555         this.split.placement = Roo.SplitBar.LEFT;
32556         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32557         this.split.el.addClass("x-layout-split-h");
32558     }
32559     var size = config.initialSize || config.width;
32560     if(typeof size != "undefined"){
32561         this.el.setWidth(size);
32562     }
32563 };
32564 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32565     orientation: Roo.SplitBar.HORIZONTAL,
32566     getBox : function(){
32567         if(this.collapsed){
32568             return this.collapsedEl.getBox();
32569         }
32570         var box = this.el.getBox();
32571         if(this.split){
32572             box.width += this.split.el.getWidth();
32573         }
32574         return box;
32575     },
32576     
32577     updateBox : function(box){
32578         if(this.split && !this.collapsed){
32579             var sw = this.split.el.getWidth();
32580             box.width -= sw;
32581             this.split.el.setLeft(box.x+box.width);
32582             this.split.el.setTop(box.y);
32583             this.split.el.setHeight(box.height);
32584         }
32585         if(this.collapsed){
32586             this.updateBody(null, box.height);
32587         }
32588         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32589     }
32590 });