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     
3044     
3045     var onStop = function(e){
3046         dragEl = null;
3047         clearProc();
3048     };
3049     
3050     var triggerRefresh = function(){
3051         if(ddm.dragCurrent){
3052              ddm.refreshCache(ddm.dragCurrent.groups);
3053         }
3054     };
3055     
3056     var doScroll = function(){
3057         if(ddm.dragCurrent){
3058             var dds = Roo.dd.ScrollManager;
3059             if(!dds.animate){
3060                 if(proc.el.scroll(proc.dir, dds.increment)){
3061                     triggerRefresh();
3062                 }
3063             }else{
3064                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
3065             }
3066         }
3067     };
3068     
3069     var clearProc = function(){
3070         if(proc.id){
3071             clearInterval(proc.id);
3072         }
3073         proc.id = 0;
3074         proc.el = null;
3075         proc.dir = "";
3076     };
3077     
3078     var startProc = function(el, dir){
3079          Roo.log('scroll startproc');
3080         clearProc();
3081         proc.el = el;
3082         proc.dir = dir;
3083         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
3084     };
3085     
3086     var onFire = function(e, isDrop){
3087        
3088         if(isDrop || !ddm.dragCurrent){ return; }
3089         var dds = Roo.dd.ScrollManager;
3090         if(!dragEl || dragEl != ddm.dragCurrent){
3091             dragEl = ddm.dragCurrent;
3092             // refresh regions on drag start
3093             dds.refreshCache();
3094         }
3095         
3096         var xy = Roo.lib.Event.getXY(e);
3097         var pt = new Roo.lib.Point(xy[0], xy[1]);
3098         for(var id in els){
3099             var el = els[id], r = el._region;
3100             if(r && r.contains(pt) && el.isScrollable()){
3101                 if(r.bottom - pt.y <= dds.thresh){
3102                     if(proc.el != el){
3103                         startProc(el, "down");
3104                     }
3105                     return;
3106                 }else if(r.right - pt.x <= dds.thresh){
3107                     if(proc.el != el){
3108                         startProc(el, "left");
3109                     }
3110                     return;
3111                 }else if(pt.y - r.top <= dds.thresh){
3112                     if(proc.el != el){
3113                         startProc(el, "up");
3114                     }
3115                     return;
3116                 }else if(pt.x - r.left <= dds.thresh){
3117                     if(proc.el != el){
3118                         startProc(el, "right");
3119                     }
3120                     return;
3121                 }
3122             }
3123         }
3124         clearProc();
3125     };
3126     
3127     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
3128     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
3129     
3130     return {
3131         /**
3132          * Registers new overflow element(s) to auto scroll
3133          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
3134          */
3135         register : function(el){
3136             if(el instanceof Array){
3137                 for(var i = 0, len = el.length; i < len; i++) {
3138                         this.register(el[i]);
3139                 }
3140             }else{
3141                 el = Roo.get(el);
3142                 els[el.id] = el;
3143             }
3144             Roo.dd.ScrollManager.els = els;
3145         },
3146         
3147         /**
3148          * Unregisters overflow element(s) so they are no longer scrolled
3149          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
3150          */
3151         unregister : function(el){
3152             if(el instanceof Array){
3153                 for(var i = 0, len = el.length; i < len; i++) {
3154                         this.unregister(el[i]);
3155                 }
3156             }else{
3157                 el = Roo.get(el);
3158                 delete els[el.id];
3159             }
3160         },
3161         
3162         /**
3163          * The number of pixels from the edge of a container the pointer needs to be to 
3164          * trigger scrolling (defaults to 25)
3165          * @type Number
3166          */
3167         thresh : 25,
3168         
3169         /**
3170          * The number of pixels to scroll in each scroll increment (defaults to 50)
3171          * @type Number
3172          */
3173         increment : 100,
3174         
3175         /**
3176          * The frequency of scrolls in milliseconds (defaults to 500)
3177          * @type Number
3178          */
3179         frequency : 500,
3180         
3181         /**
3182          * True to animate the scroll (defaults to true)
3183          * @type Boolean
3184          */
3185         animate: true,
3186         
3187         /**
3188          * The animation duration in seconds - 
3189          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
3190          * @type Number
3191          */
3192         animDuration: .4,
3193         
3194         /**
3195          * Manually trigger a cache refresh.
3196          */
3197         refreshCache : function(){
3198             for(var id in els){
3199                 if(typeof els[id] == 'object'){ // for people extending the object prototype
3200                     els[id]._region = els[id].getRegion();
3201                 }
3202             }
3203         }
3204     };
3205 }();/*
3206  * Based on:
3207  * Ext JS Library 1.1.1
3208  * Copyright(c) 2006-2007, Ext JS, LLC.
3209  *
3210  * Originally Released Under LGPL - original licence link has changed is not relivant.
3211  *
3212  * Fork - LGPL
3213  * <script type="text/javascript">
3214  */
3215  
3216
3217 /**
3218  * @class Roo.dd.Registry
3219  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
3220  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
3221  * @singleton
3222  */
3223 Roo.dd.Registry = function(){
3224     var elements = {}; 
3225     var handles = {}; 
3226     var autoIdSeed = 0;
3227
3228     var getId = function(el, autogen){
3229         if(typeof el == "string"){
3230             return el;
3231         }
3232         var id = el.id;
3233         if(!id && autogen !== false){
3234             id = "roodd-" + (++autoIdSeed);
3235             el.id = id;
3236         }
3237         return id;
3238     };
3239     
3240     return {
3241     /**
3242      * Register a drag drop element
3243      * @param {String|HTMLElement} element The id or DOM node to register
3244      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
3245      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
3246      * knows how to interpret, plus there are some specific properties known to the Registry that should be
3247      * populated in the data object (if applicable):
3248      * <pre>
3249 Value      Description<br />
3250 ---------  ------------------------------------------<br />
3251 handles    Array of DOM nodes that trigger dragging<br />
3252            for the element being registered<br />
3253 isHandle   True if the element passed in triggers<br />
3254            dragging itself, else false
3255 </pre>
3256      */
3257         register : function(el, data){
3258             data = data || {};
3259             if(typeof el == "string"){
3260                 el = document.getElementById(el);
3261             }
3262             data.ddel = el;
3263             elements[getId(el)] = data;
3264             if(data.isHandle !== false){
3265                 handles[data.ddel.id] = data;
3266             }
3267             if(data.handles){
3268                 var hs = data.handles;
3269                 for(var i = 0, len = hs.length; i < len; i++){
3270                         handles[getId(hs[i])] = data;
3271                 }
3272             }
3273         },
3274
3275     /**
3276      * Unregister a drag drop element
3277      * @param {String|HTMLElement}  element The id or DOM node to unregister
3278      */
3279         unregister : function(el){
3280             var id = getId(el, false);
3281             var data = elements[id];
3282             if(data){
3283                 delete elements[id];
3284                 if(data.handles){
3285                     var hs = data.handles;
3286                     for(var i = 0, len = hs.length; i < len; i++){
3287                         delete handles[getId(hs[i], false)];
3288                     }
3289                 }
3290             }
3291         },
3292
3293     /**
3294      * Returns the handle registered for a DOM Node by id
3295      * @param {String|HTMLElement} id The DOM node or id to look up
3296      * @return {Object} handle The custom handle data
3297      */
3298         getHandle : function(id){
3299             if(typeof id != "string"){ // must be element?
3300                 id = id.id;
3301             }
3302             return handles[id];
3303         },
3304
3305     /**
3306      * Returns the handle that is registered for the DOM node that is the target of the event
3307      * @param {Event} e The event
3308      * @return {Object} handle The custom handle data
3309      */
3310         getHandleFromEvent : function(e){
3311             var t = Roo.lib.Event.getTarget(e);
3312             return t ? handles[t.id] : null;
3313         },
3314
3315     /**
3316      * Returns a custom data object that is registered for a DOM node by id
3317      * @param {String|HTMLElement} id The DOM node or id to look up
3318      * @return {Object} data The custom data
3319      */
3320         getTarget : function(id){
3321             if(typeof id != "string"){ // must be element?
3322                 id = id.id;
3323             }
3324             return elements[id];
3325         },
3326
3327     /**
3328      * Returns a custom data object that is registered for the DOM node that is the target of the event
3329      * @param {Event} e The event
3330      * @return {Object} data The custom data
3331      */
3332         getTargetFromEvent : function(e){
3333             var t = Roo.lib.Event.getTarget(e);
3334             return t ? elements[t.id] || handles[t.id] : null;
3335         }
3336     };
3337 }();/*
3338  * Based on:
3339  * Ext JS Library 1.1.1
3340  * Copyright(c) 2006-2007, Ext JS, LLC.
3341  *
3342  * Originally Released Under LGPL - original licence link has changed is not relivant.
3343  *
3344  * Fork - LGPL
3345  * <script type="text/javascript">
3346  */
3347  
3348
3349 /**
3350  * @class Roo.dd.StatusProxy
3351  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
3352  * default drag proxy used by all Roo.dd components.
3353  * @constructor
3354  * @param {Object} config
3355  */
3356 Roo.dd.StatusProxy = function(config){
3357     Roo.apply(this, config);
3358     this.id = this.id || Roo.id();
3359     this.el = new Roo.Layer({
3360         dh: {
3361             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
3362                 {tag: "div", cls: "x-dd-drop-icon"},
3363                 {tag: "div", cls: "x-dd-drag-ghost"}
3364             ]
3365         }, 
3366         shadow: !config || config.shadow !== false
3367     });
3368     this.ghost = Roo.get(this.el.dom.childNodes[1]);
3369     this.dropStatus = this.dropNotAllowed;
3370 };
3371
3372 Roo.dd.StatusProxy.prototype = {
3373     /**
3374      * @cfg {String} dropAllowed
3375      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
3376      */
3377     dropAllowed : "x-dd-drop-ok",
3378     /**
3379      * @cfg {String} dropNotAllowed
3380      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
3381      */
3382     dropNotAllowed : "x-dd-drop-nodrop",
3383
3384     /**
3385      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
3386      * over the current target element.
3387      * @param {String} cssClass The css class for the new drop status indicator image
3388      */
3389     setStatus : function(cssClass){
3390         cssClass = cssClass || this.dropNotAllowed;
3391         if(this.dropStatus != cssClass){
3392             this.el.replaceClass(this.dropStatus, cssClass);
3393             this.dropStatus = cssClass;
3394         }
3395     },
3396
3397     /**
3398      * Resets the status indicator to the default dropNotAllowed value
3399      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
3400      */
3401     reset : function(clearGhost){
3402         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
3403         this.dropStatus = this.dropNotAllowed;
3404         if(clearGhost){
3405             this.ghost.update("");
3406         }
3407     },
3408
3409     /**
3410      * Updates the contents of the ghost element
3411      * @param {String} html The html that will replace the current innerHTML of the ghost element
3412      */
3413     update : function(html){
3414         if(typeof html == "string"){
3415             this.ghost.update(html);
3416         }else{
3417             this.ghost.update("");
3418             html.style.margin = "0";
3419             this.ghost.dom.appendChild(html);
3420         }
3421         // ensure float = none set?? cant remember why though.
3422         var el = this.ghost.dom.firstChild;
3423                 if(el){
3424                         Roo.fly(el).setStyle('float', 'none');
3425                 }
3426     },
3427     
3428     /**
3429      * Returns the underlying proxy {@link Roo.Layer}
3430      * @return {Roo.Layer} el
3431     */
3432     getEl : function(){
3433         return this.el;
3434     },
3435
3436     /**
3437      * Returns the ghost element
3438      * @return {Roo.Element} el
3439      */
3440     getGhost : function(){
3441         return this.ghost;
3442     },
3443
3444     /**
3445      * Hides the proxy
3446      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
3447      */
3448     hide : function(clear){
3449         this.el.hide();
3450         if(clear){
3451             this.reset(true);
3452         }
3453     },
3454
3455     /**
3456      * Stops the repair animation if it's currently running
3457      */
3458     stop : function(){
3459         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
3460             this.anim.stop();
3461         }
3462     },
3463
3464     /**
3465      * Displays this proxy
3466      */
3467     show : function(){
3468         this.el.show();
3469     },
3470
3471     /**
3472      * Force the Layer to sync its shadow and shim positions to the element
3473      */
3474     sync : function(){
3475         this.el.sync();
3476     },
3477
3478     /**
3479      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
3480      * invalid drop operation by the item being dragged.
3481      * @param {Array} xy The XY position of the element ([x, y])
3482      * @param {Function} callback The function to call after the repair is complete
3483      * @param {Object} scope The scope in which to execute the callback
3484      */
3485     repair : function(xy, callback, scope){
3486         this.callback = callback;
3487         this.scope = scope;
3488         if(xy && this.animRepair !== false){
3489             this.el.addClass("x-dd-drag-repair");
3490             this.el.hideUnders(true);
3491             this.anim = this.el.shift({
3492                 duration: this.repairDuration || .5,
3493                 easing: 'easeOut',
3494                 xy: xy,
3495                 stopFx: true,
3496                 callback: this.afterRepair,
3497                 scope: this
3498             });
3499         }else{
3500             this.afterRepair();
3501         }
3502     },
3503
3504     // private
3505     afterRepair : function(){
3506         this.hide(true);
3507         if(typeof this.callback == "function"){
3508             this.callback.call(this.scope || this);
3509         }
3510         this.callback = null;
3511         this.scope = null;
3512     }
3513 };/*
3514  * Based on:
3515  * Ext JS Library 1.1.1
3516  * Copyright(c) 2006-2007, Ext JS, LLC.
3517  *
3518  * Originally Released Under LGPL - original licence link has changed is not relivant.
3519  *
3520  * Fork - LGPL
3521  * <script type="text/javascript">
3522  */
3523
3524 /**
3525  * @class Roo.dd.DragSource
3526  * @extends Roo.dd.DDProxy
3527  * A simple class that provides the basic implementation needed to make any element draggable.
3528  * @constructor
3529  * @param {String/HTMLElement/Element} el The container element
3530  * @param {Object} config
3531  */
3532 Roo.dd.DragSource = function(el, config){
3533     this.el = Roo.get(el);
3534     this.dragData = {};
3535     
3536     Roo.apply(this, config);
3537     
3538     if(!this.proxy){
3539         this.proxy = new Roo.dd.StatusProxy();
3540     }
3541
3542     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
3543           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
3544     
3545     this.dragging = false;
3546 };
3547
3548 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
3549     /**
3550      * @cfg {String} dropAllowed
3551      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3552      */
3553     dropAllowed : "x-dd-drop-ok",
3554     /**
3555      * @cfg {String} dropNotAllowed
3556      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
3557      */
3558     dropNotAllowed : "x-dd-drop-nodrop",
3559
3560     /**
3561      * Returns the data object associated with this drag source
3562      * @return {Object} data An object containing arbitrary data
3563      */
3564     getDragData : function(e){
3565         return this.dragData;
3566     },
3567
3568     // private
3569     onDragEnter : function(e, id){
3570         var target = Roo.dd.DragDropMgr.getDDById(id);
3571         this.cachedTarget = target;
3572         if(this.beforeDragEnter(target, e, id) !== false){
3573             if(target.isNotifyTarget){
3574                 var status = target.notifyEnter(this, e, this.dragData);
3575                 this.proxy.setStatus(status);
3576             }else{
3577                 this.proxy.setStatus(this.dropAllowed);
3578             }
3579             
3580             if(this.afterDragEnter){
3581                 /**
3582                  * An empty function by default, but provided so that you can perform a custom action
3583                  * when the dragged item enters the drop target by providing an implementation.
3584                  * @param {Roo.dd.DragDrop} target The drop target
3585                  * @param {Event} e The event object
3586                  * @param {String} id The id of the dragged element
3587                  * @method afterDragEnter
3588                  */
3589                 this.afterDragEnter(target, e, id);
3590             }
3591         }
3592     },
3593
3594     /**
3595      * An empty function by default, but provided so that you can perform a custom action
3596      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
3597      * @param {Roo.dd.DragDrop} target The drop target
3598      * @param {Event} e The event object
3599      * @param {String} id The id of the dragged element
3600      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3601      */
3602     beforeDragEnter : function(target, e, id){
3603         return true;
3604     },
3605
3606     // private
3607     alignElWithMouse: function() {
3608         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
3609         this.proxy.sync();
3610     },
3611
3612     // private
3613     onDragOver : function(e, id){
3614         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3615         if(this.beforeDragOver(target, e, id) !== false){
3616             if(target.isNotifyTarget){
3617                 var status = target.notifyOver(this, e, this.dragData);
3618                 this.proxy.setStatus(status);
3619             }
3620
3621             if(this.afterDragOver){
3622                 /**
3623                  * An empty function by default, but provided so that you can perform a custom action
3624                  * while the dragged item is over the drop target by providing an implementation.
3625                  * @param {Roo.dd.DragDrop} target The drop target
3626                  * @param {Event} e The event object
3627                  * @param {String} id The id of the dragged element
3628                  * @method afterDragOver
3629                  */
3630                 this.afterDragOver(target, e, id);
3631             }
3632         }
3633     },
3634
3635     /**
3636      * An empty function by default, but provided so that you can perform a custom action
3637      * while the dragged item is over the drop target and optionally cancel the onDragOver.
3638      * @param {Roo.dd.DragDrop} target The drop target
3639      * @param {Event} e The event object
3640      * @param {String} id The id of the dragged element
3641      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3642      */
3643     beforeDragOver : function(target, e, id){
3644         return true;
3645     },
3646
3647     // private
3648     onDragOut : function(e, id){
3649         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3650         if(this.beforeDragOut(target, e, id) !== false){
3651             if(target.isNotifyTarget){
3652                 target.notifyOut(this, e, this.dragData);
3653             }
3654             this.proxy.reset();
3655             if(this.afterDragOut){
3656                 /**
3657                  * An empty function by default, but provided so that you can perform a custom action
3658                  * after the dragged item is dragged out of the target without dropping.
3659                  * @param {Roo.dd.DragDrop} target The drop target
3660                  * @param {Event} e The event object
3661                  * @param {String} id The id of the dragged element
3662                  * @method afterDragOut
3663                  */
3664                 this.afterDragOut(target, e, id);
3665             }
3666         }
3667         this.cachedTarget = null;
3668     },
3669
3670     /**
3671      * An empty function by default, but provided so that you can perform a custom action before the dragged
3672      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
3673      * @param {Roo.dd.DragDrop} target The drop target
3674      * @param {Event} e The event object
3675      * @param {String} id The id of the dragged element
3676      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3677      */
3678     beforeDragOut : function(target, e, id){
3679         return true;
3680     },
3681     
3682     // private
3683     onDragDrop : function(e, id){
3684         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
3685         if(this.beforeDragDrop(target, e, id) !== false){
3686             if(target.isNotifyTarget){
3687                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
3688                     this.onValidDrop(target, e, id);
3689                 }else{
3690                     this.onInvalidDrop(target, e, id);
3691                 }
3692             }else{
3693                 this.onValidDrop(target, e, id);
3694             }
3695             
3696             if(this.afterDragDrop){
3697                 /**
3698                  * An empty function by default, but provided so that you can perform a custom action
3699                  * after a valid drag drop has occurred by providing an implementation.
3700                  * @param {Roo.dd.DragDrop} target The drop target
3701                  * @param {Event} e The event object
3702                  * @param {String} id The id of the dropped element
3703                  * @method afterDragDrop
3704                  */
3705                 this.afterDragDrop(target, e, id);
3706             }
3707         }
3708         delete this.cachedTarget;
3709     },
3710
3711     /**
3712      * An empty function by default, but provided so that you can perform a custom action before the dragged
3713      * item is dropped onto the target and optionally cancel the onDragDrop.
3714      * @param {Roo.dd.DragDrop} target The drop target
3715      * @param {Event} e The event object
3716      * @param {String} id The id of the dragged element
3717      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
3718      */
3719     beforeDragDrop : function(target, e, id){
3720         return true;
3721     },
3722
3723     // private
3724     onValidDrop : function(target, e, id){
3725         this.hideProxy();
3726         if(this.afterValidDrop){
3727             /**
3728              * An empty function by default, but provided so that you can perform a custom action
3729              * after a valid drop has occurred by providing an implementation.
3730              * @param {Object} target The target DD 
3731              * @param {Event} e The event object
3732              * @param {String} id The id of the dropped element
3733              * @method afterInvalidDrop
3734              */
3735             this.afterValidDrop(target, e, id);
3736         }
3737     },
3738
3739     // private
3740     getRepairXY : function(e, data){
3741         return this.el.getXY();  
3742     },
3743
3744     // private
3745     onInvalidDrop : function(target, e, id){
3746         this.beforeInvalidDrop(target, e, id);
3747         if(this.cachedTarget){
3748             if(this.cachedTarget.isNotifyTarget){
3749                 this.cachedTarget.notifyOut(this, e, this.dragData);
3750             }
3751             this.cacheTarget = null;
3752         }
3753         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
3754
3755         if(this.afterInvalidDrop){
3756             /**
3757              * An empty function by default, but provided so that you can perform a custom action
3758              * after an invalid drop has occurred by providing an implementation.
3759              * @param {Event} e The event object
3760              * @param {String} id The id of the dropped element
3761              * @method afterInvalidDrop
3762              */
3763             this.afterInvalidDrop(e, id);
3764         }
3765     },
3766
3767     // private
3768     afterRepair : function(){
3769         if(Roo.enableFx){
3770             this.el.highlight(this.hlColor || "c3daf9");
3771         }
3772         this.dragging = false;
3773     },
3774
3775     /**
3776      * An empty function by default, but provided so that you can perform a custom action after an invalid
3777      * drop has occurred.
3778      * @param {Roo.dd.DragDrop} target The drop target
3779      * @param {Event} e The event object
3780      * @param {String} id The id of the dragged element
3781      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
3782      */
3783     beforeInvalidDrop : function(target, e, id){
3784         return true;
3785     },
3786
3787     // private
3788     handleMouseDown : function(e){
3789         if(this.dragging) {
3790             return;
3791         }
3792         var data = this.getDragData(e);
3793         if(data && this.onBeforeDrag(data, e) !== false){
3794             this.dragData = data;
3795             this.proxy.stop();
3796             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
3797         } 
3798     },
3799
3800     /**
3801      * An empty function by default, but provided so that you can perform a custom action before the initial
3802      * drag event begins and optionally cancel it.
3803      * @param {Object} data An object containing arbitrary data to be shared with drop targets
3804      * @param {Event} e The event object
3805      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
3806      */
3807     onBeforeDrag : function(data, e){
3808         return true;
3809     },
3810
3811     /**
3812      * An empty function by default, but provided so that you can perform a custom action once the initial
3813      * drag event has begun.  The drag cannot be canceled from this function.
3814      * @param {Number} x The x position of the click on the dragged object
3815      * @param {Number} y The y position of the click on the dragged object
3816      */
3817     onStartDrag : Roo.emptyFn,
3818
3819     // private - YUI override
3820     startDrag : function(x, y){
3821         this.proxy.reset();
3822         this.dragging = true;
3823         this.proxy.update("");
3824         this.onInitDrag(x, y);
3825         this.proxy.show();
3826     },
3827
3828     // private
3829     onInitDrag : function(x, y){
3830         var clone = this.el.dom.cloneNode(true);
3831         clone.id = Roo.id(); // prevent duplicate ids
3832         this.proxy.update(clone);
3833         this.onStartDrag(x, y);
3834         return true;
3835     },
3836
3837     /**
3838      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
3839      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
3840      */
3841     getProxy : function(){
3842         return this.proxy;  
3843     },
3844
3845     /**
3846      * Hides the drag source's {@link Roo.dd.StatusProxy}
3847      */
3848     hideProxy : function(){
3849         this.proxy.hide();  
3850         this.proxy.reset(true);
3851         this.dragging = false;
3852     },
3853
3854     // private
3855     triggerCacheRefresh : function(){
3856         Roo.dd.DDM.refreshCache(this.groups);
3857     },
3858
3859     // private - override to prevent hiding
3860     b4EndDrag: function(e) {
3861     },
3862
3863     // private - override to prevent moving
3864     endDrag : function(e){
3865         this.onEndDrag(this.dragData, e);
3866     },
3867
3868     // private
3869     onEndDrag : function(data, e){
3870     },
3871     
3872     // private - pin to cursor
3873     autoOffset : function(x, y) {
3874         this.setDelta(-12, -20);
3875     }    
3876 });/*
3877  * Based on:
3878  * Ext JS Library 1.1.1
3879  * Copyright(c) 2006-2007, Ext JS, LLC.
3880  *
3881  * Originally Released Under LGPL - original licence link has changed is not relivant.
3882  *
3883  * Fork - LGPL
3884  * <script type="text/javascript">
3885  */
3886
3887
3888 /**
3889  * @class Roo.dd.DropTarget
3890  * @extends Roo.dd.DDTarget
3891  * A simple class that provides the basic implementation needed to make any element a drop target that can have
3892  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
3893  * @constructor
3894  * @param {String/HTMLElement/Element} el The container element
3895  * @param {Object} config
3896  */
3897 Roo.dd.DropTarget = function(el, config){
3898     this.el = Roo.get(el);
3899     
3900     var listeners = false; ;
3901     if (config && config.listeners) {
3902         listeners= config.listeners;
3903         delete config.listeners;
3904     }
3905     Roo.apply(this, config);
3906     
3907     if(this.containerScroll){
3908         Roo.dd.ScrollManager.register(this.el);
3909     }
3910     this.addEvents( {
3911          /**
3912          * @scope Roo.dd.DropTarget
3913          */
3914          
3915          /**
3916          * @event enter
3917          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
3918          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
3919          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
3920          * 
3921          * IMPORTANT : it should set this.overClass and this.dropAllowed
3922          * 
3923          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3924          * @param {Event} e The event
3925          * @param {Object} data An object containing arbitrary data supplied by the drag source
3926          */
3927         "enter" : true,
3928         
3929          /**
3930          * @event over
3931          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
3932          * This method will be called on every mouse movement while the drag source is over the drop target.
3933          * This default implementation simply returns the dropAllowed config value.
3934          * 
3935          * IMPORTANT : it should set this.dropAllowed
3936          * 
3937          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3938          * @param {Event} e The event
3939          * @param {Object} data An object containing arbitrary data supplied by the drag source
3940          
3941          */
3942         "over" : true,
3943         /**
3944          * @event out
3945          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
3946          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
3947          * overClass (if any) from the drop element.
3948          * 
3949          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3950          * @param {Event} e The event
3951          * @param {Object} data An object containing arbitrary data supplied by the drag source
3952          */
3953          "out" : true,
3954          
3955         /**
3956          * @event drop
3957          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
3958          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
3959          * implementation that does something to process the drop event and returns true so that the drag source's
3960          * repair action does not run.
3961          * 
3962          * IMPORTANT : it should set this.success
3963          * 
3964          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
3965          * @param {Event} e The event
3966          * @param {Object} data An object containing arbitrary data supplied by the drag source
3967         */
3968          "drop" : true
3969     });
3970             
3971      
3972     Roo.dd.DropTarget.superclass.constructor.call(  this, 
3973         this.el.dom, 
3974         this.ddGroup || this.group,
3975         {
3976             isTarget: true,
3977             listeners : listeners || {} 
3978            
3979         
3980         }
3981     );
3982
3983 };
3984
3985 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
3986     /**
3987      * @cfg {String} overClass
3988      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
3989      */
3990      /**
3991      * @cfg {String} ddGroup
3992      * The drag drop group to handle drop events for
3993      */
3994      
3995     /**
3996      * @cfg {String} dropAllowed
3997      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
3998      */
3999     dropAllowed : "x-dd-drop-ok",
4000     /**
4001      * @cfg {String} dropNotAllowed
4002      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
4003      */
4004     dropNotAllowed : "x-dd-drop-nodrop",
4005     /**
4006      * @cfg {boolean} success
4007      * set this after drop listener.. 
4008      */
4009     success : false,
4010     /**
4011      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
4012      * if the drop point is valid for over/enter..
4013      */
4014     valid : false,
4015     // private
4016     isTarget : true,
4017
4018     // private
4019     isNotifyTarget : true,
4020     
4021     /**
4022      * @hide
4023      */
4024     notifyEnter : function(dd, e, data)
4025     {
4026         this.valid = true;
4027         this.fireEvent('enter', dd, e, data);
4028         if(this.overClass){
4029             this.el.addClass(this.overClass);
4030         }
4031         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4032             this.valid ? this.dropAllowed : this.dropNotAllowed
4033         );
4034     },
4035
4036     /**
4037      * @hide
4038      */
4039     notifyOver : function(dd, e, data)
4040     {
4041         this.valid = true;
4042         this.fireEvent('over', dd, e, data);
4043         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
4044             this.valid ? this.dropAllowed : this.dropNotAllowed
4045         );
4046     },
4047
4048     /**
4049      * @hide
4050      */
4051     notifyOut : function(dd, e, data)
4052     {
4053         this.fireEvent('out', dd, e, data);
4054         if(this.overClass){
4055             this.el.removeClass(this.overClass);
4056         }
4057     },
4058
4059     /**
4060      * @hide
4061      */
4062     notifyDrop : function(dd, e, data)
4063     {
4064         this.success = false;
4065         this.fireEvent('drop', dd, e, data);
4066         return this.success;
4067     }
4068 });/*
4069  * Based on:
4070  * Ext JS Library 1.1.1
4071  * Copyright(c) 2006-2007, Ext JS, LLC.
4072  *
4073  * Originally Released Under LGPL - original licence link has changed is not relivant.
4074  *
4075  * Fork - LGPL
4076  * <script type="text/javascript">
4077  */
4078
4079
4080 /**
4081  * @class Roo.dd.DragZone
4082  * @extends Roo.dd.DragSource
4083  * This class provides a container DD instance that proxies for multiple child node sources.<br />
4084  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
4085  * @constructor
4086  * @param {String/HTMLElement/Element} el The container element
4087  * @param {Object} config
4088  */
4089 Roo.dd.DragZone = function(el, config){
4090     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
4091     if(this.containerScroll){
4092         Roo.dd.ScrollManager.register(this.el);
4093     }
4094 };
4095
4096 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
4097     /**
4098      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
4099      * for auto scrolling during drag operations.
4100      */
4101     /**
4102      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
4103      * method after a failed drop (defaults to "c3daf9" - light blue)
4104      */
4105
4106     /**
4107      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
4108      * for a valid target to drag based on the mouse down. Override this method
4109      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
4110      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
4111      * @param {EventObject} e The mouse down event
4112      * @return {Object} The dragData
4113      */
4114     getDragData : function(e){
4115         return Roo.dd.Registry.getHandleFromEvent(e);
4116     },
4117     
4118     /**
4119      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
4120      * this.dragData.ddel
4121      * @param {Number} x The x position of the click on the dragged object
4122      * @param {Number} y The y position of the click on the dragged object
4123      * @return {Boolean} true to continue the drag, false to cancel
4124      */
4125     onInitDrag : function(x, y){
4126         this.proxy.update(this.dragData.ddel.cloneNode(true));
4127         this.onStartDrag(x, y);
4128         return true;
4129     },
4130     
4131     /**
4132      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
4133      */
4134     afterRepair : function(){
4135         if(Roo.enableFx){
4136             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
4137         }
4138         this.dragging = false;
4139     },
4140
4141     /**
4142      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
4143      * the XY of this.dragData.ddel
4144      * @param {EventObject} e The mouse up event
4145      * @return {Array} The xy location (e.g. [100, 200])
4146      */
4147     getRepairXY : function(e){
4148         return Roo.Element.fly(this.dragData.ddel).getXY();  
4149     }
4150 });/*
4151  * Based on:
4152  * Ext JS Library 1.1.1
4153  * Copyright(c) 2006-2007, Ext JS, LLC.
4154  *
4155  * Originally Released Under LGPL - original licence link has changed is not relivant.
4156  *
4157  * Fork - LGPL
4158  * <script type="text/javascript">
4159  */
4160 /**
4161  * @class Roo.dd.DropZone
4162  * @extends Roo.dd.DropTarget
4163  * This class provides a container DD instance that proxies for multiple child node targets.<br />
4164  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
4165  * @constructor
4166  * @param {String/HTMLElement/Element} el The container element
4167  * @param {Object} config
4168  */
4169 Roo.dd.DropZone = function(el, config){
4170     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
4171 };
4172
4173 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
4174     /**
4175      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
4176      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
4177      * provide your own custom lookup.
4178      * @param {Event} e The event
4179      * @return {Object} data The custom data
4180      */
4181     getTargetFromEvent : function(e){
4182         return Roo.dd.Registry.getTargetFromEvent(e);
4183     },
4184
4185     /**
4186      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
4187      * that it has registered.  This method has no default implementation and should be overridden to provide
4188      * node-specific processing if necessary.
4189      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
4190      * {@link #getTargetFromEvent} for this node)
4191      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4192      * @param {Event} e The event
4193      * @param {Object} data An object containing arbitrary data supplied by the drag source
4194      */
4195     onNodeEnter : function(n, dd, e, data){
4196         
4197     },
4198
4199     /**
4200      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
4201      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
4202      * overridden to provide the proper feedback.
4203      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4204      * {@link #getTargetFromEvent} for this node)
4205      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4206      * @param {Event} e The event
4207      * @param {Object} data An object containing arbitrary data supplied by the drag source
4208      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4209      * underlying {@link Roo.dd.StatusProxy} can be updated
4210      */
4211     onNodeOver : function(n, dd, e, data){
4212         return this.dropAllowed;
4213     },
4214
4215     /**
4216      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
4217      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
4218      * node-specific processing if necessary.
4219      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4220      * {@link #getTargetFromEvent} for this node)
4221      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4222      * @param {Event} e The event
4223      * @param {Object} data An object containing arbitrary data supplied by the drag source
4224      */
4225     onNodeOut : function(n, dd, e, data){
4226         
4227     },
4228
4229     /**
4230      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
4231      * the drop node.  The default implementation returns false, so it should be overridden to provide the
4232      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
4233      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
4234      * {@link #getTargetFromEvent} for this node)
4235      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4236      * @param {Event} e The event
4237      * @param {Object} data An object containing arbitrary data supplied by the drag source
4238      * @return {Boolean} True if the drop was valid, else false
4239      */
4240     onNodeDrop : function(n, dd, e, data){
4241         return false;
4242     },
4243
4244     /**
4245      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
4246      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
4247      * it should be overridden to provide the proper feedback if necessary.
4248      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4249      * @param {Event} e The event
4250      * @param {Object} data An object containing arbitrary data supplied by the drag source
4251      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4252      * underlying {@link Roo.dd.StatusProxy} can be updated
4253      */
4254     onContainerOver : function(dd, e, data){
4255         return this.dropNotAllowed;
4256     },
4257
4258     /**
4259      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
4260      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
4261      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
4262      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
4263      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4264      * @param {Event} e The event
4265      * @param {Object} data An object containing arbitrary data supplied by the drag source
4266      * @return {Boolean} True if the drop was valid, else false
4267      */
4268     onContainerDrop : function(dd, e, data){
4269         return false;
4270     },
4271
4272     /**
4273      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
4274      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
4275      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
4276      * you should override this method and provide a custom implementation.
4277      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4278      * @param {Event} e The event
4279      * @param {Object} data An object containing arbitrary data supplied by the drag source
4280      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4281      * underlying {@link Roo.dd.StatusProxy} can be updated
4282      */
4283     notifyEnter : function(dd, e, data){
4284         return this.dropNotAllowed;
4285     },
4286
4287     /**
4288      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
4289      * This method will be called on every mouse movement while the drag source is over the drop zone.
4290      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
4291      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
4292      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
4293      * registered node, it will call {@link #onContainerOver}.
4294      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4295      * @param {Event} e The event
4296      * @param {Object} data An object containing arbitrary data supplied by the drag source
4297      * @return {String} status The CSS class that communicates the drop status back to the source so that the
4298      * underlying {@link Roo.dd.StatusProxy} can be updated
4299      */
4300     notifyOver : function(dd, e, data){
4301         var n = this.getTargetFromEvent(e);
4302         if(!n){ // not over valid drop target
4303             if(this.lastOverNode){
4304                 this.onNodeOut(this.lastOverNode, dd, e, data);
4305                 this.lastOverNode = null;
4306             }
4307             return this.onContainerOver(dd, e, data);
4308         }
4309         if(this.lastOverNode != n){
4310             if(this.lastOverNode){
4311                 this.onNodeOut(this.lastOverNode, dd, e, data);
4312             }
4313             this.onNodeEnter(n, dd, e, data);
4314             this.lastOverNode = n;
4315         }
4316         return this.onNodeOver(n, dd, e, data);
4317     },
4318
4319     /**
4320      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
4321      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
4322      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
4323      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
4324      * @param {Event} e The event
4325      * @param {Object} data An object containing arbitrary data supplied by the drag zone
4326      */
4327     notifyOut : function(dd, e, data){
4328         if(this.lastOverNode){
4329             this.onNodeOut(this.lastOverNode, dd, e, data);
4330             this.lastOverNode = null;
4331         }
4332     },
4333
4334     /**
4335      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
4336      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
4337      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
4338      * otherwise it will call {@link #onContainerDrop}.
4339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
4340      * @param {Event} e The event
4341      * @param {Object} data An object containing arbitrary data supplied by the drag source
4342      * @return {Boolean} True if the drop was valid, else false
4343      */
4344     notifyDrop : function(dd, e, data){
4345         if(this.lastOverNode){
4346             this.onNodeOut(this.lastOverNode, dd, e, data);
4347             this.lastOverNode = null;
4348         }
4349         var n = this.getTargetFromEvent(e);
4350         return n ?
4351             this.onNodeDrop(n, dd, e, data) :
4352             this.onContainerDrop(dd, e, data);
4353     },
4354
4355     // private
4356     triggerCacheRefresh : function(){
4357         Roo.dd.DDM.refreshCache(this.groups);
4358     }  
4359 });/*
4360  * Based on:
4361  * Ext JS Library 1.1.1
4362  * Copyright(c) 2006-2007, Ext JS, LLC.
4363  *
4364  * Originally Released Under LGPL - original licence link has changed is not relivant.
4365  *
4366  * Fork - LGPL
4367  * <script type="text/javascript">
4368  */
4369
4370
4371 /**
4372  * @class Roo.data.SortTypes
4373  * @singleton
4374  * Defines the default sorting (casting?) comparison functions used when sorting data.
4375  */
4376 Roo.data.SortTypes = {
4377     /**
4378      * Default sort that does nothing
4379      * @param {Mixed} s The value being converted
4380      * @return {Mixed} The comparison value
4381      */
4382     none : function(s){
4383         return s;
4384     },
4385     
4386     /**
4387      * The regular expression used to strip tags
4388      * @type {RegExp}
4389      * @property
4390      */
4391     stripTagsRE : /<\/?[^>]+>/gi,
4392     
4393     /**
4394      * Strips all HTML tags to sort on text only
4395      * @param {Mixed} s The value being converted
4396      * @return {String} The comparison value
4397      */
4398     asText : function(s){
4399         return String(s).replace(this.stripTagsRE, "");
4400     },
4401     
4402     /**
4403      * Strips all HTML tags to sort on text only - Case insensitive
4404      * @param {Mixed} s The value being converted
4405      * @return {String} The comparison value
4406      */
4407     asUCText : function(s){
4408         return String(s).toUpperCase().replace(this.stripTagsRE, "");
4409     },
4410     
4411     /**
4412      * Case insensitive string
4413      * @param {Mixed} s The value being converted
4414      * @return {String} The comparison value
4415      */
4416     asUCString : function(s) {
4417         return String(s).toUpperCase();
4418     },
4419     
4420     /**
4421      * Date sorting
4422      * @param {Mixed} s The value being converted
4423      * @return {Number} The comparison value
4424      */
4425     asDate : function(s) {
4426         if(!s){
4427             return 0;
4428         }
4429         if(s instanceof Date){
4430             return s.getTime();
4431         }
4432         return Date.parse(String(s));
4433     },
4434     
4435     /**
4436      * Float sorting
4437      * @param {Mixed} s The value being converted
4438      * @return {Float} The comparison value
4439      */
4440     asFloat : function(s) {
4441         var val = parseFloat(String(s).replace(/,/g, ""));
4442         if(isNaN(val)) val = 0;
4443         return val;
4444     },
4445     
4446     /**
4447      * Integer sorting
4448      * @param {Mixed} s The value being converted
4449      * @return {Number} The comparison value
4450      */
4451     asInt : function(s) {
4452         var val = parseInt(String(s).replace(/,/g, ""));
4453         if(isNaN(val)) val = 0;
4454         return val;
4455     }
4456 };/*
4457  * Based on:
4458  * Ext JS Library 1.1.1
4459  * Copyright(c) 2006-2007, Ext JS, LLC.
4460  *
4461  * Originally Released Under LGPL - original licence link has changed is not relivant.
4462  *
4463  * Fork - LGPL
4464  * <script type="text/javascript">
4465  */
4466
4467 /**
4468 * @class Roo.data.Record
4469  * Instances of this class encapsulate both record <em>definition</em> information, and record
4470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
4471  * to access Records cached in an {@link Roo.data.Store} object.<br>
4472  * <p>
4473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
4474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
4475  * objects.<br>
4476  * <p>
4477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
4478  * @constructor
4479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
4480  * {@link #create}. The parameters are the same.
4481  * @param {Array} data An associative Array of data values keyed by the field name.
4482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
4483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
4484  * not specified an integer id is generated.
4485  */
4486 Roo.data.Record = function(data, id){
4487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
4488     this.data = data;
4489 };
4490
4491 /**
4492  * Generate a constructor for a specific record layout.
4493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
4494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
4495  * Each field definition object may contain the following properties: <ul>
4496  * <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,
4497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
4498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
4499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
4500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
4501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
4502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
4503  * this may be omitted.</p></li>
4504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
4505  * <ul><li>auto (Default, implies no conversion)</li>
4506  * <li>string</li>
4507  * <li>int</li>
4508  * <li>float</li>
4509  * <li>boolean</li>
4510  * <li>date</li></ul></p></li>
4511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
4512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
4513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
4514  * by the Reader into an object that will be stored in the Record. It is passed the
4515  * following parameters:<ul>
4516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
4517  * </ul></p></li>
4518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
4519  * </ul>
4520  * <br>usage:<br><pre><code>
4521 var TopicRecord = Roo.data.Record.create(
4522     {name: 'title', mapping: 'topic_title'},
4523     {name: 'author', mapping: 'username'},
4524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
4525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
4526     {name: 'lastPoster', mapping: 'user2'},
4527     {name: 'excerpt', mapping: 'post_text'}
4528 );
4529
4530 var myNewRecord = new TopicRecord({
4531     title: 'Do my job please',
4532     author: 'noobie',
4533     totalPosts: 1,
4534     lastPost: new Date(),
4535     lastPoster: 'Animal',
4536     excerpt: 'No way dude!'
4537 });
4538 myStore.add(myNewRecord);
4539 </code></pre>
4540  * @method create
4541  * @static
4542  */
4543 Roo.data.Record.create = function(o){
4544     var f = function(){
4545         f.superclass.constructor.apply(this, arguments);
4546     };
4547     Roo.extend(f, Roo.data.Record);
4548     var p = f.prototype;
4549     p.fields = new Roo.util.MixedCollection(false, function(field){
4550         return field.name;
4551     });
4552     for(var i = 0, len = o.length; i < len; i++){
4553         p.fields.add(new Roo.data.Field(o[i]));
4554     }
4555     f.getField = function(name){
4556         return p.fields.get(name);  
4557     };
4558     return f;
4559 };
4560
4561 Roo.data.Record.AUTO_ID = 1000;
4562 Roo.data.Record.EDIT = 'edit';
4563 Roo.data.Record.REJECT = 'reject';
4564 Roo.data.Record.COMMIT = 'commit';
4565
4566 Roo.data.Record.prototype = {
4567     /**
4568      * Readonly flag - true if this record has been modified.
4569      * @type Boolean
4570      */
4571     dirty : false,
4572     editing : false,
4573     error: null,
4574     modified: null,
4575
4576     // private
4577     join : function(store){
4578         this.store = store;
4579     },
4580
4581     /**
4582      * Set the named field to the specified value.
4583      * @param {String} name The name of the field to set.
4584      * @param {Object} value The value to set the field to.
4585      */
4586     set : function(name, value){
4587         if(this.data[name] == value){
4588             return;
4589         }
4590         this.dirty = true;
4591         if(!this.modified){
4592             this.modified = {};
4593         }
4594         if(typeof this.modified[name] == 'undefined'){
4595             this.modified[name] = this.data[name];
4596         }
4597         this.data[name] = value;
4598         if(!this.editing && this.store){
4599             this.store.afterEdit(this);
4600         }       
4601     },
4602
4603     /**
4604      * Get the value of the named field.
4605      * @param {String} name The name of the field to get the value of.
4606      * @return {Object} The value of the field.
4607      */
4608     get : function(name){
4609         return this.data[name]; 
4610     },
4611
4612     // private
4613     beginEdit : function(){
4614         this.editing = true;
4615         this.modified = {}; 
4616     },
4617
4618     // private
4619     cancelEdit : function(){
4620         this.editing = false;
4621         delete this.modified;
4622     },
4623
4624     // private
4625     endEdit : function(){
4626         this.editing = false;
4627         if(this.dirty && this.store){
4628             this.store.afterEdit(this);
4629         }
4630     },
4631
4632     /**
4633      * Usually called by the {@link Roo.data.Store} which owns the Record.
4634      * Rejects all changes made to the Record since either creation, or the last commit operation.
4635      * Modified fields are reverted to their original values.
4636      * <p>
4637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4638      * of reject operations.
4639      */
4640     reject : function(){
4641         var m = this.modified;
4642         for(var n in m){
4643             if(typeof m[n] != "function"){
4644                 this.data[n] = m[n];
4645             }
4646         }
4647         this.dirty = false;
4648         delete this.modified;
4649         this.editing = false;
4650         if(this.store){
4651             this.store.afterReject(this);
4652         }
4653     },
4654
4655     /**
4656      * Usually called by the {@link Roo.data.Store} which owns the Record.
4657      * Commits all changes made to the Record since either creation, or the last commit operation.
4658      * <p>
4659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
4660      * of commit operations.
4661      */
4662     commit : function(){
4663         this.dirty = false;
4664         delete this.modified;
4665         this.editing = false;
4666         if(this.store){
4667             this.store.afterCommit(this);
4668         }
4669     },
4670
4671     // private
4672     hasError : function(){
4673         return this.error != null;
4674     },
4675
4676     // private
4677     clearError : function(){
4678         this.error = null;
4679     },
4680
4681     /**
4682      * Creates a copy of this record.
4683      * @param {String} id (optional) A new record id if you don't want to use this record's id
4684      * @return {Record}
4685      */
4686     copy : function(newId) {
4687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
4688     }
4689 };/*
4690  * Based on:
4691  * Ext JS Library 1.1.1
4692  * Copyright(c) 2006-2007, Ext JS, LLC.
4693  *
4694  * Originally Released Under LGPL - original licence link has changed is not relivant.
4695  *
4696  * Fork - LGPL
4697  * <script type="text/javascript">
4698  */
4699
4700
4701
4702 /**
4703  * @class Roo.data.Store
4704  * @extends Roo.util.Observable
4705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
4706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
4707  * <p>
4708  * 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
4709  * has no knowledge of the format of the data returned by the Proxy.<br>
4710  * <p>
4711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
4712  * instances from the data object. These records are cached and made available through accessor functions.
4713  * @constructor
4714  * Creates a new Store.
4715  * @param {Object} config A config object containing the objects needed for the Store to access data,
4716  * and read the data into Records.
4717  */
4718 Roo.data.Store = function(config){
4719     this.data = new Roo.util.MixedCollection(false);
4720     this.data.getKey = function(o){
4721         return o.id;
4722     };
4723     this.baseParams = {};
4724     // private
4725     this.paramNames = {
4726         "start" : "start",
4727         "limit" : "limit",
4728         "sort" : "sort",
4729         "dir" : "dir",
4730         "multisort" : "_multisort"
4731     };
4732
4733     if(config && config.data){
4734         this.inlineData = config.data;
4735         delete config.data;
4736     }
4737
4738     Roo.apply(this, config);
4739     
4740     if(this.reader){ // reader passed
4741         this.reader = Roo.factory(this.reader, Roo.data);
4742         this.reader.xmodule = this.xmodule || false;
4743         if(!this.recordType){
4744             this.recordType = this.reader.recordType;
4745         }
4746         if(this.reader.onMetaChange){
4747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
4748         }
4749     }
4750
4751     if(this.recordType){
4752         this.fields = this.recordType.prototype.fields;
4753     }
4754     this.modified = [];
4755
4756     this.addEvents({
4757         /**
4758          * @event datachanged
4759          * Fires when the data cache has changed, and a widget which is using this Store
4760          * as a Record cache should refresh its view.
4761          * @param {Store} this
4762          */
4763         datachanged : true,
4764         /**
4765          * @event metachange
4766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
4767          * @param {Store} this
4768          * @param {Object} meta The JSON metadata
4769          */
4770         metachange : true,
4771         /**
4772          * @event add
4773          * Fires when Records have been added to the Store
4774          * @param {Store} this
4775          * @param {Roo.data.Record[]} records The array of Records added
4776          * @param {Number} index The index at which the record(s) were added
4777          */
4778         add : true,
4779         /**
4780          * @event remove
4781          * Fires when a Record has been removed from the Store
4782          * @param {Store} this
4783          * @param {Roo.data.Record} record The Record that was removed
4784          * @param {Number} index The index at which the record was removed
4785          */
4786         remove : true,
4787         /**
4788          * @event update
4789          * Fires when a Record has been updated
4790          * @param {Store} this
4791          * @param {Roo.data.Record} record The Record that was updated
4792          * @param {String} operation The update operation being performed.  Value may be one of:
4793          * <pre><code>
4794  Roo.data.Record.EDIT
4795  Roo.data.Record.REJECT
4796  Roo.data.Record.COMMIT
4797          * </code></pre>
4798          */
4799         update : true,
4800         /**
4801          * @event clear
4802          * Fires when the data cache has been cleared.
4803          * @param {Store} this
4804          */
4805         clear : true,
4806         /**
4807          * @event beforeload
4808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
4809          * the load action will be canceled.
4810          * @param {Store} this
4811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4812          */
4813         beforeload : true,
4814         /**
4815          * @event load
4816          * Fires after a new set of Records has been loaded.
4817          * @param {Store} this
4818          * @param {Roo.data.Record[]} records The Records that were loaded
4819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
4820          */
4821         load : true,
4822         /**
4823          * @event loadexception
4824          * Fires if an exception occurs in the Proxy during loading.
4825          * Called with the signature of the Proxy's "loadexception" event.
4826          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
4827          * 
4828          * @param {Proxy} 
4829          * @param {Object} return from JsonData.reader() - success, totalRecords, records
4830          * @param {Object} load options 
4831          * @param {Object} jsonData from your request (normally this contains the Exception)
4832          */
4833         loadexception : true
4834     });
4835     
4836     if(this.proxy){
4837         this.proxy = Roo.factory(this.proxy, Roo.data);
4838         this.proxy.xmodule = this.xmodule || false;
4839         this.relayEvents(this.proxy,  ["loadexception"]);
4840     }
4841     this.sortToggle = {};
4842     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
4843
4844     Roo.data.Store.superclass.constructor.call(this);
4845
4846     if(this.inlineData){
4847         this.loadData(this.inlineData);
4848         delete this.inlineData;
4849     }
4850 };
4851 Roo.extend(Roo.data.Store, Roo.util.Observable, {
4852      /**
4853     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
4854     * without a remote query - used by combo/forms at present.
4855     */
4856     
4857     /**
4858     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
4859     */
4860     /**
4861     * @cfg {Array} data Inline data to be loaded when the store is initialized.
4862     */
4863     /**
4864     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
4865     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
4866     */
4867     /**
4868     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
4869     * on any HTTP request
4870     */
4871     /**
4872     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
4873     */
4874     /**
4875     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
4876     */
4877     multiSort: false,
4878     /**
4879     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
4880     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
4881     */
4882     remoteSort : false,
4883
4884     /**
4885     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
4886      * loaded or when a record is removed. (defaults to false).
4887     */
4888     pruneModifiedRecords : false,
4889
4890     // private
4891     lastOptions : null,
4892
4893     /**
4894      * Add Records to the Store and fires the add event.
4895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4896      */
4897     add : function(records){
4898         records = [].concat(records);
4899         for(var i = 0, len = records.length; i < len; i++){
4900             records[i].join(this);
4901         }
4902         var index = this.data.length;
4903         this.data.addAll(records);
4904         this.fireEvent("add", this, records, index);
4905     },
4906
4907     /**
4908      * Remove a Record from the Store and fires the remove event.
4909      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
4910      */
4911     remove : function(record){
4912         var index = this.data.indexOf(record);
4913         this.data.removeAt(index);
4914         if(this.pruneModifiedRecords){
4915             this.modified.remove(record);
4916         }
4917         this.fireEvent("remove", this, record, index);
4918     },
4919
4920     /**
4921      * Remove all Records from the Store and fires the clear event.
4922      */
4923     removeAll : function(){
4924         this.data.clear();
4925         if(this.pruneModifiedRecords){
4926             this.modified = [];
4927         }
4928         this.fireEvent("clear", this);
4929     },
4930
4931     /**
4932      * Inserts Records to the Store at the given index and fires the add event.
4933      * @param {Number} index The start index at which to insert the passed Records.
4934      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
4935      */
4936     insert : function(index, records){
4937         records = [].concat(records);
4938         for(var i = 0, len = records.length; i < len; i++){
4939             this.data.insert(index, records[i]);
4940             records[i].join(this);
4941         }
4942         this.fireEvent("add", this, records, index);
4943     },
4944
4945     /**
4946      * Get the index within the cache of the passed Record.
4947      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
4948      * @return {Number} The index of the passed Record. Returns -1 if not found.
4949      */
4950     indexOf : function(record){
4951         return this.data.indexOf(record);
4952     },
4953
4954     /**
4955      * Get the index within the cache of the Record with the passed id.
4956      * @param {String} id The id of the Record to find.
4957      * @return {Number} The index of the Record. Returns -1 if not found.
4958      */
4959     indexOfId : function(id){
4960         return this.data.indexOfKey(id);
4961     },
4962
4963     /**
4964      * Get the Record with the specified id.
4965      * @param {String} id The id of the Record to find.
4966      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
4967      */
4968     getById : function(id){
4969         return this.data.key(id);
4970     },
4971
4972     /**
4973      * Get the Record at the specified index.
4974      * @param {Number} index The index of the Record to find.
4975      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
4976      */
4977     getAt : function(index){
4978         return this.data.itemAt(index);
4979     },
4980
4981     /**
4982      * Returns a range of Records between specified indices.
4983      * @param {Number} startIndex (optional) The starting index (defaults to 0)
4984      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
4985      * @return {Roo.data.Record[]} An array of Records
4986      */
4987     getRange : function(start, end){
4988         return this.data.getRange(start, end);
4989     },
4990
4991     // private
4992     storeOptions : function(o){
4993         o = Roo.apply({}, o);
4994         delete o.callback;
4995         delete o.scope;
4996         this.lastOptions = o;
4997     },
4998
4999     /**
5000      * Loads the Record cache from the configured Proxy using the configured Reader.
5001      * <p>
5002      * If using remote paging, then the first load call must specify the <em>start</em>
5003      * and <em>limit</em> properties in the options.params property to establish the initial
5004      * position within the dataset, and the number of Records to cache on each read from the Proxy.
5005      * <p>
5006      * <strong>It is important to note that for remote data sources, loading is asynchronous,
5007      * and this call will return before the new data has been loaded. Perform any post-processing
5008      * in a callback function, or in a "load" event handler.</strong>
5009      * <p>
5010      * @param {Object} options An object containing properties which control loading options:<ul>
5011      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
5012      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
5013      * passed the following arguments:<ul>
5014      * <li>r : Roo.data.Record[]</li>
5015      * <li>options: Options object from the load call</li>
5016      * <li>success: Boolean success indicator</li></ul></li>
5017      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
5018      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
5019      * </ul>
5020      */
5021     load : function(options){
5022         options = options || {};
5023         if(this.fireEvent("beforeload", this, options) !== false){
5024             this.storeOptions(options);
5025             var p = Roo.apply(options.params || {}, this.baseParams);
5026             // if meta was not loaded from remote source.. try requesting it.
5027             if (!this.reader.metaFromRemote) {
5028                 p._requestMeta = 1;
5029             }
5030             if(this.sortInfo && this.remoteSort){
5031                 var pn = this.paramNames;
5032                 p[pn["sort"]] = this.sortInfo.field;
5033                 p[pn["dir"]] = this.sortInfo.direction;
5034             }
5035             if (this.multiSort) {
5036                 var pn = this.paramNames;
5037                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
5038             }
5039             
5040             this.proxy.load(p, this.reader, this.loadRecords, this, options);
5041         }
5042     },
5043
5044     /**
5045      * Reloads the Record cache from the configured Proxy using the configured Reader and
5046      * the options from the last load operation performed.
5047      * @param {Object} options (optional) An object containing properties which may override the options
5048      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
5049      * the most recently used options are reused).
5050      */
5051     reload : function(options){
5052         this.load(Roo.applyIf(options||{}, this.lastOptions));
5053     },
5054
5055     // private
5056     // Called as a callback by the Reader during a load operation.
5057     loadRecords : function(o, options, success){
5058         if(!o || success === false){
5059             if(success !== false){
5060                 this.fireEvent("load", this, [], options);
5061             }
5062             if(options.callback){
5063                 options.callback.call(options.scope || this, [], options, false);
5064             }
5065             return;
5066         }
5067         // if data returned failure - throw an exception.
5068         if (o.success === false) {
5069             // show a message if no listener is registered.
5070             if (!this.hasListener('loadexception') && typeof(this.reader.jsonData.errorMsg) != 'undefined') {
5071                     Roo.MessageBox.alert("Error loading",this.reader.jsonData.errorMsg);
5072             }
5073             // loadmask wil be hooked into this..
5074             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
5075             return;
5076         }
5077         var r = o.records, t = o.totalRecords || r.length;
5078         if(!options || options.add !== true){
5079             if(this.pruneModifiedRecords){
5080                 this.modified = [];
5081             }
5082             for(var i = 0, len = r.length; i < len; i++){
5083                 r[i].join(this);
5084             }
5085             if(this.snapshot){
5086                 this.data = this.snapshot;
5087                 delete this.snapshot;
5088             }
5089             this.data.clear();
5090             this.data.addAll(r);
5091             this.totalLength = t;
5092             this.applySort();
5093             this.fireEvent("datachanged", this);
5094         }else{
5095             this.totalLength = Math.max(t, this.data.length+r.length);
5096             this.add(r);
5097         }
5098         this.fireEvent("load", this, r, options);
5099         if(options.callback){
5100             options.callback.call(options.scope || this, r, options, true);
5101         }
5102     },
5103
5104
5105     /**
5106      * Loads data from a passed data block. A Reader which understands the format of the data
5107      * must have been configured in the constructor.
5108      * @param {Object} data The data block from which to read the Records.  The format of the data expected
5109      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
5110      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
5111      */
5112     loadData : function(o, append){
5113         var r = this.reader.readRecords(o);
5114         this.loadRecords(r, {add: append}, true);
5115     },
5116
5117     /**
5118      * Gets the number of cached records.
5119      * <p>
5120      * <em>If using paging, this may not be the total size of the dataset. If the data object
5121      * used by the Reader contains the dataset size, then the getTotalCount() function returns
5122      * the data set size</em>
5123      */
5124     getCount : function(){
5125         return this.data.length || 0;
5126     },
5127
5128     /**
5129      * Gets the total number of records in the dataset as returned by the server.
5130      * <p>
5131      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
5132      * the dataset size</em>
5133      */
5134     getTotalCount : function(){
5135         return this.totalLength || 0;
5136     },
5137
5138     /**
5139      * Returns the sort state of the Store as an object with two properties:
5140      * <pre><code>
5141  field {String} The name of the field by which the Records are sorted
5142  direction {String} The sort order, "ASC" or "DESC"
5143      * </code></pre>
5144      */
5145     getSortState : function(){
5146         return this.sortInfo;
5147     },
5148
5149     // private
5150     applySort : function(){
5151         if(this.sortInfo && !this.remoteSort){
5152             var s = this.sortInfo, f = s.field;
5153             var st = this.fields.get(f).sortType;
5154             var fn = function(r1, r2){
5155                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
5156                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
5157             };
5158             this.data.sort(s.direction, fn);
5159             if(this.snapshot && this.snapshot != this.data){
5160                 this.snapshot.sort(s.direction, fn);
5161             }
5162         }
5163     },
5164
5165     /**
5166      * Sets the default sort column and order to be used by the next load operation.
5167      * @param {String} fieldName The name of the field to sort by.
5168      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5169      */
5170     setDefaultSort : function(field, dir){
5171         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
5172     },
5173
5174     /**
5175      * Sort the Records.
5176      * If remote sorting is used, the sort is performed on the server, and the cache is
5177      * reloaded. If local sorting is used, the cache is sorted internally.
5178      * @param {String} fieldName The name of the field to sort by.
5179      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
5180      */
5181     sort : function(fieldName, dir){
5182         var f = this.fields.get(fieldName);
5183         if(!dir){
5184             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
5185             
5186             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
5187                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
5188             }else{
5189                 dir = f.sortDir;
5190             }
5191         }
5192         this.sortToggle[f.name] = dir;
5193         this.sortInfo = {field: f.name, direction: dir};
5194         if(!this.remoteSort){
5195             this.applySort();
5196             this.fireEvent("datachanged", this);
5197         }else{
5198             this.load(this.lastOptions);
5199         }
5200     },
5201
5202     /**
5203      * Calls the specified function for each of the Records in the cache.
5204      * @param {Function} fn The function to call. The Record is passed as the first parameter.
5205      * Returning <em>false</em> aborts and exits the iteration.
5206      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
5207      */
5208     each : function(fn, scope){
5209         this.data.each(fn, scope);
5210     },
5211
5212     /**
5213      * Gets all records modified since the last commit.  Modified records are persisted across load operations
5214      * (e.g., during paging).
5215      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
5216      */
5217     getModifiedRecords : function(){
5218         return this.modified;
5219     },
5220
5221     // private
5222     createFilterFn : function(property, value, anyMatch){
5223         if(!value.exec){ // not a regex
5224             value = String(value);
5225             if(value.length == 0){
5226                 return false;
5227             }
5228             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
5229         }
5230         return function(r){
5231             return value.test(r.data[property]);
5232         };
5233     },
5234
5235     /**
5236      * Sums the value of <i>property</i> for each record between start and end and returns the result.
5237      * @param {String} property A field on your records
5238      * @param {Number} start The record index to start at (defaults to 0)
5239      * @param {Number} end The last record index to include (defaults to length - 1)
5240      * @return {Number} The sum
5241      */
5242     sum : function(property, start, end){
5243         var rs = this.data.items, v = 0;
5244         start = start || 0;
5245         end = (end || end === 0) ? end : rs.length-1;
5246
5247         for(var i = start; i <= end; i++){
5248             v += (rs[i].data[property] || 0);
5249         }
5250         return v;
5251     },
5252
5253     /**
5254      * Filter the records by a specified property.
5255      * @param {String} field A field on your records
5256      * @param {String/RegExp} value Either a string that the field
5257      * should start with or a RegExp to test against the field
5258      * @param {Boolean} anyMatch True to match any part not just the beginning
5259      */
5260     filter : function(property, value, anyMatch){
5261         var fn = this.createFilterFn(property, value, anyMatch);
5262         return fn ? this.filterBy(fn) : this.clearFilter();
5263     },
5264
5265     /**
5266      * Filter by a function. The specified function will be called with each
5267      * record in this data source. If the function returns true the record is included,
5268      * otherwise it is filtered.
5269      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5270      * @param {Object} scope (optional) The scope of the function (defaults to this)
5271      */
5272     filterBy : function(fn, scope){
5273         this.snapshot = this.snapshot || this.data;
5274         this.data = this.queryBy(fn, scope||this);
5275         this.fireEvent("datachanged", this);
5276     },
5277
5278     /**
5279      * Query the records by a specified property.
5280      * @param {String} field A field on your records
5281      * @param {String/RegExp} value Either a string that the field
5282      * should start with or a RegExp to test against the field
5283      * @param {Boolean} anyMatch True to match any part not just the beginning
5284      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5285      */
5286     query : function(property, value, anyMatch){
5287         var fn = this.createFilterFn(property, value, anyMatch);
5288         return fn ? this.queryBy(fn) : this.data.clone();
5289     },
5290
5291     /**
5292      * Query by a function. The specified function will be called with each
5293      * record in this data source. If the function returns true the record is included
5294      * in the results.
5295      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
5296      * @param {Object} scope (optional) The scope of the function (defaults to this)
5297       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
5298      **/
5299     queryBy : function(fn, scope){
5300         var data = this.snapshot || this.data;
5301         return data.filterBy(fn, scope||this);
5302     },
5303
5304     /**
5305      * Collects unique values for a particular dataIndex from this store.
5306      * @param {String} dataIndex The property to collect
5307      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
5308      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
5309      * @return {Array} An array of the unique values
5310      **/
5311     collect : function(dataIndex, allowNull, bypassFilter){
5312         var d = (bypassFilter === true && this.snapshot) ?
5313                 this.snapshot.items : this.data.items;
5314         var v, sv, r = [], l = {};
5315         for(var i = 0, len = d.length; i < len; i++){
5316             v = d[i].data[dataIndex];
5317             sv = String(v);
5318             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
5319                 l[sv] = true;
5320                 r[r.length] = v;
5321             }
5322         }
5323         return r;
5324     },
5325
5326     /**
5327      * Revert to a view of the Record cache with no filtering applied.
5328      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
5329      */
5330     clearFilter : function(suppressEvent){
5331         if(this.snapshot && this.snapshot != this.data){
5332             this.data = this.snapshot;
5333             delete this.snapshot;
5334             if(suppressEvent !== true){
5335                 this.fireEvent("datachanged", this);
5336             }
5337         }
5338     },
5339
5340     // private
5341     afterEdit : function(record){
5342         if(this.modified.indexOf(record) == -1){
5343             this.modified.push(record);
5344         }
5345         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
5346     },
5347     
5348     // private
5349     afterReject : function(record){
5350         this.modified.remove(record);
5351         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
5352     },
5353
5354     // private
5355     afterCommit : function(record){
5356         this.modified.remove(record);
5357         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
5358     },
5359
5360     /**
5361      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
5362      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
5363      */
5364     commitChanges : function(){
5365         var m = this.modified.slice(0);
5366         this.modified = [];
5367         for(var i = 0, len = m.length; i < len; i++){
5368             m[i].commit();
5369         }
5370     },
5371
5372     /**
5373      * Cancel outstanding changes on all changed records.
5374      */
5375     rejectChanges : function(){
5376         var m = this.modified.slice(0);
5377         this.modified = [];
5378         for(var i = 0, len = m.length; i < len; i++){
5379             m[i].reject();
5380         }
5381     },
5382
5383     onMetaChange : function(meta, rtype, o){
5384         this.recordType = rtype;
5385         this.fields = rtype.prototype.fields;
5386         delete this.snapshot;
5387         this.sortInfo = meta.sortInfo || this.sortInfo;
5388         this.modified = [];
5389         this.fireEvent('metachange', this, this.reader.meta);
5390     }
5391 });/*
5392  * Based on:
5393  * Ext JS Library 1.1.1
5394  * Copyright(c) 2006-2007, Ext JS, LLC.
5395  *
5396  * Originally Released Under LGPL - original licence link has changed is not relivant.
5397  *
5398  * Fork - LGPL
5399  * <script type="text/javascript">
5400  */
5401
5402 /**
5403  * @class Roo.data.SimpleStore
5404  * @extends Roo.data.Store
5405  * Small helper class to make creating Stores from Array data easier.
5406  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
5407  * @cfg {Array} fields An array of field definition objects, or field name strings.
5408  * @cfg {Array} data The multi-dimensional array of data
5409  * @constructor
5410  * @param {Object} config
5411  */
5412 Roo.data.SimpleStore = function(config){
5413     Roo.data.SimpleStore.superclass.constructor.call(this, {
5414         isLocal : true,
5415         reader: new Roo.data.ArrayReader({
5416                 id: config.id
5417             },
5418             Roo.data.Record.create(config.fields)
5419         ),
5420         proxy : new Roo.data.MemoryProxy(config.data)
5421     });
5422     this.load();
5423 };
5424 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
5425  * Based on:
5426  * Ext JS Library 1.1.1
5427  * Copyright(c) 2006-2007, Ext JS, LLC.
5428  *
5429  * Originally Released Under LGPL - original licence link has changed is not relivant.
5430  *
5431  * Fork - LGPL
5432  * <script type="text/javascript">
5433  */
5434
5435 /**
5436 /**
5437  * @extends Roo.data.Store
5438  * @class Roo.data.JsonStore
5439  * Small helper class to make creating Stores for JSON data easier. <br/>
5440 <pre><code>
5441 var store = new Roo.data.JsonStore({
5442     url: 'get-images.php',
5443     root: 'images',
5444     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
5445 });
5446 </code></pre>
5447  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
5448  * JsonReader and HttpProxy (unless inline data is provided).</b>
5449  * @cfg {Array} fields An array of field definition objects, or field name strings.
5450  * @constructor
5451  * @param {Object} config
5452  */
5453 Roo.data.JsonStore = function(c){
5454     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
5455         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
5456         reader: new Roo.data.JsonReader(c, c.fields)
5457     }));
5458 };
5459 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
5460  * Based on:
5461  * Ext JS Library 1.1.1
5462  * Copyright(c) 2006-2007, Ext JS, LLC.
5463  *
5464  * Originally Released Under LGPL - original licence link has changed is not relivant.
5465  *
5466  * Fork - LGPL
5467  * <script type="text/javascript">
5468  */
5469
5470  
5471 Roo.data.Field = function(config){
5472     if(typeof config == "string"){
5473         config = {name: config};
5474     }
5475     Roo.apply(this, config);
5476     
5477     if(!this.type){
5478         this.type = "auto";
5479     }
5480     
5481     var st = Roo.data.SortTypes;
5482     // named sortTypes are supported, here we look them up
5483     if(typeof this.sortType == "string"){
5484         this.sortType = st[this.sortType];
5485     }
5486     
5487     // set default sortType for strings and dates
5488     if(!this.sortType){
5489         switch(this.type){
5490             case "string":
5491                 this.sortType = st.asUCString;
5492                 break;
5493             case "date":
5494                 this.sortType = st.asDate;
5495                 break;
5496             default:
5497                 this.sortType = st.none;
5498         }
5499     }
5500
5501     // define once
5502     var stripRe = /[\$,%]/g;
5503
5504     // prebuilt conversion function for this field, instead of
5505     // switching every time we're reading a value
5506     if(!this.convert){
5507         var cv, dateFormat = this.dateFormat;
5508         switch(this.type){
5509             case "":
5510             case "auto":
5511             case undefined:
5512                 cv = function(v){ return v; };
5513                 break;
5514             case "string":
5515                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
5516                 break;
5517             case "int":
5518                 cv = function(v){
5519                     return v !== undefined && v !== null && v !== '' ?
5520                            parseInt(String(v).replace(stripRe, ""), 10) : '';
5521                     };
5522                 break;
5523             case "float":
5524                 cv = function(v){
5525                     return v !== undefined && v !== null && v !== '' ?
5526                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
5527                     };
5528                 break;
5529             case "bool":
5530             case "boolean":
5531                 cv = function(v){ return v === true || v === "true" || v == 1; };
5532                 break;
5533             case "date":
5534                 cv = function(v){
5535                     if(!v){
5536                         return '';
5537                     }
5538                     if(v instanceof Date){
5539                         return v;
5540                     }
5541                     if(dateFormat){
5542                         if(dateFormat == "timestamp"){
5543                             return new Date(v*1000);
5544                         }
5545                         return Date.parseDate(v, dateFormat);
5546                     }
5547                     var parsed = Date.parse(v);
5548                     return parsed ? new Date(parsed) : null;
5549                 };
5550              break;
5551             
5552         }
5553         this.convert = cv;
5554     }
5555 };
5556
5557 Roo.data.Field.prototype = {
5558     dateFormat: null,
5559     defaultValue: "",
5560     mapping: null,
5561     sortType : null,
5562     sortDir : "ASC"
5563 };/*
5564  * Based on:
5565  * Ext JS Library 1.1.1
5566  * Copyright(c) 2006-2007, Ext JS, LLC.
5567  *
5568  * Originally Released Under LGPL - original licence link has changed is not relivant.
5569  *
5570  * Fork - LGPL
5571  * <script type="text/javascript">
5572  */
5573  
5574 // Base class for reading structured data from a data source.  This class is intended to be
5575 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
5576
5577 /**
5578  * @class Roo.data.DataReader
5579  * Base class for reading structured data from a data source.  This class is intended to be
5580  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
5581  */
5582
5583 Roo.data.DataReader = function(meta, recordType){
5584     
5585     this.meta = meta;
5586     
5587     this.recordType = recordType instanceof Array ? 
5588         Roo.data.Record.create(recordType) : recordType;
5589 };
5590
5591 Roo.data.DataReader.prototype = {
5592      /**
5593      * Create an empty record
5594      * @param {Object} data (optional) - overlay some values
5595      * @return {Roo.data.Record} record created.
5596      */
5597     newRow :  function(d) {
5598         var da =  {};
5599         this.recordType.prototype.fields.each(function(c) {
5600             switch( c.type) {
5601                 case 'int' : da[c.name] = 0; break;
5602                 case 'date' : da[c.name] = new Date(); break;
5603                 case 'float' : da[c.name] = 0.0; break;
5604                 case 'boolean' : da[c.name] = false; break;
5605                 default : da[c.name] = ""; break;
5606             }
5607             
5608         });
5609         return new this.recordType(Roo.apply(da, d));
5610     }
5611     
5612 };/*
5613  * Based on:
5614  * Ext JS Library 1.1.1
5615  * Copyright(c) 2006-2007, Ext JS, LLC.
5616  *
5617  * Originally Released Under LGPL - original licence link has changed is not relivant.
5618  *
5619  * Fork - LGPL
5620  * <script type="text/javascript">
5621  */
5622
5623 /**
5624  * @class Roo.data.DataProxy
5625  * @extends Roo.data.Observable
5626  * This class is an abstract base class for implementations which provide retrieval of
5627  * unformatted data objects.<br>
5628  * <p>
5629  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
5630  * (of the appropriate type which knows how to parse the data object) to provide a block of
5631  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
5632  * <p>
5633  * Custom implementations must implement the load method as described in
5634  * {@link Roo.data.HttpProxy#load}.
5635  */
5636 Roo.data.DataProxy = function(){
5637     this.addEvents({
5638         /**
5639          * @event beforeload
5640          * Fires before a network request is made to retrieve a data object.
5641          * @param {Object} This DataProxy object.
5642          * @param {Object} params The params parameter to the load function.
5643          */
5644         beforeload : true,
5645         /**
5646          * @event load
5647          * Fires before the load method's callback is called.
5648          * @param {Object} This DataProxy object.
5649          * @param {Object} o The data object.
5650          * @param {Object} arg The callback argument object passed to the load function.
5651          */
5652         load : true,
5653         /**
5654          * @event loadexception
5655          * Fires if an Exception occurs during data retrieval.
5656          * @param {Object} This DataProxy object.
5657          * @param {Object} o The data object.
5658          * @param {Object} arg The callback argument object passed to the load function.
5659          * @param {Object} e The Exception.
5660          */
5661         loadexception : true
5662     });
5663     Roo.data.DataProxy.superclass.constructor.call(this);
5664 };
5665
5666 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
5667
5668     /**
5669      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
5670      */
5671 /*
5672  * Based on:
5673  * Ext JS Library 1.1.1
5674  * Copyright(c) 2006-2007, Ext JS, LLC.
5675  *
5676  * Originally Released Under LGPL - original licence link has changed is not relivant.
5677  *
5678  * Fork - LGPL
5679  * <script type="text/javascript">
5680  */
5681 /**
5682  * @class Roo.data.MemoryProxy
5683  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
5684  * to the Reader when its load method is called.
5685  * @constructor
5686  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
5687  */
5688 Roo.data.MemoryProxy = function(data){
5689     if (data.data) {
5690         data = data.data;
5691     }
5692     Roo.data.MemoryProxy.superclass.constructor.call(this);
5693     this.data = data;
5694 };
5695
5696 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
5697     /**
5698      * Load data from the requested source (in this case an in-memory
5699      * data object passed to the constructor), read the data object into
5700      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5701      * process that block using the passed callback.
5702      * @param {Object} params This parameter is not used by the MemoryProxy class.
5703      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5704      * object into a block of Roo.data.Records.
5705      * @param {Function} callback The function into which to pass the block of Roo.data.records.
5706      * The function must be passed <ul>
5707      * <li>The Record block object</li>
5708      * <li>The "arg" argument from the load function</li>
5709      * <li>A boolean success indicator</li>
5710      * </ul>
5711      * @param {Object} scope The scope in which to call the callback
5712      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5713      */
5714     load : function(params, reader, callback, scope, arg){
5715         params = params || {};
5716         var result;
5717         try {
5718             result = reader.readRecords(this.data);
5719         }catch(e){
5720             this.fireEvent("loadexception", this, arg, null, e);
5721             callback.call(scope, null, arg, false);
5722             return;
5723         }
5724         callback.call(scope, result, arg, true);
5725     },
5726     
5727     // private
5728     update : function(params, records){
5729         
5730     }
5731 });/*
5732  * Based on:
5733  * Ext JS Library 1.1.1
5734  * Copyright(c) 2006-2007, Ext JS, LLC.
5735  *
5736  * Originally Released Under LGPL - original licence link has changed is not relivant.
5737  *
5738  * Fork - LGPL
5739  * <script type="text/javascript">
5740  */
5741 /**
5742  * @class Roo.data.HttpProxy
5743  * @extends Roo.data.DataProxy
5744  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
5745  * configured to reference a certain URL.<br><br>
5746  * <p>
5747  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
5748  * from which the running page was served.<br><br>
5749  * <p>
5750  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
5751  * <p>
5752  * Be aware that to enable the browser to parse an XML document, the server must set
5753  * the Content-Type header in the HTTP response to "text/xml".
5754  * @constructor
5755  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
5756  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
5757  * will be used to make the request.
5758  */
5759 Roo.data.HttpProxy = function(conn){
5760     Roo.data.HttpProxy.superclass.constructor.call(this);
5761     // is conn a conn config or a real conn?
5762     this.conn = conn;
5763     this.useAjax = !conn || !conn.events;
5764   
5765 };
5766
5767 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
5768     // thse are take from connection...
5769     
5770     /**
5771      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
5772      */
5773     /**
5774      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
5775      * extra parameters to each request made by this object. (defaults to undefined)
5776      */
5777     /**
5778      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
5779      *  to each request made by this object. (defaults to undefined)
5780      */
5781     /**
5782      * @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)
5783      */
5784     /**
5785      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
5786      */
5787      /**
5788      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
5789      * @type Boolean
5790      */
5791   
5792
5793     /**
5794      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
5795      * @type Boolean
5796      */
5797     /**
5798      * Return the {@link Roo.data.Connection} object being used by this Proxy.
5799      * @return {Connection} The Connection object. This object may be used to subscribe to events on
5800      * a finer-grained basis than the DataProxy events.
5801      */
5802     getConnection : function(){
5803         return this.useAjax ? Roo.Ajax : this.conn;
5804     },
5805
5806     /**
5807      * Load data from the configured {@link Roo.data.Connection}, read the data object into
5808      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
5809      * process that block using the passed callback.
5810      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5811      * for the request to the remote server.
5812      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5813      * object into a block of Roo.data.Records.
5814      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5815      * The function must be passed <ul>
5816      * <li>The Record block object</li>
5817      * <li>The "arg" argument from the load function</li>
5818      * <li>A boolean success indicator</li>
5819      * </ul>
5820      * @param {Object} scope The scope in which to call the callback
5821      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5822      */
5823     load : function(params, reader, callback, scope, arg){
5824         if(this.fireEvent("beforeload", this, params) !== false){
5825             var  o = {
5826                 params : params || {},
5827                 request: {
5828                     callback : callback,
5829                     scope : scope,
5830                     arg : arg
5831                 },
5832                 reader: reader,
5833                 callback : this.loadResponse,
5834                 scope: this
5835             };
5836             if(this.useAjax){
5837                 Roo.applyIf(o, this.conn);
5838                 if(this.activeRequest){
5839                     Roo.Ajax.abort(this.activeRequest);
5840                 }
5841                 this.activeRequest = Roo.Ajax.request(o);
5842             }else{
5843                 this.conn.request(o);
5844             }
5845         }else{
5846             callback.call(scope||this, null, arg, false);
5847         }
5848     },
5849
5850     // private
5851     loadResponse : function(o, success, response){
5852         delete this.activeRequest;
5853         if(!success){
5854             this.fireEvent("loadexception", this, o, response);
5855             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5856             return;
5857         }
5858         var result;
5859         try {
5860             result = o.reader.read(response);
5861         }catch(e){
5862             this.fireEvent("loadexception", this, o, response, e);
5863             o.request.callback.call(o.request.scope, null, o.request.arg, false);
5864             return;
5865         }
5866         
5867         this.fireEvent("load", this, o, o.request.arg);
5868         o.request.callback.call(o.request.scope, result, o.request.arg, true);
5869     },
5870
5871     // private
5872     update : function(dataSet){
5873
5874     },
5875
5876     // private
5877     updateResponse : function(dataSet){
5878
5879     }
5880 });/*
5881  * Based on:
5882  * Ext JS Library 1.1.1
5883  * Copyright(c) 2006-2007, Ext JS, LLC.
5884  *
5885  * Originally Released Under LGPL - original licence link has changed is not relivant.
5886  *
5887  * Fork - LGPL
5888  * <script type="text/javascript">
5889  */
5890
5891 /**
5892  * @class Roo.data.ScriptTagProxy
5893  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
5894  * other than the originating domain of the running page.<br><br>
5895  * <p>
5896  * <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
5897  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
5898  * <p>
5899  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
5900  * source code that is used as the source inside a &lt;script> tag.<br><br>
5901  * <p>
5902  * In order for the browser to process the returned data, the server must wrap the data object
5903  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
5904  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
5905  * depending on whether the callback name was passed:
5906  * <p>
5907  * <pre><code>
5908 boolean scriptTag = false;
5909 String cb = request.getParameter("callback");
5910 if (cb != null) {
5911     scriptTag = true;
5912     response.setContentType("text/javascript");
5913 } else {
5914     response.setContentType("application/x-json");
5915 }
5916 Writer out = response.getWriter();
5917 if (scriptTag) {
5918     out.write(cb + "(");
5919 }
5920 out.print(dataBlock.toJsonString());
5921 if (scriptTag) {
5922     out.write(");");
5923 }
5924 </pre></code>
5925  *
5926  * @constructor
5927  * @param {Object} config A configuration object.
5928  */
5929 Roo.data.ScriptTagProxy = function(config){
5930     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
5931     Roo.apply(this, config);
5932     this.head = document.getElementsByTagName("head")[0];
5933 };
5934
5935 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
5936
5937 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
5938     /**
5939      * @cfg {String} url The URL from which to request the data object.
5940      */
5941     /**
5942      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
5943      */
5944     timeout : 30000,
5945     /**
5946      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
5947      * the server the name of the callback function set up by the load call to process the returned data object.
5948      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
5949      * javascript output which calls this named function passing the data object as its only parameter.
5950      */
5951     callbackParam : "callback",
5952     /**
5953      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
5954      * name to the request.
5955      */
5956     nocache : true,
5957
5958     /**
5959      * Load data from the configured URL, read the data object into
5960      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
5961      * process that block using the passed callback.
5962      * @param {Object} params An object containing properties which are to be used as HTTP parameters
5963      * for the request to the remote server.
5964      * @param {Roo.data.DataReader} reader The Reader object which converts the data
5965      * object into a block of Roo.data.Records.
5966      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
5967      * The function must be passed <ul>
5968      * <li>The Record block object</li>
5969      * <li>The "arg" argument from the load function</li>
5970      * <li>A boolean success indicator</li>
5971      * </ul>
5972      * @param {Object} scope The scope in which to call the callback
5973      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
5974      */
5975     load : function(params, reader, callback, scope, arg){
5976         if(this.fireEvent("beforeload", this, params) !== false){
5977
5978             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
5979
5980             var url = this.url;
5981             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
5982             if(this.nocache){
5983                 url += "&_dc=" + (new Date().getTime());
5984             }
5985             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
5986             var trans = {
5987                 id : transId,
5988                 cb : "stcCallback"+transId,
5989                 scriptId : "stcScript"+transId,
5990                 params : params,
5991                 arg : arg,
5992                 url : url,
5993                 callback : callback,
5994                 scope : scope,
5995                 reader : reader
5996             };
5997             var conn = this;
5998
5999             window[trans.cb] = function(o){
6000                 conn.handleResponse(o, trans);
6001             };
6002
6003             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
6004
6005             if(this.autoAbort !== false){
6006                 this.abort();
6007             }
6008
6009             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
6010
6011             var script = document.createElement("script");
6012             script.setAttribute("src", url);
6013             script.setAttribute("type", "text/javascript");
6014             script.setAttribute("id", trans.scriptId);
6015             this.head.appendChild(script);
6016
6017             this.trans = trans;
6018         }else{
6019             callback.call(scope||this, null, arg, false);
6020         }
6021     },
6022
6023     // private
6024     isLoading : function(){
6025         return this.trans ? true : false;
6026     },
6027
6028     /**
6029      * Abort the current server request.
6030      */
6031     abort : function(){
6032         if(this.isLoading()){
6033             this.destroyTrans(this.trans);
6034         }
6035     },
6036
6037     // private
6038     destroyTrans : function(trans, isLoaded){
6039         this.head.removeChild(document.getElementById(trans.scriptId));
6040         clearTimeout(trans.timeoutId);
6041         if(isLoaded){
6042             window[trans.cb] = undefined;
6043             try{
6044                 delete window[trans.cb];
6045             }catch(e){}
6046         }else{
6047             // if hasn't been loaded, wait for load to remove it to prevent script error
6048             window[trans.cb] = function(){
6049                 window[trans.cb] = undefined;
6050                 try{
6051                     delete window[trans.cb];
6052                 }catch(e){}
6053             };
6054         }
6055     },
6056
6057     // private
6058     handleResponse : function(o, trans){
6059         this.trans = false;
6060         this.destroyTrans(trans, true);
6061         var result;
6062         try {
6063             result = trans.reader.readRecords(o);
6064         }catch(e){
6065             this.fireEvent("loadexception", this, o, trans.arg, e);
6066             trans.callback.call(trans.scope||window, null, trans.arg, false);
6067             return;
6068         }
6069         this.fireEvent("load", this, o, trans.arg);
6070         trans.callback.call(trans.scope||window, result, trans.arg, true);
6071     },
6072
6073     // private
6074     handleFailure : function(trans){
6075         this.trans = false;
6076         this.destroyTrans(trans, false);
6077         this.fireEvent("loadexception", this, null, trans.arg);
6078         trans.callback.call(trans.scope||window, null, trans.arg, false);
6079     }
6080 });/*
6081  * Based on:
6082  * Ext JS Library 1.1.1
6083  * Copyright(c) 2006-2007, Ext JS, LLC.
6084  *
6085  * Originally Released Under LGPL - original licence link has changed is not relivant.
6086  *
6087  * Fork - LGPL
6088  * <script type="text/javascript">
6089  */
6090
6091 /**
6092  * @class Roo.data.JsonReader
6093  * @extends Roo.data.DataReader
6094  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
6095  * based on mappings in a provided Roo.data.Record constructor.
6096  * 
6097  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
6098  * in the reply previously. 
6099  * 
6100  * <p>
6101  * Example code:
6102  * <pre><code>
6103 var RecordDef = Roo.data.Record.create([
6104     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6105     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6106 ]);
6107 var myReader = new Roo.data.JsonReader({
6108     totalProperty: "results",    // The property which contains the total dataset size (optional)
6109     root: "rows",                // The property which contains an Array of row objects
6110     id: "id"                     // The property within each row object that provides an ID for the record (optional)
6111 }, RecordDef);
6112 </code></pre>
6113  * <p>
6114  * This would consume a JSON file like this:
6115  * <pre><code>
6116 { 'results': 2, 'rows': [
6117     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
6118     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
6119 }
6120 </code></pre>
6121  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
6122  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6123  * paged from the remote server.
6124  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
6125  * @cfg {String} root name of the property which contains the Array of row objects.
6126  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
6127  * @constructor
6128  * Create a new JsonReader
6129  * @param {Object} meta Metadata configuration options
6130  * @param {Object} recordType Either an Array of field definition objects,
6131  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
6132  */
6133 Roo.data.JsonReader = function(meta, recordType){
6134     
6135     meta = meta || {};
6136     // set some defaults:
6137     Roo.applyIf(meta, {
6138         totalProperty: 'total',
6139         successProperty : 'success',
6140         root : 'data',
6141         id : 'id'
6142     });
6143     
6144     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6145 };
6146 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
6147     
6148     /**
6149      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
6150      * Used by Store query builder to append _requestMeta to params.
6151      * 
6152      */
6153     metaFromRemote : false,
6154     /**
6155      * This method is only used by a DataProxy which has retrieved data from a remote server.
6156      * @param {Object} response The XHR object which contains the JSON data in its responseText.
6157      * @return {Object} data A data block which is used by an Roo.data.Store object as
6158      * a cache of Roo.data.Records.
6159      */
6160     read : function(response){
6161         var json = response.responseText;
6162        
6163         var o = /* eval:var:o */ eval("("+json+")");
6164         if(!o) {
6165             throw {message: "JsonReader.read: Json object not found"};
6166         }
6167         
6168         if(o.metaData){
6169             
6170             delete this.ef;
6171             this.metaFromRemote = true;
6172             this.meta = o.metaData;
6173             this.recordType = Roo.data.Record.create(o.metaData.fields);
6174             this.onMetaChange(this.meta, this.recordType, o);
6175         }
6176         return this.readRecords(o);
6177     },
6178
6179     // private function a store will implement
6180     onMetaChange : function(meta, recordType, o){
6181
6182     },
6183
6184     /**
6185          * @ignore
6186          */
6187     simpleAccess: function(obj, subsc) {
6188         return obj[subsc];
6189     },
6190
6191         /**
6192          * @ignore
6193          */
6194     getJsonAccessor: function(){
6195         var re = /[\[\.]/;
6196         return function(expr) {
6197             try {
6198                 return(re.test(expr))
6199                     ? new Function("obj", "return obj." + expr)
6200                     : function(obj){
6201                         return obj[expr];
6202                     };
6203             } catch(e){}
6204             return Roo.emptyFn;
6205         };
6206     }(),
6207
6208     /**
6209      * Create a data block containing Roo.data.Records from an XML document.
6210      * @param {Object} o An object which contains an Array of row objects in the property specified
6211      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
6212      * which contains the total size of the dataset.
6213      * @return {Object} data A data block which is used by an Roo.data.Store object as
6214      * a cache of Roo.data.Records.
6215      */
6216     readRecords : function(o){
6217         /**
6218          * After any data loads, the raw JSON data is available for further custom processing.
6219          * @type Object
6220          */
6221         this.jsonData = o;
6222         var s = this.meta, Record = this.recordType,
6223             f = Record.prototype.fields, fi = f.items, fl = f.length;
6224
6225 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
6226         if (!this.ef) {
6227             if(s.totalProperty) {
6228                     this.getTotal = this.getJsonAccessor(s.totalProperty);
6229                 }
6230                 if(s.successProperty) {
6231                     this.getSuccess = this.getJsonAccessor(s.successProperty);
6232                 }
6233                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
6234                 if (s.id) {
6235                         var g = this.getJsonAccessor(s.id);
6236                         this.getId = function(rec) {
6237                                 var r = g(rec);
6238                                 return (r === undefined || r === "") ? null : r;
6239                         };
6240                 } else {
6241                         this.getId = function(){return null;};
6242                 }
6243             this.ef = [];
6244             for(var jj = 0; jj < fl; jj++){
6245                 f = fi[jj];
6246                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
6247                 this.ef[jj] = this.getJsonAccessor(map);
6248             }
6249         }
6250
6251         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
6252         if(s.totalProperty){
6253             var vt = parseInt(this.getTotal(o), 10);
6254             if(!isNaN(vt)){
6255                 totalRecords = vt;
6256             }
6257         }
6258         if(s.successProperty){
6259             var vs = this.getSuccess(o);
6260             if(vs === false || vs === 'false'){
6261                 success = false;
6262             }
6263         }
6264         var records = [];
6265             for(var i = 0; i < c; i++){
6266                     var n = root[i];
6267                 var values = {};
6268                 var id = this.getId(n);
6269                 for(var j = 0; j < fl; j++){
6270                     f = fi[j];
6271                 var v = this.ef[j](n);
6272                 if (!f.convert) {
6273                     Roo.log('missing convert for ' + f.name);
6274                     Roo.log(f);
6275                     continue;
6276                 }
6277                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
6278                 }
6279                 var record = new Record(values, id);
6280                 record.json = n;
6281                 records[i] = record;
6282             }
6283             return {
6284                 success : success,
6285                 records : records,
6286                 totalRecords : totalRecords
6287             };
6288     }
6289 });/*
6290  * Based on:
6291  * Ext JS Library 1.1.1
6292  * Copyright(c) 2006-2007, Ext JS, LLC.
6293  *
6294  * Originally Released Under LGPL - original licence link has changed is not relivant.
6295  *
6296  * Fork - LGPL
6297  * <script type="text/javascript">
6298  */
6299
6300 /**
6301  * @class Roo.data.XmlReader
6302  * @extends Roo.data.DataReader
6303  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
6304  * based on mappings in a provided Roo.data.Record constructor.<br><br>
6305  * <p>
6306  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
6307  * header in the HTTP response must be set to "text/xml".</em>
6308  * <p>
6309  * Example code:
6310  * <pre><code>
6311 var RecordDef = Roo.data.Record.create([
6312    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
6313    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
6314 ]);
6315 var myReader = new Roo.data.XmlReader({
6316    totalRecords: "results", // The element which contains the total dataset size (optional)
6317    record: "row",           // The repeated element which contains row information
6318    id: "id"                 // The element within the row that provides an ID for the record (optional)
6319 }, RecordDef);
6320 </code></pre>
6321  * <p>
6322  * This would consume an XML file like this:
6323  * <pre><code>
6324 &lt;?xml?>
6325 &lt;dataset>
6326  &lt;results>2&lt;/results>
6327  &lt;row>
6328    &lt;id>1&lt;/id>
6329    &lt;name>Bill&lt;/name>
6330    &lt;occupation>Gardener&lt;/occupation>
6331  &lt;/row>
6332  &lt;row>
6333    &lt;id>2&lt;/id>
6334    &lt;name>Ben&lt;/name>
6335    &lt;occupation>Horticulturalist&lt;/occupation>
6336  &lt;/row>
6337 &lt;/dataset>
6338 </code></pre>
6339  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
6340  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
6341  * paged from the remote server.
6342  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
6343  * @cfg {String} success The DomQuery path to the success attribute used by forms.
6344  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
6345  * a record identifier value.
6346  * @constructor
6347  * Create a new XmlReader
6348  * @param {Object} meta Metadata configuration options
6349  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
6350  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
6351  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
6352  */
6353 Roo.data.XmlReader = function(meta, recordType){
6354     meta = meta || {};
6355     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
6356 };
6357 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
6358     /**
6359      * This method is only used by a DataProxy which has retrieved data from a remote server.
6360          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
6361          * to contain a method called 'responseXML' that returns an XML document object.
6362      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6363      * a cache of Roo.data.Records.
6364      */
6365     read : function(response){
6366         var doc = response.responseXML;
6367         if(!doc) {
6368             throw {message: "XmlReader.read: XML Document not available"};
6369         }
6370         return this.readRecords(doc);
6371     },
6372
6373     /**
6374      * Create a data block containing Roo.data.Records from an XML document.
6375          * @param {Object} doc A parsed XML document.
6376      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
6377      * a cache of Roo.data.Records.
6378      */
6379     readRecords : function(doc){
6380         /**
6381          * After any data loads/reads, the raw XML Document is available for further custom processing.
6382          * @type XMLDocument
6383          */
6384         this.xmlData = doc;
6385         var root = doc.documentElement || doc;
6386         var q = Roo.DomQuery;
6387         var recordType = this.recordType, fields = recordType.prototype.fields;
6388         var sid = this.meta.id;
6389         var totalRecords = 0, success = true;
6390         if(this.meta.totalRecords){
6391             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
6392         }
6393         
6394         if(this.meta.success){
6395             var sv = q.selectValue(this.meta.success, root, true);
6396             success = sv !== false && sv !== 'false';
6397         }
6398         var records = [];
6399         var ns = q.select(this.meta.record, root);
6400         for(var i = 0, len = ns.length; i < len; i++) {
6401                 var n = ns[i];
6402                 var values = {};
6403                 var id = sid ? q.selectValue(sid, n) : undefined;
6404                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6405                     var f = fields.items[j];
6406                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
6407                     v = f.convert(v);
6408                     values[f.name] = v;
6409                 }
6410                 var record = new recordType(values, id);
6411                 record.node = n;
6412                 records[records.length] = record;
6413             }
6414
6415             return {
6416                 success : success,
6417                 records : records,
6418                 totalRecords : totalRecords || records.length
6419             };
6420     }
6421 });/*
6422  * Based on:
6423  * Ext JS Library 1.1.1
6424  * Copyright(c) 2006-2007, Ext JS, LLC.
6425  *
6426  * Originally Released Under LGPL - original licence link has changed is not relivant.
6427  *
6428  * Fork - LGPL
6429  * <script type="text/javascript">
6430  */
6431
6432 /**
6433  * @class Roo.data.ArrayReader
6434  * @extends Roo.data.DataReader
6435  * Data reader class to create an Array of Roo.data.Record objects from an Array.
6436  * Each element of that Array represents a row of data fields. The
6437  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
6438  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
6439  * <p>
6440  * Example code:.
6441  * <pre><code>
6442 var RecordDef = Roo.data.Record.create([
6443     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
6444     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
6445 ]);
6446 var myReader = new Roo.data.ArrayReader({
6447     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
6448 }, RecordDef);
6449 </code></pre>
6450  * <p>
6451  * This would consume an Array like this:
6452  * <pre><code>
6453 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
6454   </code></pre>
6455  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
6456  * @constructor
6457  * Create a new JsonReader
6458  * @param {Object} meta Metadata configuration options.
6459  * @param {Object} recordType Either an Array of field definition objects
6460  * as specified to {@link Roo.data.Record#create},
6461  * or an {@link Roo.data.Record} object
6462  * created using {@link Roo.data.Record#create}.
6463  */
6464 Roo.data.ArrayReader = function(meta, recordType){
6465     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
6466 };
6467
6468 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
6469     /**
6470      * Create a data block containing Roo.data.Records from an XML document.
6471      * @param {Object} o An Array of row objects which represents the dataset.
6472      * @return {Object} data A data block which is used by an Roo.data.Store object as
6473      * a cache of Roo.data.Records.
6474      */
6475     readRecords : function(o){
6476         var sid = this.meta ? this.meta.id : null;
6477         var recordType = this.recordType, fields = recordType.prototype.fields;
6478         var records = [];
6479         var root = o;
6480             for(var i = 0; i < root.length; i++){
6481                     var n = root[i];
6482                 var values = {};
6483                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
6484                 for(var j = 0, jlen = fields.length; j < jlen; j++){
6485                 var f = fields.items[j];
6486                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
6487                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
6488                 v = f.convert(v);
6489                 values[f.name] = v;
6490             }
6491                 var record = new recordType(values, id);
6492                 record.json = n;
6493                 records[records.length] = record;
6494             }
6495             return {
6496                 records : records,
6497                 totalRecords : records.length
6498             };
6499     }
6500 });/*
6501  * Based on:
6502  * Ext JS Library 1.1.1
6503  * Copyright(c) 2006-2007, Ext JS, LLC.
6504  *
6505  * Originally Released Under LGPL - original licence link has changed is not relivant.
6506  *
6507  * Fork - LGPL
6508  * <script type="text/javascript">
6509  */
6510
6511
6512 /**
6513  * @class Roo.data.Tree
6514  * @extends Roo.util.Observable
6515  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
6516  * in the tree have most standard DOM functionality.
6517  * @constructor
6518  * @param {Node} root (optional) The root node
6519  */
6520 Roo.data.Tree = function(root){
6521    this.nodeHash = {};
6522    /**
6523     * The root node for this tree
6524     * @type Node
6525     */
6526    this.root = null;
6527    if(root){
6528        this.setRootNode(root);
6529    }
6530    this.addEvents({
6531        /**
6532         * @event append
6533         * Fires when a new child node is appended to a node in this tree.
6534         * @param {Tree} tree The owner tree
6535         * @param {Node} parent The parent node
6536         * @param {Node} node The newly appended node
6537         * @param {Number} index The index of the newly appended node
6538         */
6539        "append" : true,
6540        /**
6541         * @event remove
6542         * Fires when a child node is removed from a node in this tree.
6543         * @param {Tree} tree The owner tree
6544         * @param {Node} parent The parent node
6545         * @param {Node} node The child node removed
6546         */
6547        "remove" : true,
6548        /**
6549         * @event move
6550         * Fires when a node is moved to a new location in the tree
6551         * @param {Tree} tree The owner tree
6552         * @param {Node} node The node moved
6553         * @param {Node} oldParent The old parent of this node
6554         * @param {Node} newParent The new parent of this node
6555         * @param {Number} index The index it was moved to
6556         */
6557        "move" : true,
6558        /**
6559         * @event insert
6560         * Fires when a new child node is inserted in a node in this tree.
6561         * @param {Tree} tree The owner tree
6562         * @param {Node} parent The parent node
6563         * @param {Node} node The child node inserted
6564         * @param {Node} refNode The child node the node was inserted before
6565         */
6566        "insert" : true,
6567        /**
6568         * @event beforeappend
6569         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
6570         * @param {Tree} tree The owner tree
6571         * @param {Node} parent The parent node
6572         * @param {Node} node The child node to be appended
6573         */
6574        "beforeappend" : true,
6575        /**
6576         * @event beforeremove
6577         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
6578         * @param {Tree} tree The owner tree
6579         * @param {Node} parent The parent node
6580         * @param {Node} node The child node to be removed
6581         */
6582        "beforeremove" : true,
6583        /**
6584         * @event beforemove
6585         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
6586         * @param {Tree} tree The owner tree
6587         * @param {Node} node The node being moved
6588         * @param {Node} oldParent The parent of the node
6589         * @param {Node} newParent The new parent the node is moving to
6590         * @param {Number} index The index it is being moved to
6591         */
6592        "beforemove" : true,
6593        /**
6594         * @event beforeinsert
6595         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
6596         * @param {Tree} tree The owner tree
6597         * @param {Node} parent The parent node
6598         * @param {Node} node The child node to be inserted
6599         * @param {Node} refNode The child node the node is being inserted before
6600         */
6601        "beforeinsert" : true
6602    });
6603
6604     Roo.data.Tree.superclass.constructor.call(this);
6605 };
6606
6607 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
6608     pathSeparator: "/",
6609
6610     proxyNodeEvent : function(){
6611         return this.fireEvent.apply(this, arguments);
6612     },
6613
6614     /**
6615      * Returns the root node for this tree.
6616      * @return {Node}
6617      */
6618     getRootNode : function(){
6619         return this.root;
6620     },
6621
6622     /**
6623      * Sets the root node for this tree.
6624      * @param {Node} node
6625      * @return {Node}
6626      */
6627     setRootNode : function(node){
6628         this.root = node;
6629         node.ownerTree = this;
6630         node.isRoot = true;
6631         this.registerNode(node);
6632         return node;
6633     },
6634
6635     /**
6636      * Gets a node in this tree by its id.
6637      * @param {String} id
6638      * @return {Node}
6639      */
6640     getNodeById : function(id){
6641         return this.nodeHash[id];
6642     },
6643
6644     registerNode : function(node){
6645         this.nodeHash[node.id] = node;
6646     },
6647
6648     unregisterNode : function(node){
6649         delete this.nodeHash[node.id];
6650     },
6651
6652     toString : function(){
6653         return "[Tree"+(this.id?" "+this.id:"")+"]";
6654     }
6655 });
6656
6657 /**
6658  * @class Roo.data.Node
6659  * @extends Roo.util.Observable
6660  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
6661  * @cfg {String} id The id for this node. If one is not specified, one is generated.
6662  * @constructor
6663  * @param {Object} attributes The attributes/config for the node
6664  */
6665 Roo.data.Node = function(attributes){
6666     /**
6667      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
6668      * @type {Object}
6669      */
6670     this.attributes = attributes || {};
6671     this.leaf = this.attributes.leaf;
6672     /**
6673      * The node id. @type String
6674      */
6675     this.id = this.attributes.id;
6676     if(!this.id){
6677         this.id = Roo.id(null, "ynode-");
6678         this.attributes.id = this.id;
6679     }
6680      
6681     
6682     /**
6683      * All child nodes of this node. @type Array
6684      */
6685     this.childNodes = [];
6686     if(!this.childNodes.indexOf){ // indexOf is a must
6687         this.childNodes.indexOf = function(o){
6688             for(var i = 0, len = this.length; i < len; i++){
6689                 if(this[i] == o) {
6690                     return i;
6691                 }
6692             }
6693             return -1;
6694         };
6695     }
6696     /**
6697      * The parent node for this node. @type Node
6698      */
6699     this.parentNode = null;
6700     /**
6701      * The first direct child node of this node, or null if this node has no child nodes. @type Node
6702      */
6703     this.firstChild = null;
6704     /**
6705      * The last direct child node of this node, or null if this node has no child nodes. @type Node
6706      */
6707     this.lastChild = null;
6708     /**
6709      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
6710      */
6711     this.previousSibling = null;
6712     /**
6713      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
6714      */
6715     this.nextSibling = null;
6716
6717     this.addEvents({
6718        /**
6719         * @event append
6720         * Fires when a new child node is appended
6721         * @param {Tree} tree The owner tree
6722         * @param {Node} this This node
6723         * @param {Node} node The newly appended node
6724         * @param {Number} index The index of the newly appended node
6725         */
6726        "append" : true,
6727        /**
6728         * @event remove
6729         * Fires when a child node is removed
6730         * @param {Tree} tree The owner tree
6731         * @param {Node} this This node
6732         * @param {Node} node The removed node
6733         */
6734        "remove" : true,
6735        /**
6736         * @event move
6737         * Fires when this node is moved to a new location in the tree
6738         * @param {Tree} tree The owner tree
6739         * @param {Node} this This node
6740         * @param {Node} oldParent The old parent of this node
6741         * @param {Node} newParent The new parent of this node
6742         * @param {Number} index The index it was moved to
6743         */
6744        "move" : true,
6745        /**
6746         * @event insert
6747         * Fires when a new child node is inserted.
6748         * @param {Tree} tree The owner tree
6749         * @param {Node} this This node
6750         * @param {Node} node The child node inserted
6751         * @param {Node} refNode The child node the node was inserted before
6752         */
6753        "insert" : true,
6754        /**
6755         * @event beforeappend
6756         * Fires before a new child is appended, return false to cancel the append.
6757         * @param {Tree} tree The owner tree
6758         * @param {Node} this This node
6759         * @param {Node} node The child node to be appended
6760         */
6761        "beforeappend" : true,
6762        /**
6763         * @event beforeremove
6764         * Fires before a child is removed, return false to cancel the remove.
6765         * @param {Tree} tree The owner tree
6766         * @param {Node} this This node
6767         * @param {Node} node The child node to be removed
6768         */
6769        "beforeremove" : true,
6770        /**
6771         * @event beforemove
6772         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
6773         * @param {Tree} tree The owner tree
6774         * @param {Node} this This node
6775         * @param {Node} oldParent The parent of this node
6776         * @param {Node} newParent The new parent this node is moving to
6777         * @param {Number} index The index it is being moved to
6778         */
6779        "beforemove" : true,
6780        /**
6781         * @event beforeinsert
6782         * Fires before a new child is inserted, return false to cancel the insert.
6783         * @param {Tree} tree The owner tree
6784         * @param {Node} this This node
6785         * @param {Node} node The child node to be inserted
6786         * @param {Node} refNode The child node the node is being inserted before
6787         */
6788        "beforeinsert" : true
6789    });
6790     this.listeners = this.attributes.listeners;
6791     Roo.data.Node.superclass.constructor.call(this);
6792 };
6793
6794 Roo.extend(Roo.data.Node, Roo.util.Observable, {
6795     fireEvent : function(evtName){
6796         // first do standard event for this node
6797         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
6798             return false;
6799         }
6800         // then bubble it up to the tree if the event wasn't cancelled
6801         var ot = this.getOwnerTree();
6802         if(ot){
6803             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
6804                 return false;
6805             }
6806         }
6807         return true;
6808     },
6809
6810     /**
6811      * Returns true if this node is a leaf
6812      * @return {Boolean}
6813      */
6814     isLeaf : function(){
6815         return this.leaf === true;
6816     },
6817
6818     // private
6819     setFirstChild : function(node){
6820         this.firstChild = node;
6821     },
6822
6823     //private
6824     setLastChild : function(node){
6825         this.lastChild = node;
6826     },
6827
6828
6829     /**
6830      * Returns true if this node is the last child of its parent
6831      * @return {Boolean}
6832      */
6833     isLast : function(){
6834        return (!this.parentNode ? true : this.parentNode.lastChild == this);
6835     },
6836
6837     /**
6838      * Returns true if this node is the first child of its parent
6839      * @return {Boolean}
6840      */
6841     isFirst : function(){
6842        return (!this.parentNode ? true : this.parentNode.firstChild == this);
6843     },
6844
6845     hasChildNodes : function(){
6846         return !this.isLeaf() && this.childNodes.length > 0;
6847     },
6848
6849     /**
6850      * Insert node(s) as the last child node of this node.
6851      * @param {Node/Array} node The node or Array of nodes to append
6852      * @return {Node} The appended node if single append, or null if an array was passed
6853      */
6854     appendChild : function(node){
6855         var multi = false;
6856         if(node instanceof Array){
6857             multi = node;
6858         }else if(arguments.length > 1){
6859             multi = arguments;
6860         }
6861         // if passed an array or multiple args do them one by one
6862         if(multi){
6863             for(var i = 0, len = multi.length; i < len; i++) {
6864                 this.appendChild(multi[i]);
6865             }
6866         }else{
6867             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
6868                 return false;
6869             }
6870             var index = this.childNodes.length;
6871             var oldParent = node.parentNode;
6872             // it's a move, make sure we move it cleanly
6873             if(oldParent){
6874                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
6875                     return false;
6876                 }
6877                 oldParent.removeChild(node);
6878             }
6879             index = this.childNodes.length;
6880             if(index == 0){
6881                 this.setFirstChild(node);
6882             }
6883             this.childNodes.push(node);
6884             node.parentNode = this;
6885             var ps = this.childNodes[index-1];
6886             if(ps){
6887                 node.previousSibling = ps;
6888                 ps.nextSibling = node;
6889             }else{
6890                 node.previousSibling = null;
6891             }
6892             node.nextSibling = null;
6893             this.setLastChild(node);
6894             node.setOwnerTree(this.getOwnerTree());
6895             this.fireEvent("append", this.ownerTree, this, node, index);
6896             if(oldParent){
6897                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
6898             }
6899             return node;
6900         }
6901     },
6902
6903     /**
6904      * Removes a child node from this node.
6905      * @param {Node} node The node to remove
6906      * @return {Node} The removed node
6907      */
6908     removeChild : function(node){
6909         var index = this.childNodes.indexOf(node);
6910         if(index == -1){
6911             return false;
6912         }
6913         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
6914             return false;
6915         }
6916
6917         // remove it from childNodes collection
6918         this.childNodes.splice(index, 1);
6919
6920         // update siblings
6921         if(node.previousSibling){
6922             node.previousSibling.nextSibling = node.nextSibling;
6923         }
6924         if(node.nextSibling){
6925             node.nextSibling.previousSibling = node.previousSibling;
6926         }
6927
6928         // update child refs
6929         if(this.firstChild == node){
6930             this.setFirstChild(node.nextSibling);
6931         }
6932         if(this.lastChild == node){
6933             this.setLastChild(node.previousSibling);
6934         }
6935
6936         node.setOwnerTree(null);
6937         // clear any references from the node
6938         node.parentNode = null;
6939         node.previousSibling = null;
6940         node.nextSibling = null;
6941         this.fireEvent("remove", this.ownerTree, this, node);
6942         return node;
6943     },
6944
6945     /**
6946      * Inserts the first node before the second node in this nodes childNodes collection.
6947      * @param {Node} node The node to insert
6948      * @param {Node} refNode The node to insert before (if null the node is appended)
6949      * @return {Node} The inserted node
6950      */
6951     insertBefore : function(node, refNode){
6952         if(!refNode){ // like standard Dom, refNode can be null for append
6953             return this.appendChild(node);
6954         }
6955         // nothing to do
6956         if(node == refNode){
6957             return false;
6958         }
6959
6960         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
6961             return false;
6962         }
6963         var index = this.childNodes.indexOf(refNode);
6964         var oldParent = node.parentNode;
6965         var refIndex = index;
6966
6967         // when moving internally, indexes will change after remove
6968         if(oldParent == this && this.childNodes.indexOf(node) < index){
6969             refIndex--;
6970         }
6971
6972         // it's a move, make sure we move it cleanly
6973         if(oldParent){
6974             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
6975                 return false;
6976             }
6977             oldParent.removeChild(node);
6978         }
6979         if(refIndex == 0){
6980             this.setFirstChild(node);
6981         }
6982         this.childNodes.splice(refIndex, 0, node);
6983         node.parentNode = this;
6984         var ps = this.childNodes[refIndex-1];
6985         if(ps){
6986             node.previousSibling = ps;
6987             ps.nextSibling = node;
6988         }else{
6989             node.previousSibling = null;
6990         }
6991         node.nextSibling = refNode;
6992         refNode.previousSibling = node;
6993         node.setOwnerTree(this.getOwnerTree());
6994         this.fireEvent("insert", this.ownerTree, this, node, refNode);
6995         if(oldParent){
6996             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
6997         }
6998         return node;
6999     },
7000
7001     /**
7002      * Returns the child node at the specified index.
7003      * @param {Number} index
7004      * @return {Node}
7005      */
7006     item : function(index){
7007         return this.childNodes[index];
7008     },
7009
7010     /**
7011      * Replaces one child node in this node with another.
7012      * @param {Node} newChild The replacement node
7013      * @param {Node} oldChild The node to replace
7014      * @return {Node} The replaced node
7015      */
7016     replaceChild : function(newChild, oldChild){
7017         this.insertBefore(newChild, oldChild);
7018         this.removeChild(oldChild);
7019         return oldChild;
7020     },
7021
7022     /**
7023      * Returns the index of a child node
7024      * @param {Node} node
7025      * @return {Number} The index of the node or -1 if it was not found
7026      */
7027     indexOf : function(child){
7028         return this.childNodes.indexOf(child);
7029     },
7030
7031     /**
7032      * Returns the tree this node is in.
7033      * @return {Tree}
7034      */
7035     getOwnerTree : function(){
7036         // if it doesn't have one, look for one
7037         if(!this.ownerTree){
7038             var p = this;
7039             while(p){
7040                 if(p.ownerTree){
7041                     this.ownerTree = p.ownerTree;
7042                     break;
7043                 }
7044                 p = p.parentNode;
7045             }
7046         }
7047         return this.ownerTree;
7048     },
7049
7050     /**
7051      * Returns depth of this node (the root node has a depth of 0)
7052      * @return {Number}
7053      */
7054     getDepth : function(){
7055         var depth = 0;
7056         var p = this;
7057         while(p.parentNode){
7058             ++depth;
7059             p = p.parentNode;
7060         }
7061         return depth;
7062     },
7063
7064     // private
7065     setOwnerTree : function(tree){
7066         // if it's move, we need to update everyone
7067         if(tree != this.ownerTree){
7068             if(this.ownerTree){
7069                 this.ownerTree.unregisterNode(this);
7070             }
7071             this.ownerTree = tree;
7072             var cs = this.childNodes;
7073             for(var i = 0, len = cs.length; i < len; i++) {
7074                 cs[i].setOwnerTree(tree);
7075             }
7076             if(tree){
7077                 tree.registerNode(this);
7078             }
7079         }
7080     },
7081
7082     /**
7083      * Returns the path for this node. The path can be used to expand or select this node programmatically.
7084      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
7085      * @return {String} The path
7086      */
7087     getPath : function(attr){
7088         attr = attr || "id";
7089         var p = this.parentNode;
7090         var b = [this.attributes[attr]];
7091         while(p){
7092             b.unshift(p.attributes[attr]);
7093             p = p.parentNode;
7094         }
7095         var sep = this.getOwnerTree().pathSeparator;
7096         return sep + b.join(sep);
7097     },
7098
7099     /**
7100      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7101      * function call will be the scope provided or the current node. The arguments to the function
7102      * will be the args provided or the current node. If the function returns false at any point,
7103      * the bubble is stopped.
7104      * @param {Function} fn The function to call
7105      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7106      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7107      */
7108     bubble : function(fn, scope, args){
7109         var p = this;
7110         while(p){
7111             if(fn.call(scope || p, args || p) === false){
7112                 break;
7113             }
7114             p = p.parentNode;
7115         }
7116     },
7117
7118     /**
7119      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
7120      * function call will be the scope provided or the current node. The arguments to the function
7121      * will be the args provided or the current node. If the function returns false at any point,
7122      * the cascade is stopped on that branch.
7123      * @param {Function} fn The function to call
7124      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7125      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7126      */
7127     cascade : function(fn, scope, args){
7128         if(fn.call(scope || this, args || this) !== false){
7129             var cs = this.childNodes;
7130             for(var i = 0, len = cs.length; i < len; i++) {
7131                 cs[i].cascade(fn, scope, args);
7132             }
7133         }
7134     },
7135
7136     /**
7137      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
7138      * function call will be the scope provided or the current node. The arguments to the function
7139      * will be the args provided or the current node. If the function returns false at any point,
7140      * the iteration stops.
7141      * @param {Function} fn The function to call
7142      * @param {Object} scope (optional) The scope of the function (defaults to current node)
7143      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
7144      */
7145     eachChild : function(fn, scope, args){
7146         var cs = this.childNodes;
7147         for(var i = 0, len = cs.length; i < len; i++) {
7148                 if(fn.call(scope || this, args || cs[i]) === false){
7149                     break;
7150                 }
7151         }
7152     },
7153
7154     /**
7155      * Finds the first child that has the attribute with the specified value.
7156      * @param {String} attribute The attribute name
7157      * @param {Mixed} value The value to search for
7158      * @return {Node} The found child or null if none was found
7159      */
7160     findChild : function(attribute, value){
7161         var cs = this.childNodes;
7162         for(var i = 0, len = cs.length; i < len; i++) {
7163                 if(cs[i].attributes[attribute] == value){
7164                     return cs[i];
7165                 }
7166         }
7167         return null;
7168     },
7169
7170     /**
7171      * Finds the first child by a custom function. The child matches if the function passed
7172      * returns true.
7173      * @param {Function} fn
7174      * @param {Object} scope (optional)
7175      * @return {Node} The found child or null if none was found
7176      */
7177     findChildBy : function(fn, scope){
7178         var cs = this.childNodes;
7179         for(var i = 0, len = cs.length; i < len; i++) {
7180                 if(fn.call(scope||cs[i], cs[i]) === true){
7181                     return cs[i];
7182                 }
7183         }
7184         return null;
7185     },
7186
7187     /**
7188      * Sorts this nodes children using the supplied sort function
7189      * @param {Function} fn
7190      * @param {Object} scope (optional)
7191      */
7192     sort : function(fn, scope){
7193         var cs = this.childNodes;
7194         var len = cs.length;
7195         if(len > 0){
7196             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
7197             cs.sort(sortFn);
7198             for(var i = 0; i < len; i++){
7199                 var n = cs[i];
7200                 n.previousSibling = cs[i-1];
7201                 n.nextSibling = cs[i+1];
7202                 if(i == 0){
7203                     this.setFirstChild(n);
7204                 }
7205                 if(i == len-1){
7206                     this.setLastChild(n);
7207                 }
7208             }
7209         }
7210     },
7211
7212     /**
7213      * Returns true if this node is an ancestor (at any point) of the passed node.
7214      * @param {Node} node
7215      * @return {Boolean}
7216      */
7217     contains : function(node){
7218         return node.isAncestor(this);
7219     },
7220
7221     /**
7222      * Returns true if the passed node is an ancestor (at any point) of this node.
7223      * @param {Node} node
7224      * @return {Boolean}
7225      */
7226     isAncestor : function(node){
7227         var p = this.parentNode;
7228         while(p){
7229             if(p == node){
7230                 return true;
7231             }
7232             p = p.parentNode;
7233         }
7234         return false;
7235     },
7236
7237     toString : function(){
7238         return "[Node"+(this.id?" "+this.id:"")+"]";
7239     }
7240 });/*
7241  * Based on:
7242  * Ext JS Library 1.1.1
7243  * Copyright(c) 2006-2007, Ext JS, LLC.
7244  *
7245  * Originally Released Under LGPL - original licence link has changed is not relivant.
7246  *
7247  * Fork - LGPL
7248  * <script type="text/javascript">
7249  */
7250  
7251
7252 /**
7253  * @class Roo.ComponentMgr
7254  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
7255  * @singleton
7256  */
7257 Roo.ComponentMgr = function(){
7258     var all = new Roo.util.MixedCollection();
7259
7260     return {
7261         /**
7262          * Registers a component.
7263          * @param {Roo.Component} c The component
7264          */
7265         register : function(c){
7266             all.add(c);
7267         },
7268
7269         /**
7270          * Unregisters a component.
7271          * @param {Roo.Component} c The component
7272          */
7273         unregister : function(c){
7274             all.remove(c);
7275         },
7276
7277         /**
7278          * Returns a component by id
7279          * @param {String} id The component id
7280          */
7281         get : function(id){
7282             return all.get(id);
7283         },
7284
7285         /**
7286          * Registers a function that will be called when a specified component is added to ComponentMgr
7287          * @param {String} id The component id
7288          * @param {Funtction} fn The callback function
7289          * @param {Object} scope The scope of the callback
7290          */
7291         onAvailable : function(id, fn, scope){
7292             all.on("add", function(index, o){
7293                 if(o.id == id){
7294                     fn.call(scope || o, o);
7295                     all.un("add", fn, scope);
7296                 }
7297             });
7298         }
7299     };
7300 }();/*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310  
7311 /**
7312  * @class Roo.Component
7313  * @extends Roo.util.Observable
7314  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
7315  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
7316  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
7317  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
7318  * All visual components (widgets) that require rendering into a layout should subclass Component.
7319  * @constructor
7320  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
7321  * 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
7322  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
7323  */
7324 Roo.Component = function(config){
7325     config = config || {};
7326     if(config.tagName || config.dom || typeof config == "string"){ // element object
7327         config = {el: config, id: config.id || config};
7328     }
7329     this.initialConfig = config;
7330
7331     Roo.apply(this, config);
7332     this.addEvents({
7333         /**
7334          * @event disable
7335          * Fires after the component is disabled.
7336              * @param {Roo.Component} this
7337              */
7338         disable : true,
7339         /**
7340          * @event enable
7341          * Fires after the component is enabled.
7342              * @param {Roo.Component} this
7343              */
7344         enable : true,
7345         /**
7346          * @event beforeshow
7347          * Fires before the component is shown.  Return false to stop the show.
7348              * @param {Roo.Component} this
7349              */
7350         beforeshow : true,
7351         /**
7352          * @event show
7353          * Fires after the component is shown.
7354              * @param {Roo.Component} this
7355              */
7356         show : true,
7357         /**
7358          * @event beforehide
7359          * Fires before the component is hidden. Return false to stop the hide.
7360              * @param {Roo.Component} this
7361              */
7362         beforehide : true,
7363         /**
7364          * @event hide
7365          * Fires after the component is hidden.
7366              * @param {Roo.Component} this
7367              */
7368         hide : true,
7369         /**
7370          * @event beforerender
7371          * Fires before the component is rendered. Return false to stop the render.
7372              * @param {Roo.Component} this
7373              */
7374         beforerender : true,
7375         /**
7376          * @event render
7377          * Fires after the component is rendered.
7378              * @param {Roo.Component} this
7379              */
7380         render : true,
7381         /**
7382          * @event beforedestroy
7383          * Fires before the component is destroyed. Return false to stop the destroy.
7384              * @param {Roo.Component} this
7385              */
7386         beforedestroy : true,
7387         /**
7388          * @event destroy
7389          * Fires after the component is destroyed.
7390              * @param {Roo.Component} this
7391              */
7392         destroy : true
7393     });
7394     if(!this.id){
7395         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
7396     }
7397     Roo.ComponentMgr.register(this);
7398     Roo.Component.superclass.constructor.call(this);
7399     this.initComponent();
7400     if(this.renderTo){ // not supported by all components yet. use at your own risk!
7401         this.render(this.renderTo);
7402         delete this.renderTo;
7403     }
7404 };
7405
7406 /** @private */
7407 Roo.Component.AUTO_ID = 1000;
7408
7409 Roo.extend(Roo.Component, Roo.util.Observable, {
7410     /**
7411      * @scope Roo.Component.prototype
7412      * @type {Boolean}
7413      * true if this component is hidden. Read-only.
7414      */
7415     hidden : false,
7416     /**
7417      * @type {Boolean}
7418      * true if this component is disabled. Read-only.
7419      */
7420     disabled : false,
7421     /**
7422      * @type {Boolean}
7423      * true if this component has been rendered. Read-only.
7424      */
7425     rendered : false,
7426     
7427     /** @cfg {String} disableClass
7428      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
7429      */
7430     disabledClass : "x-item-disabled",
7431         /** @cfg {Boolean} allowDomMove
7432          * Whether the component can move the Dom node when rendering (defaults to true).
7433          */
7434     allowDomMove : true,
7435     /** @cfg {String} hideMode
7436      * How this component should hidden. Supported values are
7437      * "visibility" (css visibility), "offsets" (negative offset position) and
7438      * "display" (css display) - defaults to "display".
7439      */
7440     hideMode: 'display',
7441
7442     /** @private */
7443     ctype : "Roo.Component",
7444
7445     /**
7446      * @cfg {String} actionMode 
7447      * which property holds the element that used for  hide() / show() / disable() / enable()
7448      * default is 'el' 
7449      */
7450     actionMode : "el",
7451
7452     /** @private */
7453     getActionEl : function(){
7454         return this[this.actionMode];
7455     },
7456
7457     initComponent : Roo.emptyFn,
7458     /**
7459      * If this is a lazy rendering component, render it to its container element.
7460      * @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.
7461      */
7462     render : function(container, position){
7463         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
7464             if(!container && this.el){
7465                 this.el = Roo.get(this.el);
7466                 container = this.el.dom.parentNode;
7467                 this.allowDomMove = false;
7468             }
7469             this.container = Roo.get(container);
7470             this.rendered = true;
7471             if(position !== undefined){
7472                 if(typeof position == 'number'){
7473                     position = this.container.dom.childNodes[position];
7474                 }else{
7475                     position = Roo.getDom(position);
7476                 }
7477             }
7478             this.onRender(this.container, position || null);
7479             if(this.cls){
7480                 this.el.addClass(this.cls);
7481                 delete this.cls;
7482             }
7483             if(this.style){
7484                 this.el.applyStyles(this.style);
7485                 delete this.style;
7486             }
7487             this.fireEvent("render", this);
7488             this.afterRender(this.container);
7489             if(this.hidden){
7490                 this.hide();
7491             }
7492             if(this.disabled){
7493                 this.disable();
7494             }
7495         }
7496         return this;
7497     },
7498
7499     /** @private */
7500     // default function is not really useful
7501     onRender : function(ct, position){
7502         if(this.el){
7503             this.el = Roo.get(this.el);
7504             if(this.allowDomMove !== false){
7505                 ct.dom.insertBefore(this.el.dom, position);
7506             }
7507         }
7508     },
7509
7510     /** @private */
7511     getAutoCreate : function(){
7512         var cfg = typeof this.autoCreate == "object" ?
7513                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
7514         if(this.id && !cfg.id){
7515             cfg.id = this.id;
7516         }
7517         return cfg;
7518     },
7519
7520     /** @private */
7521     afterRender : Roo.emptyFn,
7522
7523     /**
7524      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
7525      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
7526      */
7527     destroy : function(){
7528         if(this.fireEvent("beforedestroy", this) !== false){
7529             this.purgeListeners();
7530             this.beforeDestroy();
7531             if(this.rendered){
7532                 this.el.removeAllListeners();
7533                 this.el.remove();
7534                 if(this.actionMode == "container"){
7535                     this.container.remove();
7536                 }
7537             }
7538             this.onDestroy();
7539             Roo.ComponentMgr.unregister(this);
7540             this.fireEvent("destroy", this);
7541         }
7542     },
7543
7544         /** @private */
7545     beforeDestroy : function(){
7546
7547     },
7548
7549         /** @private */
7550         onDestroy : function(){
7551
7552     },
7553
7554     /**
7555      * Returns the underlying {@link Roo.Element}.
7556      * @return {Roo.Element} The element
7557      */
7558     getEl : function(){
7559         return this.el;
7560     },
7561
7562     /**
7563      * Returns the id of this component.
7564      * @return {String}
7565      */
7566     getId : function(){
7567         return this.id;
7568     },
7569
7570     /**
7571      * Try to focus this component.
7572      * @param {Boolean} selectText True to also select the text in this component (if applicable)
7573      * @return {Roo.Component} this
7574      */
7575     focus : function(selectText){
7576         if(this.rendered){
7577             this.el.focus();
7578             if(selectText === true){
7579                 this.el.dom.select();
7580             }
7581         }
7582         return this;
7583     },
7584
7585     /** @private */
7586     blur : function(){
7587         if(this.rendered){
7588             this.el.blur();
7589         }
7590         return this;
7591     },
7592
7593     /**
7594      * Disable this component.
7595      * @return {Roo.Component} this
7596      */
7597     disable : function(){
7598         if(this.rendered){
7599             this.onDisable();
7600         }
7601         this.disabled = true;
7602         this.fireEvent("disable", this);
7603         return this;
7604     },
7605
7606         // private
7607     onDisable : function(){
7608         this.getActionEl().addClass(this.disabledClass);
7609         this.el.dom.disabled = true;
7610     },
7611
7612     /**
7613      * Enable this component.
7614      * @return {Roo.Component} this
7615      */
7616     enable : function(){
7617         if(this.rendered){
7618             this.onEnable();
7619         }
7620         this.disabled = false;
7621         this.fireEvent("enable", this);
7622         return this;
7623     },
7624
7625         // private
7626     onEnable : function(){
7627         this.getActionEl().removeClass(this.disabledClass);
7628         this.el.dom.disabled = false;
7629     },
7630
7631     /**
7632      * Convenience function for setting disabled/enabled by boolean.
7633      * @param {Boolean} disabled
7634      */
7635     setDisabled : function(disabled){
7636         this[disabled ? "disable" : "enable"]();
7637     },
7638
7639     /**
7640      * Show this component.
7641      * @return {Roo.Component} this
7642      */
7643     show: function(){
7644         if(this.fireEvent("beforeshow", this) !== false){
7645             this.hidden = false;
7646             if(this.rendered){
7647                 this.onShow();
7648             }
7649             this.fireEvent("show", this);
7650         }
7651         return this;
7652     },
7653
7654     // private
7655     onShow : function(){
7656         var ae = this.getActionEl();
7657         if(this.hideMode == 'visibility'){
7658             ae.dom.style.visibility = "visible";
7659         }else if(this.hideMode == 'offsets'){
7660             ae.removeClass('x-hidden');
7661         }else{
7662             ae.dom.style.display = "";
7663         }
7664     },
7665
7666     /**
7667      * Hide this component.
7668      * @return {Roo.Component} this
7669      */
7670     hide: function(){
7671         if(this.fireEvent("beforehide", this) !== false){
7672             this.hidden = true;
7673             if(this.rendered){
7674                 this.onHide();
7675             }
7676             this.fireEvent("hide", this);
7677         }
7678         return this;
7679     },
7680
7681     // private
7682     onHide : function(){
7683         var ae = this.getActionEl();
7684         if(this.hideMode == 'visibility'){
7685             ae.dom.style.visibility = "hidden";
7686         }else if(this.hideMode == 'offsets'){
7687             ae.addClass('x-hidden');
7688         }else{
7689             ae.dom.style.display = "none";
7690         }
7691     },
7692
7693     /**
7694      * Convenience function to hide or show this component by boolean.
7695      * @param {Boolean} visible True to show, false to hide
7696      * @return {Roo.Component} this
7697      */
7698     setVisible: function(visible){
7699         if(visible) {
7700             this.show();
7701         }else{
7702             this.hide();
7703         }
7704         return this;
7705     },
7706
7707     /**
7708      * Returns true if this component is visible.
7709      */
7710     isVisible : function(){
7711         return this.getActionEl().isVisible();
7712     },
7713
7714     cloneConfig : function(overrides){
7715         overrides = overrides || {};
7716         var id = overrides.id || Roo.id();
7717         var cfg = Roo.applyIf(overrides, this.initialConfig);
7718         cfg.id = id; // prevent dup id
7719         return new this.constructor(cfg);
7720     }
7721 });/*
7722  * Based on:
7723  * Ext JS Library 1.1.1
7724  * Copyright(c) 2006-2007, Ext JS, LLC.
7725  *
7726  * Originally Released Under LGPL - original licence link has changed is not relivant.
7727  *
7728  * Fork - LGPL
7729  * <script type="text/javascript">
7730  */
7731  (function(){ 
7732 /**
7733  * @class Roo.Layer
7734  * @extends Roo.Element
7735  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
7736  * automatic maintaining of shadow/shim positions.
7737  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
7738  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
7739  * you can pass a string with a CSS class name. False turns off the shadow.
7740  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
7741  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
7742  * @cfg {String} cls CSS class to add to the element
7743  * @cfg {Number} zindex Starting z-index (defaults to 11000)
7744  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
7745  * @constructor
7746  * @param {Object} config An object with config options.
7747  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
7748  */
7749
7750 Roo.Layer = function(config, existingEl){
7751     config = config || {};
7752     var dh = Roo.DomHelper;
7753     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
7754     if(existingEl){
7755         this.dom = Roo.getDom(existingEl);
7756     }
7757     if(!this.dom){
7758         var o = config.dh || {tag: "div", cls: "x-layer"};
7759         this.dom = dh.append(pel, o);
7760     }
7761     if(config.cls){
7762         this.addClass(config.cls);
7763     }
7764     this.constrain = config.constrain !== false;
7765     this.visibilityMode = Roo.Element.VISIBILITY;
7766     if(config.id){
7767         this.id = this.dom.id = config.id;
7768     }else{
7769         this.id = Roo.id(this.dom);
7770     }
7771     this.zindex = config.zindex || this.getZIndex();
7772     this.position("absolute", this.zindex);
7773     if(config.shadow){
7774         this.shadowOffset = config.shadowOffset || 4;
7775         this.shadow = new Roo.Shadow({
7776             offset : this.shadowOffset,
7777             mode : config.shadow
7778         });
7779     }else{
7780         this.shadowOffset = 0;
7781     }
7782     this.useShim = config.shim !== false && Roo.useShims;
7783     this.useDisplay = config.useDisplay;
7784     this.hide();
7785 };
7786
7787 var supr = Roo.Element.prototype;
7788
7789 // shims are shared among layer to keep from having 100 iframes
7790 var shims = [];
7791
7792 Roo.extend(Roo.Layer, Roo.Element, {
7793
7794     getZIndex : function(){
7795         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
7796     },
7797
7798     getShim : function(){
7799         if(!this.useShim){
7800             return null;
7801         }
7802         if(this.shim){
7803             return this.shim;
7804         }
7805         var shim = shims.shift();
7806         if(!shim){
7807             shim = this.createShim();
7808             shim.enableDisplayMode('block');
7809             shim.dom.style.display = 'none';
7810             shim.dom.style.visibility = 'visible';
7811         }
7812         var pn = this.dom.parentNode;
7813         if(shim.dom.parentNode != pn){
7814             pn.insertBefore(shim.dom, this.dom);
7815         }
7816         shim.setStyle('z-index', this.getZIndex()-2);
7817         this.shim = shim;
7818         return shim;
7819     },
7820
7821     hideShim : function(){
7822         if(this.shim){
7823             this.shim.setDisplayed(false);
7824             shims.push(this.shim);
7825             delete this.shim;
7826         }
7827     },
7828
7829     disableShadow : function(){
7830         if(this.shadow){
7831             this.shadowDisabled = true;
7832             this.shadow.hide();
7833             this.lastShadowOffset = this.shadowOffset;
7834             this.shadowOffset = 0;
7835         }
7836     },
7837
7838     enableShadow : function(show){
7839         if(this.shadow){
7840             this.shadowDisabled = false;
7841             this.shadowOffset = this.lastShadowOffset;
7842             delete this.lastShadowOffset;
7843             if(show){
7844                 this.sync(true);
7845             }
7846         }
7847     },
7848
7849     // private
7850     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
7851     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
7852     sync : function(doShow){
7853         var sw = this.shadow;
7854         if(!this.updating && this.isVisible() && (sw || this.useShim)){
7855             var sh = this.getShim();
7856
7857             var w = this.getWidth(),
7858                 h = this.getHeight();
7859
7860             var l = this.getLeft(true),
7861                 t = this.getTop(true);
7862
7863             if(sw && !this.shadowDisabled){
7864                 if(doShow && !sw.isVisible()){
7865                     sw.show(this);
7866                 }else{
7867                     sw.realign(l, t, w, h);
7868                 }
7869                 if(sh){
7870                     if(doShow){
7871                        sh.show();
7872                     }
7873                     // fit the shim behind the shadow, so it is shimmed too
7874                     var a = sw.adjusts, s = sh.dom.style;
7875                     s.left = (Math.min(l, l+a.l))+"px";
7876                     s.top = (Math.min(t, t+a.t))+"px";
7877                     s.width = (w+a.w)+"px";
7878                     s.height = (h+a.h)+"px";
7879                 }
7880             }else if(sh){
7881                 if(doShow){
7882                    sh.show();
7883                 }
7884                 sh.setSize(w, h);
7885                 sh.setLeftTop(l, t);
7886             }
7887             
7888         }
7889     },
7890
7891     // private
7892     destroy : function(){
7893         this.hideShim();
7894         if(this.shadow){
7895             this.shadow.hide();
7896         }
7897         this.removeAllListeners();
7898         var pn = this.dom.parentNode;
7899         if(pn){
7900             pn.removeChild(this.dom);
7901         }
7902         Roo.Element.uncache(this.id);
7903     },
7904
7905     remove : function(){
7906         this.destroy();
7907     },
7908
7909     // private
7910     beginUpdate : function(){
7911         this.updating = true;
7912     },
7913
7914     // private
7915     endUpdate : function(){
7916         this.updating = false;
7917         this.sync(true);
7918     },
7919
7920     // private
7921     hideUnders : function(negOffset){
7922         if(this.shadow){
7923             this.shadow.hide();
7924         }
7925         this.hideShim();
7926     },
7927
7928     // private
7929     constrainXY : function(){
7930         if(this.constrain){
7931             var vw = Roo.lib.Dom.getViewWidth(),
7932                 vh = Roo.lib.Dom.getViewHeight();
7933             var s = Roo.get(document).getScroll();
7934
7935             var xy = this.getXY();
7936             var x = xy[0], y = xy[1];   
7937             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
7938             // only move it if it needs it
7939             var moved = false;
7940             // first validate right/bottom
7941             if((x + w) > vw+s.left){
7942                 x = vw - w - this.shadowOffset;
7943                 moved = true;
7944             }
7945             if((y + h) > vh+s.top){
7946                 y = vh - h - this.shadowOffset;
7947                 moved = true;
7948             }
7949             // then make sure top/left isn't negative
7950             if(x < s.left){
7951                 x = s.left;
7952                 moved = true;
7953             }
7954             if(y < s.top){
7955                 y = s.top;
7956                 moved = true;
7957             }
7958             if(moved){
7959                 if(this.avoidY){
7960                     var ay = this.avoidY;
7961                     if(y <= ay && (y+h) >= ay){
7962                         y = ay-h-5;   
7963                     }
7964                 }
7965                 xy = [x, y];
7966                 this.storeXY(xy);
7967                 supr.setXY.call(this, xy);
7968                 this.sync();
7969             }
7970         }
7971     },
7972
7973     isVisible : function(){
7974         return this.visible;    
7975     },
7976
7977     // private
7978     showAction : function(){
7979         this.visible = true; // track visibility to prevent getStyle calls
7980         if(this.useDisplay === true){
7981             this.setDisplayed("");
7982         }else if(this.lastXY){
7983             supr.setXY.call(this, this.lastXY);
7984         }else if(this.lastLT){
7985             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
7986         }
7987     },
7988
7989     // private
7990     hideAction : function(){
7991         this.visible = false;
7992         if(this.useDisplay === true){
7993             this.setDisplayed(false);
7994         }else{
7995             this.setLeftTop(-10000,-10000);
7996         }
7997     },
7998
7999     // overridden Element method
8000     setVisible : function(v, a, d, c, e){
8001         if(v){
8002             this.showAction();
8003         }
8004         if(a && v){
8005             var cb = function(){
8006                 this.sync(true);
8007                 if(c){
8008                     c();
8009                 }
8010             }.createDelegate(this);
8011             supr.setVisible.call(this, true, true, d, cb, e);
8012         }else{
8013             if(!v){
8014                 this.hideUnders(true);
8015             }
8016             var cb = c;
8017             if(a){
8018                 cb = function(){
8019                     this.hideAction();
8020                     if(c){
8021                         c();
8022                     }
8023                 }.createDelegate(this);
8024             }
8025             supr.setVisible.call(this, v, a, d, cb, e);
8026             if(v){
8027                 this.sync(true);
8028             }else if(!a){
8029                 this.hideAction();
8030             }
8031         }
8032     },
8033
8034     storeXY : function(xy){
8035         delete this.lastLT;
8036         this.lastXY = xy;
8037     },
8038
8039     storeLeftTop : function(left, top){
8040         delete this.lastXY;
8041         this.lastLT = [left, top];
8042     },
8043
8044     // private
8045     beforeFx : function(){
8046         this.beforeAction();
8047         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
8048     },
8049
8050     // private
8051     afterFx : function(){
8052         Roo.Layer.superclass.afterFx.apply(this, arguments);
8053         this.sync(this.isVisible());
8054     },
8055
8056     // private
8057     beforeAction : function(){
8058         if(!this.updating && this.shadow){
8059             this.shadow.hide();
8060         }
8061     },
8062
8063     // overridden Element method
8064     setLeft : function(left){
8065         this.storeLeftTop(left, this.getTop(true));
8066         supr.setLeft.apply(this, arguments);
8067         this.sync();
8068     },
8069
8070     setTop : function(top){
8071         this.storeLeftTop(this.getLeft(true), top);
8072         supr.setTop.apply(this, arguments);
8073         this.sync();
8074     },
8075
8076     setLeftTop : function(left, top){
8077         this.storeLeftTop(left, top);
8078         supr.setLeftTop.apply(this, arguments);
8079         this.sync();
8080     },
8081
8082     setXY : function(xy, a, d, c, e){
8083         this.fixDisplay();
8084         this.beforeAction();
8085         this.storeXY(xy);
8086         var cb = this.createCB(c);
8087         supr.setXY.call(this, xy, a, d, cb, e);
8088         if(!a){
8089             cb();
8090         }
8091     },
8092
8093     // private
8094     createCB : function(c){
8095         var el = this;
8096         return function(){
8097             el.constrainXY();
8098             el.sync(true);
8099             if(c){
8100                 c();
8101             }
8102         };
8103     },
8104
8105     // overridden Element method
8106     setX : function(x, a, d, c, e){
8107         this.setXY([x, this.getY()], a, d, c, e);
8108     },
8109
8110     // overridden Element method
8111     setY : function(y, a, d, c, e){
8112         this.setXY([this.getX(), y], a, d, c, e);
8113     },
8114
8115     // overridden Element method
8116     setSize : function(w, h, a, d, c, e){
8117         this.beforeAction();
8118         var cb = this.createCB(c);
8119         supr.setSize.call(this, w, h, a, d, cb, e);
8120         if(!a){
8121             cb();
8122         }
8123     },
8124
8125     // overridden Element method
8126     setWidth : function(w, a, d, c, e){
8127         this.beforeAction();
8128         var cb = this.createCB(c);
8129         supr.setWidth.call(this, w, a, d, cb, e);
8130         if(!a){
8131             cb();
8132         }
8133     },
8134
8135     // overridden Element method
8136     setHeight : function(h, a, d, c, e){
8137         this.beforeAction();
8138         var cb = this.createCB(c);
8139         supr.setHeight.call(this, h, a, d, cb, e);
8140         if(!a){
8141             cb();
8142         }
8143     },
8144
8145     // overridden Element method
8146     setBounds : function(x, y, w, h, a, d, c, e){
8147         this.beforeAction();
8148         var cb = this.createCB(c);
8149         if(!a){
8150             this.storeXY([x, y]);
8151             supr.setXY.call(this, [x, y]);
8152             supr.setSize.call(this, w, h, a, d, cb, e);
8153             cb();
8154         }else{
8155             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
8156         }
8157         return this;
8158     },
8159     
8160     /**
8161      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
8162      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
8163      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
8164      * @param {Number} zindex The new z-index to set
8165      * @return {this} The Layer
8166      */
8167     setZIndex : function(zindex){
8168         this.zindex = zindex;
8169         this.setStyle("z-index", zindex + 2);
8170         if(this.shadow){
8171             this.shadow.setZIndex(zindex + 1);
8172         }
8173         if(this.shim){
8174             this.shim.setStyle("z-index", zindex);
8175         }
8176     }
8177 });
8178 })();/*
8179  * Based on:
8180  * Ext JS Library 1.1.1
8181  * Copyright(c) 2006-2007, Ext JS, LLC.
8182  *
8183  * Originally Released Under LGPL - original licence link has changed is not relivant.
8184  *
8185  * Fork - LGPL
8186  * <script type="text/javascript">
8187  */
8188
8189
8190 /**
8191  * @class Roo.Shadow
8192  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
8193  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
8194  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
8195  * @constructor
8196  * Create a new Shadow
8197  * @param {Object} config The config object
8198  */
8199 Roo.Shadow = function(config){
8200     Roo.apply(this, config);
8201     if(typeof this.mode != "string"){
8202         this.mode = this.defaultMode;
8203     }
8204     var o = this.offset, a = {h: 0};
8205     var rad = Math.floor(this.offset/2);
8206     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
8207         case "drop":
8208             a.w = 0;
8209             a.l = a.t = o;
8210             a.t -= 1;
8211             if(Roo.isIE){
8212                 a.l -= this.offset + rad;
8213                 a.t -= this.offset + rad;
8214                 a.w -= rad;
8215                 a.h -= rad;
8216                 a.t += 1;
8217             }
8218         break;
8219         case "sides":
8220             a.w = (o*2);
8221             a.l = -o;
8222             a.t = o-1;
8223             if(Roo.isIE){
8224                 a.l -= (this.offset - rad);
8225                 a.t -= this.offset + rad;
8226                 a.l += 1;
8227                 a.w -= (this.offset - rad)*2;
8228                 a.w -= rad + 1;
8229                 a.h -= 1;
8230             }
8231         break;
8232         case "frame":
8233             a.w = a.h = (o*2);
8234             a.l = a.t = -o;
8235             a.t += 1;
8236             a.h -= 2;
8237             if(Roo.isIE){
8238                 a.l -= (this.offset - rad);
8239                 a.t -= (this.offset - rad);
8240                 a.l += 1;
8241                 a.w -= (this.offset + rad + 1);
8242                 a.h -= (this.offset + rad);
8243                 a.h += 1;
8244             }
8245         break;
8246     };
8247
8248     this.adjusts = a;
8249 };
8250
8251 Roo.Shadow.prototype = {
8252     /**
8253      * @cfg {String} mode
8254      * The shadow display mode.  Supports the following options:<br />
8255      * sides: Shadow displays on both sides and bottom only<br />
8256      * frame: Shadow displays equally on all four sides<br />
8257      * drop: Traditional bottom-right drop shadow (default)
8258      */
8259     /**
8260      * @cfg {String} offset
8261      * The number of pixels to offset the shadow from the element (defaults to 4)
8262      */
8263     offset: 4,
8264
8265     // private
8266     defaultMode: "drop",
8267
8268     /**
8269      * Displays the shadow under the target element
8270      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
8271      */
8272     show : function(target){
8273         target = Roo.get(target);
8274         if(!this.el){
8275             this.el = Roo.Shadow.Pool.pull();
8276             if(this.el.dom.nextSibling != target.dom){
8277                 this.el.insertBefore(target);
8278             }
8279         }
8280         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
8281         if(Roo.isIE){
8282             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
8283         }
8284         this.realign(
8285             target.getLeft(true),
8286             target.getTop(true),
8287             target.getWidth(),
8288             target.getHeight()
8289         );
8290         this.el.dom.style.display = "block";
8291     },
8292
8293     /**
8294      * Returns true if the shadow is visible, else false
8295      */
8296     isVisible : function(){
8297         return this.el ? true : false;  
8298     },
8299
8300     /**
8301      * Direct alignment when values are already available. Show must be called at least once before
8302      * calling this method to ensure it is initialized.
8303      * @param {Number} left The target element left position
8304      * @param {Number} top The target element top position
8305      * @param {Number} width The target element width
8306      * @param {Number} height The target element height
8307      */
8308     realign : function(l, t, w, h){
8309         if(!this.el){
8310             return;
8311         }
8312         var a = this.adjusts, d = this.el.dom, s = d.style;
8313         var iea = 0;
8314         s.left = (l+a.l)+"px";
8315         s.top = (t+a.t)+"px";
8316         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
8317  
8318         if(s.width != sws || s.height != shs){
8319             s.width = sws;
8320             s.height = shs;
8321             if(!Roo.isIE){
8322                 var cn = d.childNodes;
8323                 var sww = Math.max(0, (sw-12))+"px";
8324                 cn[0].childNodes[1].style.width = sww;
8325                 cn[1].childNodes[1].style.width = sww;
8326                 cn[2].childNodes[1].style.width = sww;
8327                 cn[1].style.height = Math.max(0, (sh-12))+"px";
8328             }
8329         }
8330     },
8331
8332     /**
8333      * Hides this shadow
8334      */
8335     hide : function(){
8336         if(this.el){
8337             this.el.dom.style.display = "none";
8338             Roo.Shadow.Pool.push(this.el);
8339             delete this.el;
8340         }
8341     },
8342
8343     /**
8344      * Adjust the z-index of this shadow
8345      * @param {Number} zindex The new z-index
8346      */
8347     setZIndex : function(z){
8348         this.zIndex = z;
8349         if(this.el){
8350             this.el.setStyle("z-index", z);
8351         }
8352     }
8353 };
8354
8355 // Private utility class that manages the internal Shadow cache
8356 Roo.Shadow.Pool = function(){
8357     var p = [];
8358     var markup = Roo.isIE ?
8359                  '<div class="x-ie-shadow"></div>' :
8360                  '<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>';
8361     return {
8362         pull : function(){
8363             var sh = p.shift();
8364             if(!sh){
8365                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
8366                 sh.autoBoxAdjust = false;
8367             }
8368             return sh;
8369         },
8370
8371         push : function(sh){
8372             p.push(sh);
8373         }
8374     };
8375 }();/*
8376  * Based on:
8377  * Ext JS Library 1.1.1
8378  * Copyright(c) 2006-2007, Ext JS, LLC.
8379  *
8380  * Originally Released Under LGPL - original licence link has changed is not relivant.
8381  *
8382  * Fork - LGPL
8383  * <script type="text/javascript">
8384  */
8385
8386 /**
8387  * @class Roo.BoxComponent
8388  * @extends Roo.Component
8389  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
8390  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
8391  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
8392  * layout containers.
8393  * @constructor
8394  * @param {Roo.Element/String/Object} config The configuration options.
8395  */
8396 Roo.BoxComponent = function(config){
8397     Roo.Component.call(this, config);
8398     this.addEvents({
8399         /**
8400          * @event resize
8401          * Fires after the component is resized.
8402              * @param {Roo.Component} this
8403              * @param {Number} adjWidth The box-adjusted width that was set
8404              * @param {Number} adjHeight The box-adjusted height that was set
8405              * @param {Number} rawWidth The width that was originally specified
8406              * @param {Number} rawHeight The height that was originally specified
8407              */
8408         resize : true,
8409         /**
8410          * @event move
8411          * Fires after the component is moved.
8412              * @param {Roo.Component} this
8413              * @param {Number} x The new x position
8414              * @param {Number} y The new y position
8415              */
8416         move : true
8417     });
8418 };
8419
8420 Roo.extend(Roo.BoxComponent, Roo.Component, {
8421     // private, set in afterRender to signify that the component has been rendered
8422     boxReady : false,
8423     // private, used to defer height settings to subclasses
8424     deferHeight: false,
8425     /** @cfg {Number} width
8426      * width (optional) size of component
8427      */
8428      /** @cfg {Number} height
8429      * height (optional) size of component
8430      */
8431      
8432     /**
8433      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
8434      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
8435      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
8436      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
8437      * @return {Roo.BoxComponent} this
8438      */
8439     setSize : function(w, h){
8440         // support for standard size objects
8441         if(typeof w == 'object'){
8442             h = w.height;
8443             w = w.width;
8444         }
8445         // not rendered
8446         if(!this.boxReady){
8447             this.width = w;
8448             this.height = h;
8449             return this;
8450         }
8451
8452         // prevent recalcs when not needed
8453         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
8454             return this;
8455         }
8456         this.lastSize = {width: w, height: h};
8457
8458         var adj = this.adjustSize(w, h);
8459         var aw = adj.width, ah = adj.height;
8460         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
8461             var rz = this.getResizeEl();
8462             if(!this.deferHeight && aw !== undefined && ah !== undefined){
8463                 rz.setSize(aw, ah);
8464             }else if(!this.deferHeight && ah !== undefined){
8465                 rz.setHeight(ah);
8466             }else if(aw !== undefined){
8467                 rz.setWidth(aw);
8468             }
8469             this.onResize(aw, ah, w, h);
8470             this.fireEvent('resize', this, aw, ah, w, h);
8471         }
8472         return this;
8473     },
8474
8475     /**
8476      * Gets the current size of the component's underlying element.
8477      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8478      */
8479     getSize : function(){
8480         return this.el.getSize();
8481     },
8482
8483     /**
8484      * Gets the current XY position of the component's underlying element.
8485      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8486      * @return {Array} The XY position of the element (e.g., [100, 200])
8487      */
8488     getPosition : function(local){
8489         if(local === true){
8490             return [this.el.getLeft(true), this.el.getTop(true)];
8491         }
8492         return this.xy || this.el.getXY();
8493     },
8494
8495     /**
8496      * Gets the current box measurements of the component's underlying element.
8497      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
8498      * @returns {Object} box An object in the format {x, y, width, height}
8499      */
8500     getBox : function(local){
8501         var s = this.el.getSize();
8502         if(local){
8503             s.x = this.el.getLeft(true);
8504             s.y = this.el.getTop(true);
8505         }else{
8506             var xy = this.xy || this.el.getXY();
8507             s.x = xy[0];
8508             s.y = xy[1];
8509         }
8510         return s;
8511     },
8512
8513     /**
8514      * Sets the current box measurements of the component's underlying element.
8515      * @param {Object} box An object in the format {x, y, width, height}
8516      * @returns {Roo.BoxComponent} this
8517      */
8518     updateBox : function(box){
8519         this.setSize(box.width, box.height);
8520         this.setPagePosition(box.x, box.y);
8521         return this;
8522     },
8523
8524     // protected
8525     getResizeEl : function(){
8526         return this.resizeEl || this.el;
8527     },
8528
8529     // protected
8530     getPositionEl : function(){
8531         return this.positionEl || this.el;
8532     },
8533
8534     /**
8535      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
8536      * This method fires the move event.
8537      * @param {Number} left The new left
8538      * @param {Number} top The new top
8539      * @returns {Roo.BoxComponent} this
8540      */
8541     setPosition : function(x, y){
8542         this.x = x;
8543         this.y = y;
8544         if(!this.boxReady){
8545             return this;
8546         }
8547         var adj = this.adjustPosition(x, y);
8548         var ax = adj.x, ay = adj.y;
8549
8550         var el = this.getPositionEl();
8551         if(ax !== undefined || ay !== undefined){
8552             if(ax !== undefined && ay !== undefined){
8553                 el.setLeftTop(ax, ay);
8554             }else if(ax !== undefined){
8555                 el.setLeft(ax);
8556             }else if(ay !== undefined){
8557                 el.setTop(ay);
8558             }
8559             this.onPosition(ax, ay);
8560             this.fireEvent('move', this, ax, ay);
8561         }
8562         return this;
8563     },
8564
8565     /**
8566      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
8567      * This method fires the move event.
8568      * @param {Number} x The new x position
8569      * @param {Number} y The new y position
8570      * @returns {Roo.BoxComponent} this
8571      */
8572     setPagePosition : function(x, y){
8573         this.pageX = x;
8574         this.pageY = y;
8575         if(!this.boxReady){
8576             return;
8577         }
8578         if(x === undefined || y === undefined){ // cannot translate undefined points
8579             return;
8580         }
8581         var p = this.el.translatePoints(x, y);
8582         this.setPosition(p.left, p.top);
8583         return this;
8584     },
8585
8586     // private
8587     onRender : function(ct, position){
8588         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
8589         if(this.resizeEl){
8590             this.resizeEl = Roo.get(this.resizeEl);
8591         }
8592         if(this.positionEl){
8593             this.positionEl = Roo.get(this.positionEl);
8594         }
8595     },
8596
8597     // private
8598     afterRender : function(){
8599         Roo.BoxComponent.superclass.afterRender.call(this);
8600         this.boxReady = true;
8601         this.setSize(this.width, this.height);
8602         if(this.x || this.y){
8603             this.setPosition(this.x, this.y);
8604         }
8605         if(this.pageX || this.pageY){
8606             this.setPagePosition(this.pageX, this.pageY);
8607         }
8608     },
8609
8610     /**
8611      * Force the component's size to recalculate based on the underlying element's current height and width.
8612      * @returns {Roo.BoxComponent} this
8613      */
8614     syncSize : function(){
8615         delete this.lastSize;
8616         this.setSize(this.el.getWidth(), this.el.getHeight());
8617         return this;
8618     },
8619
8620     /**
8621      * Called after the component is resized, this method is empty by default but can be implemented by any
8622      * subclass that needs to perform custom logic after a resize occurs.
8623      * @param {Number} adjWidth The box-adjusted width that was set
8624      * @param {Number} adjHeight The box-adjusted height that was set
8625      * @param {Number} rawWidth The width that was originally specified
8626      * @param {Number} rawHeight The height that was originally specified
8627      */
8628     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
8629
8630     },
8631
8632     /**
8633      * Called after the component is moved, this method is empty by default but can be implemented by any
8634      * subclass that needs to perform custom logic after a move occurs.
8635      * @param {Number} x The new x position
8636      * @param {Number} y The new y position
8637      */
8638     onPosition : function(x, y){
8639
8640     },
8641
8642     // private
8643     adjustSize : function(w, h){
8644         if(this.autoWidth){
8645             w = 'auto';
8646         }
8647         if(this.autoHeight){
8648             h = 'auto';
8649         }
8650         return {width : w, height: h};
8651     },
8652
8653     // private
8654     adjustPosition : function(x, y){
8655         return {x : x, y: y};
8656     }
8657 });/*
8658  * Based on:
8659  * Ext JS Library 1.1.1
8660  * Copyright(c) 2006-2007, Ext JS, LLC.
8661  *
8662  * Originally Released Under LGPL - original licence link has changed is not relivant.
8663  *
8664  * Fork - LGPL
8665  * <script type="text/javascript">
8666  */
8667
8668
8669 /**
8670  * @class Roo.SplitBar
8671  * @extends Roo.util.Observable
8672  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
8673  * <br><br>
8674  * Usage:
8675  * <pre><code>
8676 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
8677                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
8678 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
8679 split.minSize = 100;
8680 split.maxSize = 600;
8681 split.animate = true;
8682 split.on('moved', splitterMoved);
8683 </code></pre>
8684  * @constructor
8685  * Create a new SplitBar
8686  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
8687  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
8688  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8689  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
8690                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
8691                         position of the SplitBar).
8692  */
8693 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
8694     
8695     /** @private */
8696     this.el = Roo.get(dragElement, true);
8697     this.el.dom.unselectable = "on";
8698     /** @private */
8699     this.resizingEl = Roo.get(resizingElement, true);
8700
8701     /**
8702      * @private
8703      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
8704      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
8705      * @type Number
8706      */
8707     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
8708     
8709     /**
8710      * The minimum size of the resizing element. (Defaults to 0)
8711      * @type Number
8712      */
8713     this.minSize = 0;
8714     
8715     /**
8716      * The maximum size of the resizing element. (Defaults to 2000)
8717      * @type Number
8718      */
8719     this.maxSize = 2000;
8720     
8721     /**
8722      * Whether to animate the transition to the new size
8723      * @type Boolean
8724      */
8725     this.animate = false;
8726     
8727     /**
8728      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
8729      * @type Boolean
8730      */
8731     this.useShim = false;
8732     
8733     /** @private */
8734     this.shim = null;
8735     
8736     if(!existingProxy){
8737         /** @private */
8738         this.proxy = Roo.SplitBar.createProxy(this.orientation);
8739     }else{
8740         this.proxy = Roo.get(existingProxy).dom;
8741     }
8742     /** @private */
8743     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
8744     
8745     /** @private */
8746     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
8747     
8748     /** @private */
8749     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
8750     
8751     /** @private */
8752     this.dragSpecs = {};
8753     
8754     /**
8755      * @private The adapter to use to positon and resize elements
8756      */
8757     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
8758     this.adapter.init(this);
8759     
8760     if(this.orientation == Roo.SplitBar.HORIZONTAL){
8761         /** @private */
8762         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
8763         this.el.addClass("x-splitbar-h");
8764     }else{
8765         /** @private */
8766         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
8767         this.el.addClass("x-splitbar-v");
8768     }
8769     
8770     this.addEvents({
8771         /**
8772          * @event resize
8773          * Fires when the splitter is moved (alias for {@link #event-moved})
8774          * @param {Roo.SplitBar} this
8775          * @param {Number} newSize the new width or height
8776          */
8777         "resize" : true,
8778         /**
8779          * @event moved
8780          * Fires when the splitter is moved
8781          * @param {Roo.SplitBar} this
8782          * @param {Number} newSize the new width or height
8783          */
8784         "moved" : true,
8785         /**
8786          * @event beforeresize
8787          * Fires before the splitter is dragged
8788          * @param {Roo.SplitBar} this
8789          */
8790         "beforeresize" : true,
8791
8792         "beforeapply" : true
8793     });
8794
8795     Roo.util.Observable.call(this);
8796 };
8797
8798 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
8799     onStartProxyDrag : function(x, y){
8800         this.fireEvent("beforeresize", this);
8801         if(!this.overlay){
8802             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
8803             o.unselectable();
8804             o.enableDisplayMode("block");
8805             // all splitbars share the same overlay
8806             Roo.SplitBar.prototype.overlay = o;
8807         }
8808         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8809         this.overlay.show();
8810         Roo.get(this.proxy).setDisplayed("block");
8811         var size = this.adapter.getElementSize(this);
8812         this.activeMinSize = this.getMinimumSize();;
8813         this.activeMaxSize = this.getMaximumSize();;
8814         var c1 = size - this.activeMinSize;
8815         var c2 = Math.max(this.activeMaxSize - size, 0);
8816         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8817             this.dd.resetConstraints();
8818             this.dd.setXConstraint(
8819                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
8820                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
8821             );
8822             this.dd.setYConstraint(0, 0);
8823         }else{
8824             this.dd.resetConstraints();
8825             this.dd.setXConstraint(0, 0);
8826             this.dd.setYConstraint(
8827                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
8828                 this.placement == Roo.SplitBar.TOP ? c2 : c1
8829             );
8830          }
8831         this.dragSpecs.startSize = size;
8832         this.dragSpecs.startPoint = [x, y];
8833         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
8834     },
8835     
8836     /** 
8837      * @private Called after the drag operation by the DDProxy
8838      */
8839     onEndProxyDrag : function(e){
8840         Roo.get(this.proxy).setDisplayed(false);
8841         var endPoint = Roo.lib.Event.getXY(e);
8842         if(this.overlay){
8843             this.overlay.hide();
8844         }
8845         var newSize;
8846         if(this.orientation == Roo.SplitBar.HORIZONTAL){
8847             newSize = this.dragSpecs.startSize + 
8848                 (this.placement == Roo.SplitBar.LEFT ?
8849                     endPoint[0] - this.dragSpecs.startPoint[0] :
8850                     this.dragSpecs.startPoint[0] - endPoint[0]
8851                 );
8852         }else{
8853             newSize = this.dragSpecs.startSize + 
8854                 (this.placement == Roo.SplitBar.TOP ?
8855                     endPoint[1] - this.dragSpecs.startPoint[1] :
8856                     this.dragSpecs.startPoint[1] - endPoint[1]
8857                 );
8858         }
8859         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
8860         if(newSize != this.dragSpecs.startSize){
8861             if(this.fireEvent('beforeapply', this, newSize) !== false){
8862                 this.adapter.setElementSize(this, newSize);
8863                 this.fireEvent("moved", this, newSize);
8864                 this.fireEvent("resize", this, newSize);
8865             }
8866         }
8867     },
8868     
8869     /**
8870      * Get the adapter this SplitBar uses
8871      * @return The adapter object
8872      */
8873     getAdapter : function(){
8874         return this.adapter;
8875     },
8876     
8877     /**
8878      * Set the adapter this SplitBar uses
8879      * @param {Object} adapter A SplitBar adapter object
8880      */
8881     setAdapter : function(adapter){
8882         this.adapter = adapter;
8883         this.adapter.init(this);
8884     },
8885     
8886     /**
8887      * Gets the minimum size for the resizing element
8888      * @return {Number} The minimum size
8889      */
8890     getMinimumSize : function(){
8891         return this.minSize;
8892     },
8893     
8894     /**
8895      * Sets the minimum size for the resizing element
8896      * @param {Number} minSize The minimum size
8897      */
8898     setMinimumSize : function(minSize){
8899         this.minSize = minSize;
8900     },
8901     
8902     /**
8903      * Gets the maximum size for the resizing element
8904      * @return {Number} The maximum size
8905      */
8906     getMaximumSize : function(){
8907         return this.maxSize;
8908     },
8909     
8910     /**
8911      * Sets the maximum size for the resizing element
8912      * @param {Number} maxSize The maximum size
8913      */
8914     setMaximumSize : function(maxSize){
8915         this.maxSize = maxSize;
8916     },
8917     
8918     /**
8919      * Sets the initialize size for the resizing element
8920      * @param {Number} size The initial size
8921      */
8922     setCurrentSize : function(size){
8923         var oldAnimate = this.animate;
8924         this.animate = false;
8925         this.adapter.setElementSize(this, size);
8926         this.animate = oldAnimate;
8927     },
8928     
8929     /**
8930      * Destroy this splitbar. 
8931      * @param {Boolean} removeEl True to remove the element
8932      */
8933     destroy : function(removeEl){
8934         if(this.shim){
8935             this.shim.remove();
8936         }
8937         this.dd.unreg();
8938         this.proxy.parentNode.removeChild(this.proxy);
8939         if(removeEl){
8940             this.el.remove();
8941         }
8942     }
8943 });
8944
8945 /**
8946  * @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.
8947  */
8948 Roo.SplitBar.createProxy = function(dir){
8949     var proxy = new Roo.Element(document.createElement("div"));
8950     proxy.unselectable();
8951     var cls = 'x-splitbar-proxy';
8952     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
8953     document.body.appendChild(proxy.dom);
8954     return proxy.dom;
8955 };
8956
8957 /** 
8958  * @class Roo.SplitBar.BasicLayoutAdapter
8959  * Default Adapter. It assumes the splitter and resizing element are not positioned
8960  * elements and only gets/sets the width of the element. Generally used for table based layouts.
8961  */
8962 Roo.SplitBar.BasicLayoutAdapter = function(){
8963 };
8964
8965 Roo.SplitBar.BasicLayoutAdapter.prototype = {
8966     // do nothing for now
8967     init : function(s){
8968     
8969     },
8970     /**
8971      * Called before drag operations to get the current size of the resizing element. 
8972      * @param {Roo.SplitBar} s The SplitBar using this adapter
8973      */
8974      getElementSize : function(s){
8975         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8976             return s.resizingEl.getWidth();
8977         }else{
8978             return s.resizingEl.getHeight();
8979         }
8980     },
8981     
8982     /**
8983      * Called after drag operations to set the size of the resizing element.
8984      * @param {Roo.SplitBar} s The SplitBar using this adapter
8985      * @param {Number} newSize The new size to set
8986      * @param {Function} onComplete A function to be invoked when resizing is complete
8987      */
8988     setElementSize : function(s, newSize, onComplete){
8989         if(s.orientation == Roo.SplitBar.HORIZONTAL){
8990             if(!s.animate){
8991                 s.resizingEl.setWidth(newSize);
8992                 if(onComplete){
8993                     onComplete(s, newSize);
8994                 }
8995             }else{
8996                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
8997             }
8998         }else{
8999             
9000             if(!s.animate){
9001                 s.resizingEl.setHeight(newSize);
9002                 if(onComplete){
9003                     onComplete(s, newSize);
9004                 }
9005             }else{
9006                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
9007             }
9008         }
9009     }
9010 };
9011
9012 /** 
9013  *@class Roo.SplitBar.AbsoluteLayoutAdapter
9014  * @extends Roo.SplitBar.BasicLayoutAdapter
9015  * Adapter that  moves the splitter element to align with the resized sizing element. 
9016  * Used with an absolute positioned SplitBar.
9017  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
9018  * document.body, make sure you assign an id to the body element.
9019  */
9020 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
9021     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
9022     this.container = Roo.get(container);
9023 };
9024
9025 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
9026     init : function(s){
9027         this.basic.init(s);
9028     },
9029     
9030     getElementSize : function(s){
9031         return this.basic.getElementSize(s);
9032     },
9033     
9034     setElementSize : function(s, newSize, onComplete){
9035         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
9036     },
9037     
9038     moveSplitter : function(s){
9039         var yes = Roo.SplitBar;
9040         switch(s.placement){
9041             case yes.LEFT:
9042                 s.el.setX(s.resizingEl.getRight());
9043                 break;
9044             case yes.RIGHT:
9045                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
9046                 break;
9047             case yes.TOP:
9048                 s.el.setY(s.resizingEl.getBottom());
9049                 break;
9050             case yes.BOTTOM:
9051                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
9052                 break;
9053         }
9054     }
9055 };
9056
9057 /**
9058  * Orientation constant - Create a vertical SplitBar
9059  * @static
9060  * @type Number
9061  */
9062 Roo.SplitBar.VERTICAL = 1;
9063
9064 /**
9065  * Orientation constant - Create a horizontal SplitBar
9066  * @static
9067  * @type Number
9068  */
9069 Roo.SplitBar.HORIZONTAL = 2;
9070
9071 /**
9072  * Placement constant - The resizing element is to the left of the splitter element
9073  * @static
9074  * @type Number
9075  */
9076 Roo.SplitBar.LEFT = 1;
9077
9078 /**
9079  * Placement constant - The resizing element is to the right of the splitter element
9080  * @static
9081  * @type Number
9082  */
9083 Roo.SplitBar.RIGHT = 2;
9084
9085 /**
9086  * Placement constant - The resizing element is positioned above the splitter element
9087  * @static
9088  * @type Number
9089  */
9090 Roo.SplitBar.TOP = 3;
9091
9092 /**
9093  * Placement constant - The resizing element is positioned under splitter element
9094  * @static
9095  * @type Number
9096  */
9097 Roo.SplitBar.BOTTOM = 4;
9098 /*
9099  * Based on:
9100  * Ext JS Library 1.1.1
9101  * Copyright(c) 2006-2007, Ext JS, LLC.
9102  *
9103  * Originally Released Under LGPL - original licence link has changed is not relivant.
9104  *
9105  * Fork - LGPL
9106  * <script type="text/javascript">
9107  */
9108
9109 /**
9110  * @class Roo.View
9111  * @extends Roo.util.Observable
9112  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
9113  * This class also supports single and multi selection modes. <br>
9114  * Create a data model bound view:
9115  <pre><code>
9116  var store = new Roo.data.Store(...);
9117
9118  var view = new Roo.View({
9119     el : "my-element",
9120     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
9121  
9122     singleSelect: true,
9123     selectedClass: "ydataview-selected",
9124     store: store
9125  });
9126
9127  // listen for node click?
9128  view.on("click", function(vw, index, node, e){
9129  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9130  });
9131
9132  // load XML data
9133  dataModel.load("foobar.xml");
9134  </code></pre>
9135  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9136  * <br><br>
9137  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9138  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9139  * 
9140  * Note: old style constructor is still suported (container, template, config)
9141  * 
9142  * @constructor
9143  * Create a new View
9144  * @param {Object} config The config object
9145  * 
9146  */
9147 Roo.View = function(config, depreciated_tpl, depreciated_config){
9148     
9149     if (typeof(depreciated_tpl) == 'undefined') {
9150         // new way.. - universal constructor.
9151         Roo.apply(this, config);
9152         this.el  = Roo.get(this.el);
9153     } else {
9154         // old format..
9155         this.el  = Roo.get(config);
9156         this.tpl = depreciated_tpl;
9157         Roo.apply(this, depreciated_config);
9158     }
9159      
9160     
9161     if(typeof(this.tpl) == "string"){
9162         this.tpl = new Roo.Template(this.tpl);
9163     } else {
9164         // support xtype ctors..
9165         this.tpl = new Roo.factory(this.tpl, Roo);
9166     }
9167     
9168     
9169     this.tpl.compile();
9170    
9171
9172      
9173     /** @private */
9174     this.addEvents({
9175         /**
9176          * @event beforeclick
9177          * Fires before a click is processed. Returns false to cancel the default action.
9178          * @param {Roo.View} this
9179          * @param {Number} index The index of the target node
9180          * @param {HTMLElement} node The target node
9181          * @param {Roo.EventObject} e The raw event object
9182          */
9183             "beforeclick" : true,
9184         /**
9185          * @event click
9186          * Fires when a template node is clicked.
9187          * @param {Roo.View} this
9188          * @param {Number} index The index of the target node
9189          * @param {HTMLElement} node The target node
9190          * @param {Roo.EventObject} e The raw event object
9191          */
9192             "click" : true,
9193         /**
9194          * @event dblclick
9195          * Fires when a template node is double clicked.
9196          * @param {Roo.View} this
9197          * @param {Number} index The index of the target node
9198          * @param {HTMLElement} node The target node
9199          * @param {Roo.EventObject} e The raw event object
9200          */
9201             "dblclick" : true,
9202         /**
9203          * @event contextmenu
9204          * Fires when a template node is right clicked.
9205          * @param {Roo.View} this
9206          * @param {Number} index The index of the target node
9207          * @param {HTMLElement} node The target node
9208          * @param {Roo.EventObject} e The raw event object
9209          */
9210             "contextmenu" : true,
9211         /**
9212          * @event selectionchange
9213          * Fires when the selected nodes change.
9214          * @param {Roo.View} this
9215          * @param {Array} selections Array of the selected nodes
9216          */
9217             "selectionchange" : true,
9218     
9219         /**
9220          * @event beforeselect
9221          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9222          * @param {Roo.View} this
9223          * @param {HTMLElement} node The node to be selected
9224          * @param {Array} selections Array of currently selected nodes
9225          */
9226             "beforeselect" : true,
9227         /**
9228          * @event preparedata
9229          * Fires on every row to render, to allow you to change the data.
9230          * @param {Roo.View} this
9231          * @param {Object} data to be rendered (change this)
9232          */
9233           "preparedata" : true
9234         });
9235
9236     this.el.on({
9237         "click": this.onClick,
9238         "dblclick": this.onDblClick,
9239         "contextmenu": this.onContextMenu,
9240         scope:this
9241     });
9242
9243     this.selections = [];
9244     this.nodes = [];
9245     this.cmp = new Roo.CompositeElementLite([]);
9246     if(this.store){
9247         this.store = Roo.factory(this.store, Roo.data);
9248         this.setStore(this.store, true);
9249     }
9250     Roo.View.superclass.constructor.call(this);
9251 };
9252
9253 Roo.extend(Roo.View, Roo.util.Observable, {
9254     
9255      /**
9256      * @cfg {Roo.data.Store} store Data store to load data from.
9257      */
9258     store : false,
9259     
9260     /**
9261      * @cfg {String|Roo.Element} el The container element.
9262      */
9263     el : '',
9264     
9265     /**
9266      * @cfg {String|Roo.Template} tpl The template used by this View 
9267      */
9268     tpl : false,
9269     /**
9270      * @cfg {String} dataName the named area of the template to use as the data area
9271      *                          Works with domtemplates roo-name="name"
9272      */
9273     dataName: false,
9274     /**
9275      * @cfg {String} selectedClass The css class to add to selected nodes
9276      */
9277     selectedClass : "x-view-selected",
9278      /**
9279      * @cfg {String} emptyText The empty text to show when nothing is loaded.
9280      */
9281     emptyText : "",
9282     /**
9283      * @cfg {Boolean} multiSelect Allow multiple selection
9284      */
9285     multiSelect : false,
9286     /**
9287      * @cfg {Boolean} singleSelect Allow single selection
9288      */
9289     singleSelect:  false,
9290     
9291     /**
9292      * @cfg {Boolean} toggleSelect - selecting 
9293      */
9294     toggleSelect : false,
9295     
9296     /**
9297      * Returns the element this view is bound to.
9298      * @return {Roo.Element}
9299      */
9300     getEl : function(){
9301         return this.el;
9302     },
9303
9304     /**
9305      * Refreshes the view.
9306      */
9307     refresh : function(){
9308         var t = this.tpl;
9309         
9310         // if we are using something like 'domtemplate', then
9311         // the what gets used is:
9312         // t.applySubtemplate(NAME, data, wrapping data..)
9313         // the outer template then get' applied with
9314         //     the store 'extra data'
9315         // and the body get's added to the
9316         //      roo-name="data" node?
9317         //      <span class='roo-tpl-{name}'></span> ?????
9318         
9319         
9320         
9321         this.clearSelections();
9322         this.el.update("");
9323         var html = [];
9324         var records = this.store.getRange();
9325         if(records.length < 1) {
9326             
9327             // is this valid??  = should it render a template??
9328             
9329             this.el.update(this.emptyText);
9330             return;
9331         }
9332         var el = this.el;
9333         if (this.dataName) {
9334             this.el.update(t.apply(this.store.meta)); //????
9335             el = this.el.child('.roo-tpl-' + this.dataName);
9336         }
9337         
9338         for(var i = 0, len = records.length; i < len; i++){
9339             var data = this.prepareData(records[i].data, i, records[i]);
9340             this.fireEvent("preparedata", this, data, i, records[i]);
9341             html[html.length] = Roo.util.Format.trim(
9342                 this.dataName ?
9343                     t.applySubtemplate(this.dataName, data, this.store.meta) :
9344                     t.apply(data)
9345             );
9346         }
9347         
9348         
9349         
9350         el.update(html.join(""));
9351         this.nodes = el.dom.childNodes;
9352         this.updateIndexes(0);
9353     },
9354
9355     /**
9356      * Function to override to reformat the data that is sent to
9357      * the template for each node.
9358      * DEPRICATED - use the preparedata event handler.
9359      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9360      * a JSON object for an UpdateManager bound view).
9361      */
9362     prepareData : function(data, index, record)
9363     {
9364         this.fireEvent("preparedata", this, data, index, record);
9365         return data;
9366     },
9367
9368     onUpdate : function(ds, record){
9369         this.clearSelections();
9370         var index = this.store.indexOf(record);
9371         var n = this.nodes[index];
9372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9373         n.parentNode.removeChild(n);
9374         this.updateIndexes(index, index);
9375     },
9376
9377     
9378     
9379 // --------- FIXME     
9380     onAdd : function(ds, records, index)
9381     {
9382         this.clearSelections();
9383         if(this.nodes.length == 0){
9384             this.refresh();
9385             return;
9386         }
9387         var n = this.nodes[index];
9388         for(var i = 0, len = records.length; i < len; i++){
9389             var d = this.prepareData(records[i].data, i, records[i]);
9390             if(n){
9391                 this.tpl.insertBefore(n, d);
9392             }else{
9393                 
9394                 this.tpl.append(this.el, d);
9395             }
9396         }
9397         this.updateIndexes(index);
9398     },
9399
9400     onRemove : function(ds, record, index){
9401         this.clearSelections();
9402         var el = this.dataName  ?
9403             this.el.child('.roo-tpl-' + this.dataName) :
9404             this.el; 
9405         el.dom.removeChild(this.nodes[index]);
9406         this.updateIndexes(index);
9407     },
9408
9409     /**
9410      * Refresh an individual node.
9411      * @param {Number} index
9412      */
9413     refreshNode : function(index){
9414         this.onUpdate(this.store, this.store.getAt(index));
9415     },
9416
9417     updateIndexes : function(startIndex, endIndex){
9418         var ns = this.nodes;
9419         startIndex = startIndex || 0;
9420         endIndex = endIndex || ns.length - 1;
9421         for(var i = startIndex; i <= endIndex; i++){
9422             ns[i].nodeIndex = i;
9423         }
9424     },
9425
9426     /**
9427      * Changes the data store this view uses and refresh the view.
9428      * @param {Store} store
9429      */
9430     setStore : function(store, initial){
9431         if(!initial && this.store){
9432             this.store.un("datachanged", this.refresh);
9433             this.store.un("add", this.onAdd);
9434             this.store.un("remove", this.onRemove);
9435             this.store.un("update", this.onUpdate);
9436             this.store.un("clear", this.refresh);
9437         }
9438         if(store){
9439           
9440             store.on("datachanged", this.refresh, this);
9441             store.on("add", this.onAdd, this);
9442             store.on("remove", this.onRemove, this);
9443             store.on("update", this.onUpdate, this);
9444             store.on("clear", this.refresh, this);
9445         }
9446         
9447         if(store){
9448             this.refresh();
9449         }
9450     },
9451
9452     /**
9453      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9454      * @param {HTMLElement} node
9455      * @return {HTMLElement} The template node
9456      */
9457     findItemFromChild : function(node){
9458         var el = this.dataName  ?
9459             this.el.child('.roo-tpl-' + this.dataName,true) :
9460             this.el.dom; 
9461         
9462         if(!node || node.parentNode == el){
9463                     return node;
9464             }
9465             var p = node.parentNode;
9466             while(p && p != el){
9467             if(p.parentNode == el){
9468                 return p;
9469             }
9470             p = p.parentNode;
9471         }
9472             return null;
9473     },
9474
9475     /** @ignore */
9476     onClick : function(e){
9477         var item = this.findItemFromChild(e.getTarget());
9478         if(item){
9479             var index = this.indexOf(item);
9480             if(this.onItemClick(item, index, e) !== false){
9481                 this.fireEvent("click", this, index, item, e);
9482             }
9483         }else{
9484             this.clearSelections();
9485         }
9486     },
9487
9488     /** @ignore */
9489     onContextMenu : function(e){
9490         var item = this.findItemFromChild(e.getTarget());
9491         if(item){
9492             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493         }
9494     },
9495
9496     /** @ignore */
9497     onDblClick : function(e){
9498         var item = this.findItemFromChild(e.getTarget());
9499         if(item){
9500             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9501         }
9502     },
9503
9504     onItemClick : function(item, index, e)
9505     {
9506         if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507             return false;
9508         }
9509         if (this.toggleSelect) {
9510             var m = this.isSelected(item) ? 'unselect' : 'select';
9511             Roo.log(m);
9512             var _t = this;
9513             _t[m](item, true, false);
9514             return true;
9515         }
9516         if(this.multiSelect || this.singleSelect){
9517             if(this.multiSelect && e.shiftKey && this.lastSelection){
9518                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519             }else{
9520                 this.select(item, this.multiSelect && e.ctrlKey);
9521                 this.lastSelection = item;
9522             }
9523             e.preventDefault();
9524         }
9525         return true;
9526     },
9527
9528     /**
9529      * Get the number of selected nodes.
9530      * @return {Number}
9531      */
9532     getSelectionCount : function(){
9533         return this.selections.length;
9534     },
9535
9536     /**
9537      * Get the currently selected nodes.
9538      * @return {Array} An array of HTMLElements
9539      */
9540     getSelectedNodes : function(){
9541         return this.selections;
9542     },
9543
9544     /**
9545      * Get the indexes of the selected nodes.
9546      * @return {Array}
9547      */
9548     getSelectedIndexes : function(){
9549         var indexes = [], s = this.selections;
9550         for(var i = 0, len = s.length; i < len; i++){
9551             indexes.push(s[i].nodeIndex);
9552         }
9553         return indexes;
9554     },
9555
9556     /**
9557      * Clear all selections
9558      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559      */
9560     clearSelections : function(suppressEvent){
9561         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9562             this.cmp.elements = this.selections;
9563             this.cmp.removeClass(this.selectedClass);
9564             this.selections = [];
9565             if(!suppressEvent){
9566                 this.fireEvent("selectionchange", this, this.selections);
9567             }
9568         }
9569     },
9570
9571     /**
9572      * Returns true if the passed node is selected
9573      * @param {HTMLElement/Number} node The node or node index
9574      * @return {Boolean}
9575      */
9576     isSelected : function(node){
9577         var s = this.selections;
9578         if(s.length < 1){
9579             return false;
9580         }
9581         node = this.getNode(node);
9582         return s.indexOf(node) !== -1;
9583     },
9584
9585     /**
9586      * Selects nodes.
9587      * @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
9588      * @param {Boolean} keepExisting (optional) true to keep existing selections
9589      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590      */
9591     select : function(nodeInfo, keepExisting, suppressEvent){
9592         if(nodeInfo instanceof Array){
9593             if(!keepExisting){
9594                 this.clearSelections(true);
9595             }
9596             for(var i = 0, len = nodeInfo.length; i < len; i++){
9597                 this.select(nodeInfo[i], true, true);
9598             }
9599             return;
9600         } 
9601         var node = this.getNode(nodeInfo);
9602         if(!node || this.isSelected(node)){
9603             return; // already selected.
9604         }
9605         if(!keepExisting){
9606             this.clearSelections(true);
9607         }
9608         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9609             Roo.fly(node).addClass(this.selectedClass);
9610             this.selections.push(node);
9611             if(!suppressEvent){
9612                 this.fireEvent("selectionchange", this, this.selections);
9613             }
9614         }
9615         
9616         
9617     },
9618       /**
9619      * Unselects nodes.
9620      * @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
9621      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623      */
9624     unselect : function(nodeInfo, keepExisting, suppressEvent)
9625     {
9626         if(nodeInfo instanceof Array){
9627             Roo.each(this.selections, function(s) {
9628                 this.unselect(s, nodeInfo);
9629             }, this);
9630             return;
9631         }
9632         var node = this.getNode(nodeInfo);
9633         if(!node || !this.isSelected(node)){
9634             Roo.log("not selected");
9635             return; // not selected.
9636         }
9637         // fireevent???
9638         var ns = [];
9639         Roo.each(this.selections, function(s) {
9640             if (s == node ) {
9641                 Roo.fly(node).removeClass(this.selectedClass);
9642
9643                 return;
9644             }
9645             ns.push(s);
9646         },this);
9647         
9648         this.selections= ns;
9649         this.fireEvent("selectionchange", this, this.selections);
9650     },
9651
9652     /**
9653      * Gets a template node.
9654      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9655      * @return {HTMLElement} The node or null if it wasn't found
9656      */
9657     getNode : function(nodeInfo){
9658         if(typeof nodeInfo == "string"){
9659             return document.getElementById(nodeInfo);
9660         }else if(typeof nodeInfo == "number"){
9661             return this.nodes[nodeInfo];
9662         }
9663         return nodeInfo;
9664     },
9665
9666     /**
9667      * Gets a range template nodes.
9668      * @param {Number} startIndex
9669      * @param {Number} endIndex
9670      * @return {Array} An array of nodes
9671      */
9672     getNodes : function(start, end){
9673         var ns = this.nodes;
9674         start = start || 0;
9675         end = typeof end == "undefined" ? ns.length - 1 : end;
9676         var nodes = [];
9677         if(start <= end){
9678             for(var i = start; i <= end; i++){
9679                 nodes.push(ns[i]);
9680             }
9681         } else{
9682             for(var i = start; i >= end; i--){
9683                 nodes.push(ns[i]);
9684             }
9685         }
9686         return nodes;
9687     },
9688
9689     /**
9690      * Finds the index of the passed node
9691      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692      * @return {Number} The index of the node or -1
9693      */
9694     indexOf : function(node){
9695         node = this.getNode(node);
9696         if(typeof node.nodeIndex == "number"){
9697             return node.nodeIndex;
9698         }
9699         var ns = this.nodes;
9700         for(var i = 0, len = ns.length; i < len; i++){
9701             if(ns[i] == node){
9702                 return i;
9703             }
9704         }
9705         return -1;
9706     }
9707 });
9708 /*
9709  * Based on:
9710  * Ext JS Library 1.1.1
9711  * Copyright(c) 2006-2007, Ext JS, LLC.
9712  *
9713  * Originally Released Under LGPL - original licence link has changed is not relivant.
9714  *
9715  * Fork - LGPL
9716  * <script type="text/javascript">
9717  */
9718
9719 /**
9720  * @class Roo.JsonView
9721  * @extends Roo.View
9722  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
9723 <pre><code>
9724 var view = new Roo.JsonView({
9725     container: "my-element",
9726     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
9727     multiSelect: true, 
9728     jsonRoot: "data" 
9729 });
9730
9731 // listen for node click?
9732 view.on("click", function(vw, index, node, e){
9733     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9734 });
9735
9736 // direct load of JSON data
9737 view.load("foobar.php");
9738
9739 // Example from my blog list
9740 var tpl = new Roo.Template(
9741     '&lt;div class="entry"&gt;' +
9742     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
9743     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
9744     "&lt;/div&gt;&lt;hr /&gt;"
9745 );
9746
9747 var moreView = new Roo.JsonView({
9748     container :  "entry-list", 
9749     template : tpl,
9750     jsonRoot: "posts"
9751 });
9752 moreView.on("beforerender", this.sortEntries, this);
9753 moreView.load({
9754     url: "/blog/get-posts.php",
9755     params: "allposts=true",
9756     text: "Loading Blog Entries..."
9757 });
9758 </code></pre>
9759
9760 * Note: old code is supported with arguments : (container, template, config)
9761
9762
9763  * @constructor
9764  * Create a new JsonView
9765  * 
9766  * @param {Object} config The config object
9767  * 
9768  */
9769 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
9770     
9771     
9772     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
9773
9774     var um = this.el.getUpdateManager();
9775     um.setRenderer(this);
9776     um.on("update", this.onLoad, this);
9777     um.on("failure", this.onLoadException, this);
9778
9779     /**
9780      * @event beforerender
9781      * Fires before rendering of the downloaded JSON data.
9782      * @param {Roo.JsonView} this
9783      * @param {Object} data The JSON data loaded
9784      */
9785     /**
9786      * @event load
9787      * Fires when data is loaded.
9788      * @param {Roo.JsonView} this
9789      * @param {Object} data The JSON data loaded
9790      * @param {Object} response The raw Connect response object
9791      */
9792     /**
9793      * @event loadexception
9794      * Fires when loading fails.
9795      * @param {Roo.JsonView} this
9796      * @param {Object} response The raw Connect response object
9797      */
9798     this.addEvents({
9799         'beforerender' : true,
9800         'load' : true,
9801         'loadexception' : true
9802     });
9803 };
9804 Roo.extend(Roo.JsonView, Roo.View, {
9805     /**
9806      * @type {String} The root property in the loaded JSON object that contains the data
9807      */
9808     jsonRoot : "",
9809
9810     /**
9811      * Refreshes the view.
9812      */
9813     refresh : function(){
9814         this.clearSelections();
9815         this.el.update("");
9816         var html = [];
9817         var o = this.jsonData;
9818         if(o && o.length > 0){
9819             for(var i = 0, len = o.length; i < len; i++){
9820                 var data = this.prepareData(o[i], i, o);
9821                 html[html.length] = this.tpl.apply(data);
9822             }
9823         }else{
9824             html.push(this.emptyText);
9825         }
9826         this.el.update(html.join(""));
9827         this.nodes = this.el.dom.childNodes;
9828         this.updateIndexes(0);
9829     },
9830
9831     /**
9832      * 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.
9833      * @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:
9834      <pre><code>
9835      view.load({
9836          url: "your-url.php",
9837          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
9838          callback: yourFunction,
9839          scope: yourObject, //(optional scope)
9840          discardUrl: false,
9841          nocache: false,
9842          text: "Loading...",
9843          timeout: 30,
9844          scripts: false
9845      });
9846      </code></pre>
9847      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
9848      * 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.
9849      * @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}
9850      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9851      * @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.
9852      */
9853     load : function(){
9854         var um = this.el.getUpdateManager();
9855         um.update.apply(um, arguments);
9856     },
9857
9858     render : function(el, response){
9859         this.clearSelections();
9860         this.el.update("");
9861         var o;
9862         try{
9863             o = Roo.util.JSON.decode(response.responseText);
9864             if(this.jsonRoot){
9865                 
9866                 o = o[this.jsonRoot];
9867             }
9868         } catch(e){
9869         }
9870         /**
9871          * The current JSON data or null
9872          */
9873         this.jsonData = o;
9874         this.beforeRender();
9875         this.refresh();
9876     },
9877
9878 /**
9879  * Get the number of records in the current JSON dataset
9880  * @return {Number}
9881  */
9882     getCount : function(){
9883         return this.jsonData ? this.jsonData.length : 0;
9884     },
9885
9886 /**
9887  * Returns the JSON object for the specified node(s)
9888  * @param {HTMLElement/Array} node The node or an array of nodes
9889  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
9890  * you get the JSON object for the node
9891  */
9892     getNodeData : function(node){
9893         if(node instanceof Array){
9894             var data = [];
9895             for(var i = 0, len = node.length; i < len; i++){
9896                 data.push(this.getNodeData(node[i]));
9897             }
9898             return data;
9899         }
9900         return this.jsonData[this.indexOf(node)] || null;
9901     },
9902
9903     beforeRender : function(){
9904         this.snapshot = this.jsonData;
9905         if(this.sortInfo){
9906             this.sort.apply(this, this.sortInfo);
9907         }
9908         this.fireEvent("beforerender", this, this.jsonData);
9909     },
9910
9911     onLoad : function(el, o){
9912         this.fireEvent("load", this, this.jsonData, o);
9913     },
9914
9915     onLoadException : function(el, o){
9916         this.fireEvent("loadexception", this, o);
9917     },
9918
9919 /**
9920  * Filter the data by a specific property.
9921  * @param {String} property A property on your JSON objects
9922  * @param {String/RegExp} value Either string that the property values
9923  * should start with, or a RegExp to test against the property
9924  */
9925     filter : function(property, value){
9926         if(this.jsonData){
9927             var data = [];
9928             var ss = this.snapshot;
9929             if(typeof value == "string"){
9930                 var vlen = value.length;
9931                 if(vlen == 0){
9932                     this.clearFilter();
9933                     return;
9934                 }
9935                 value = value.toLowerCase();
9936                 for(var i = 0, len = ss.length; i < len; i++){
9937                     var o = ss[i];
9938                     if(o[property].substr(0, vlen).toLowerCase() == value){
9939                         data.push(o);
9940                     }
9941                 }
9942             } else if(value.exec){ // regex?
9943                 for(var i = 0, len = ss.length; i < len; i++){
9944                     var o = ss[i];
9945                     if(value.test(o[property])){
9946                         data.push(o);
9947                     }
9948                 }
9949             } else{
9950                 return;
9951             }
9952             this.jsonData = data;
9953             this.refresh();
9954         }
9955     },
9956
9957 /**
9958  * Filter by a function. The passed function will be called with each
9959  * object in the current dataset. If the function returns true the value is kept,
9960  * otherwise it is filtered.
9961  * @param {Function} fn
9962  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
9963  */
9964     filterBy : function(fn, scope){
9965         if(this.jsonData){
9966             var data = [];
9967             var ss = this.snapshot;
9968             for(var i = 0, len = ss.length; i < len; i++){
9969                 var o = ss[i];
9970                 if(fn.call(scope || this, o)){
9971                     data.push(o);
9972                 }
9973             }
9974             this.jsonData = data;
9975             this.refresh();
9976         }
9977     },
9978
9979 /**
9980  * Clears the current filter.
9981  */
9982     clearFilter : function(){
9983         if(this.snapshot && this.jsonData != this.snapshot){
9984             this.jsonData = this.snapshot;
9985             this.refresh();
9986         }
9987     },
9988
9989
9990 /**
9991  * Sorts the data for this view and refreshes it.
9992  * @param {String} property A property on your JSON objects to sort on
9993  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
9994  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
9995  */
9996     sort : function(property, dir, sortType){
9997         this.sortInfo = Array.prototype.slice.call(arguments, 0);
9998         if(this.jsonData){
9999             var p = property;
10000             var dsc = dir && dir.toLowerCase() == "desc";
10001             var f = function(o1, o2){
10002                 var v1 = sortType ? sortType(o1[p]) : o1[p];
10003                 var v2 = sortType ? sortType(o2[p]) : o2[p];
10004                 ;
10005                 if(v1 < v2){
10006                     return dsc ? +1 : -1;
10007                 } else if(v1 > v2){
10008                     return dsc ? -1 : +1;
10009                 } else{
10010                     return 0;
10011                 }
10012             };
10013             this.jsonData.sort(f);
10014             this.refresh();
10015             if(this.jsonData != this.snapshot){
10016                 this.snapshot.sort(f);
10017             }
10018         }
10019     }
10020 });/*
10021  * Based on:
10022  * Ext JS Library 1.1.1
10023  * Copyright(c) 2006-2007, Ext JS, LLC.
10024  *
10025  * Originally Released Under LGPL - original licence link has changed is not relivant.
10026  *
10027  * Fork - LGPL
10028  * <script type="text/javascript">
10029  */
10030  
10031
10032 /**
10033  * @class Roo.ColorPalette
10034  * @extends Roo.Component
10035  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
10036  * Here's an example of typical usage:
10037  * <pre><code>
10038 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
10039 cp.render('my-div');
10040
10041 cp.on('select', function(palette, selColor){
10042     // do something with selColor
10043 });
10044 </code></pre>
10045  * @constructor
10046  * Create a new ColorPalette
10047  * @param {Object} config The config object
10048  */
10049 Roo.ColorPalette = function(config){
10050     Roo.ColorPalette.superclass.constructor.call(this, config);
10051     this.addEvents({
10052         /**
10053              * @event select
10054              * Fires when a color is selected
10055              * @param {ColorPalette} this
10056              * @param {String} color The 6-digit color hex code (without the # symbol)
10057              */
10058         select: true
10059     });
10060
10061     if(this.handler){
10062         this.on("select", this.handler, this.scope, true);
10063     }
10064 };
10065 Roo.extend(Roo.ColorPalette, Roo.Component, {
10066     /**
10067      * @cfg {String} itemCls
10068      * The CSS class to apply to the containing element (defaults to "x-color-palette")
10069      */
10070     itemCls : "x-color-palette",
10071     /**
10072      * @cfg {String} value
10073      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
10074      * the hex codes are case-sensitive.
10075      */
10076     value : null,
10077     clickEvent:'click',
10078     // private
10079     ctype: "Roo.ColorPalette",
10080
10081     /**
10082      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
10083      */
10084     allowReselect : false,
10085
10086     /**
10087      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
10088      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
10089      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
10090      * of colors with the width setting until the box is symmetrical.</p>
10091      * <p>You can override individual colors if needed:</p>
10092      * <pre><code>
10093 var cp = new Roo.ColorPalette();
10094 cp.colors[0] = "FF0000";  // change the first box to red
10095 </code></pre>
10096
10097 Or you can provide a custom array of your own for complete control:
10098 <pre><code>
10099 var cp = new Roo.ColorPalette();
10100 cp.colors = ["000000", "993300", "333300"];
10101 </code></pre>
10102      * @type Array
10103      */
10104     colors : [
10105         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
10106         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
10107         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
10108         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
10109         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
10110     ],
10111
10112     // private
10113     onRender : function(container, position){
10114         var t = new Roo.MasterTemplate(
10115             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
10116         );
10117         var c = this.colors;
10118         for(var i = 0, len = c.length; i < len; i++){
10119             t.add([c[i]]);
10120         }
10121         var el = document.createElement("div");
10122         el.className = this.itemCls;
10123         t.overwrite(el);
10124         container.dom.insertBefore(el, position);
10125         this.el = Roo.get(el);
10126         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
10127         if(this.clickEvent != 'click'){
10128             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
10129         }
10130     },
10131
10132     // private
10133     afterRender : function(){
10134         Roo.ColorPalette.superclass.afterRender.call(this);
10135         if(this.value){
10136             var s = this.value;
10137             this.value = null;
10138             this.select(s);
10139         }
10140     },
10141
10142     // private
10143     handleClick : function(e, t){
10144         e.preventDefault();
10145         if(!this.disabled){
10146             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
10147             this.select(c.toUpperCase());
10148         }
10149     },
10150
10151     /**
10152      * Selects the specified color in the palette (fires the select event)
10153      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
10154      */
10155     select : function(color){
10156         color = color.replace("#", "");
10157         if(color != this.value || this.allowReselect){
10158             var el = this.el;
10159             if(this.value){
10160                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
10161             }
10162             el.child("a.color-"+color).addClass("x-color-palette-sel");
10163             this.value = color;
10164             this.fireEvent("select", this, color);
10165         }
10166     }
10167 });/*
10168  * Based on:
10169  * Ext JS Library 1.1.1
10170  * Copyright(c) 2006-2007, Ext JS, LLC.
10171  *
10172  * Originally Released Under LGPL - original licence link has changed is not relivant.
10173  *
10174  * Fork - LGPL
10175  * <script type="text/javascript">
10176  */
10177  
10178 /**
10179  * @class Roo.DatePicker
10180  * @extends Roo.Component
10181  * Simple date picker class.
10182  * @constructor
10183  * Create a new DatePicker
10184  * @param {Object} config The config object
10185  */
10186 Roo.DatePicker = function(config){
10187     Roo.DatePicker.superclass.constructor.call(this, config);
10188
10189     this.value = config && config.value ?
10190                  config.value.clearTime() : new Date().clearTime();
10191
10192     this.addEvents({
10193         /**
10194              * @event select
10195              * Fires when a date is selected
10196              * @param {DatePicker} this
10197              * @param {Date} date The selected date
10198              */
10199         'select': true,
10200         /**
10201              * @event monthchange
10202              * Fires when the displayed month changes 
10203              * @param {DatePicker} this
10204              * @param {Date} date The selected month
10205              */
10206         'monthchange': true
10207     });
10208
10209     if(this.handler){
10210         this.on("select", this.handler,  this.scope || this);
10211     }
10212     // build the disabledDatesRE
10213     if(!this.disabledDatesRE && this.disabledDates){
10214         var dd = this.disabledDates;
10215         var re = "(?:";
10216         for(var i = 0; i < dd.length; i++){
10217             re += dd[i];
10218             if(i != dd.length-1) re += "|";
10219         }
10220         this.disabledDatesRE = new RegExp(re + ")");
10221     }
10222 };
10223
10224 Roo.extend(Roo.DatePicker, Roo.Component, {
10225     /**
10226      * @cfg {String} todayText
10227      * The text to display on the button that selects the current date (defaults to "Today")
10228      */
10229     todayText : "Today",
10230     /**
10231      * @cfg {String} okText
10232      * The text to display on the ok button
10233      */
10234     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
10235     /**
10236      * @cfg {String} cancelText
10237      * The text to display on the cancel button
10238      */
10239     cancelText : "Cancel",
10240     /**
10241      * @cfg {String} todayTip
10242      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
10243      */
10244     todayTip : "{0} (Spacebar)",
10245     /**
10246      * @cfg {Date} minDate
10247      * Minimum allowable date (JavaScript date object, defaults to null)
10248      */
10249     minDate : null,
10250     /**
10251      * @cfg {Date} maxDate
10252      * Maximum allowable date (JavaScript date object, defaults to null)
10253      */
10254     maxDate : null,
10255     /**
10256      * @cfg {String} minText
10257      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
10258      */
10259     minText : "This date is before the minimum date",
10260     /**
10261      * @cfg {String} maxText
10262      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
10263      */
10264     maxText : "This date is after the maximum date",
10265     /**
10266      * @cfg {String} format
10267      * The default date format string which can be overriden for localization support.  The format must be
10268      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10269      */
10270     format : "m/d/y",
10271     /**
10272      * @cfg {Array} disabledDays
10273      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
10274      */
10275     disabledDays : null,
10276     /**
10277      * @cfg {String} disabledDaysText
10278      * The tooltip to display when the date falls on a disabled day (defaults to "")
10279      */
10280     disabledDaysText : "",
10281     /**
10282      * @cfg {RegExp} disabledDatesRE
10283      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
10284      */
10285     disabledDatesRE : null,
10286     /**
10287      * @cfg {String} disabledDatesText
10288      * The tooltip text to display when the date falls on a disabled date (defaults to "")
10289      */
10290     disabledDatesText : "",
10291     /**
10292      * @cfg {Boolean} constrainToViewport
10293      * True to constrain the date picker to the viewport (defaults to true)
10294      */
10295     constrainToViewport : true,
10296     /**
10297      * @cfg {Array} monthNames
10298      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
10299      */
10300     monthNames : Date.monthNames,
10301     /**
10302      * @cfg {Array} dayNames
10303      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
10304      */
10305     dayNames : Date.dayNames,
10306     /**
10307      * @cfg {String} nextText
10308      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
10309      */
10310     nextText: 'Next Month (Control+Right)',
10311     /**
10312      * @cfg {String} prevText
10313      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
10314      */
10315     prevText: 'Previous Month (Control+Left)',
10316     /**
10317      * @cfg {String} monthYearText
10318      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
10319      */
10320     monthYearText: 'Choose a month (Control+Up/Down to move years)',
10321     /**
10322      * @cfg {Number} startDay
10323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10324      */
10325     startDay : 0,
10326     /**
10327      * @cfg {Bool} showClear
10328      * Show a clear button (usefull for date form elements that can be blank.)
10329      */
10330     
10331     showClear: false,
10332     
10333     /**
10334      * Sets the value of the date field
10335      * @param {Date} value The date to set
10336      */
10337     setValue : function(value){
10338         var old = this.value;
10339         if (typeof(value) == 'string') {
10340             value = Date.parseDate(value, this.format);
10341         }
10342         
10343         this.value = value.clearTime(true);
10344         if(this.el){
10345             this.update(this.value);
10346         }
10347     },
10348
10349     /**
10350      * Gets the current selected value of the date field
10351      * @return {Date} The selected date
10352      */
10353     getValue : function(){
10354         return this.value;
10355     },
10356
10357     // private
10358     focus : function(){
10359         if(this.el){
10360             this.update(this.activeDate);
10361         }
10362     },
10363
10364     // private
10365     onRender : function(container, position){
10366         
10367         var m = [
10368              '<table cellspacing="0">',
10369                 '<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>',
10370                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
10371         var dn = this.dayNames;
10372         for(var i = 0; i < 7; i++){
10373             var d = this.startDay+i;
10374             if(d > 6){
10375                 d = d-7;
10376             }
10377             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
10378         }
10379         m[m.length] = "</tr></thead><tbody><tr>";
10380         for(var i = 0; i < 42; i++) {
10381             if(i % 7 == 0 && i != 0){
10382                 m[m.length] = "</tr><tr>";
10383             }
10384             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
10385         }
10386         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
10387             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
10388
10389         var el = document.createElement("div");
10390         el.className = "x-date-picker";
10391         el.innerHTML = m.join("");
10392
10393         container.dom.insertBefore(el, position);
10394
10395         this.el = Roo.get(el);
10396         this.eventEl = Roo.get(el.firstChild);
10397
10398         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
10399             handler: this.showPrevMonth,
10400             scope: this,
10401             preventDefault:true,
10402             stopDefault:true
10403         });
10404
10405         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
10406             handler: this.showNextMonth,
10407             scope: this,
10408             preventDefault:true,
10409             stopDefault:true
10410         });
10411
10412         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
10413
10414         this.monthPicker = this.el.down('div.x-date-mp');
10415         this.monthPicker.enableDisplayMode('block');
10416         
10417         var kn = new Roo.KeyNav(this.eventEl, {
10418             "left" : function(e){
10419                 e.ctrlKey ?
10420                     this.showPrevMonth() :
10421                     this.update(this.activeDate.add("d", -1));
10422             },
10423
10424             "right" : function(e){
10425                 e.ctrlKey ?
10426                     this.showNextMonth() :
10427                     this.update(this.activeDate.add("d", 1));
10428             },
10429
10430             "up" : function(e){
10431                 e.ctrlKey ?
10432                     this.showNextYear() :
10433                     this.update(this.activeDate.add("d", -7));
10434             },
10435
10436             "down" : function(e){
10437                 e.ctrlKey ?
10438                     this.showPrevYear() :
10439                     this.update(this.activeDate.add("d", 7));
10440             },
10441
10442             "pageUp" : function(e){
10443                 this.showNextMonth();
10444             },
10445
10446             "pageDown" : function(e){
10447                 this.showPrevMonth();
10448             },
10449
10450             "enter" : function(e){
10451                 e.stopPropagation();
10452                 return true;
10453             },
10454
10455             scope : this
10456         });
10457
10458         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
10459
10460         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
10461
10462         this.el.unselectable();
10463         
10464         this.cells = this.el.select("table.x-date-inner tbody td");
10465         this.textNodes = this.el.query("table.x-date-inner tbody span");
10466
10467         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
10468             text: "&#160;",
10469             tooltip: this.monthYearText
10470         });
10471
10472         this.mbtn.on('click', this.showMonthPicker, this);
10473         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
10474
10475
10476         var today = (new Date()).dateFormat(this.format);
10477         
10478         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
10479         if (this.showClear) {
10480             baseTb.add( new Roo.Toolbar.Fill());
10481         }
10482         baseTb.add({
10483             text: String.format(this.todayText, today),
10484             tooltip: String.format(this.todayTip, today),
10485             handler: this.selectToday,
10486             scope: this
10487         });
10488         
10489         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
10490             
10491         //});
10492         if (this.showClear) {
10493             
10494             baseTb.add( new Roo.Toolbar.Fill());
10495             baseTb.add({
10496                 text: '&#160;',
10497                 cls: 'x-btn-icon x-btn-clear',
10498                 handler: function() {
10499                     //this.value = '';
10500                     this.fireEvent("select", this, '');
10501                 },
10502                 scope: this
10503             });
10504         }
10505         
10506         
10507         if(Roo.isIE){
10508             this.el.repaint();
10509         }
10510         this.update(this.value);
10511     },
10512
10513     createMonthPicker : function(){
10514         if(!this.monthPicker.dom.firstChild){
10515             var buf = ['<table border="0" cellspacing="0">'];
10516             for(var i = 0; i < 6; i++){
10517                 buf.push(
10518                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
10519                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
10520                     i == 0 ?
10521                     '<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>' :
10522                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
10523                 );
10524             }
10525             buf.push(
10526                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
10527                     this.okText,
10528                     '</button><button type="button" class="x-date-mp-cancel">',
10529                     this.cancelText,
10530                     '</button></td></tr>',
10531                 '</table>'
10532             );
10533             this.monthPicker.update(buf.join(''));
10534             this.monthPicker.on('click', this.onMonthClick, this);
10535             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
10536
10537             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
10538             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
10539
10540             this.mpMonths.each(function(m, a, i){
10541                 i += 1;
10542                 if((i%2) == 0){
10543                     m.dom.xmonth = 5 + Math.round(i * .5);
10544                 }else{
10545                     m.dom.xmonth = Math.round((i-1) * .5);
10546                 }
10547             });
10548         }
10549     },
10550
10551     showMonthPicker : function(){
10552         this.createMonthPicker();
10553         var size = this.el.getSize();
10554         this.monthPicker.setSize(size);
10555         this.monthPicker.child('table').setSize(size);
10556
10557         this.mpSelMonth = (this.activeDate || this.value).getMonth();
10558         this.updateMPMonth(this.mpSelMonth);
10559         this.mpSelYear = (this.activeDate || this.value).getFullYear();
10560         this.updateMPYear(this.mpSelYear);
10561
10562         this.monthPicker.slideIn('t', {duration:.2});
10563     },
10564
10565     updateMPYear : function(y){
10566         this.mpyear = y;
10567         var ys = this.mpYears.elements;
10568         for(var i = 1; i <= 10; i++){
10569             var td = ys[i-1], y2;
10570             if((i%2) == 0){
10571                 y2 = y + Math.round(i * .5);
10572                 td.firstChild.innerHTML = y2;
10573                 td.xyear = y2;
10574             }else{
10575                 y2 = y - (5-Math.round(i * .5));
10576                 td.firstChild.innerHTML = y2;
10577                 td.xyear = y2;
10578             }
10579             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
10580         }
10581     },
10582
10583     updateMPMonth : function(sm){
10584         this.mpMonths.each(function(m, a, i){
10585             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
10586         });
10587     },
10588
10589     selectMPMonth: function(m){
10590         
10591     },
10592
10593     onMonthClick : function(e, t){
10594         e.stopEvent();
10595         var el = new Roo.Element(t), pn;
10596         if(el.is('button.x-date-mp-cancel')){
10597             this.hideMonthPicker();
10598         }
10599         else if(el.is('button.x-date-mp-ok')){
10600             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10601             this.hideMonthPicker();
10602         }
10603         else if(pn = el.up('td.x-date-mp-month', 2)){
10604             this.mpMonths.removeClass('x-date-mp-sel');
10605             pn.addClass('x-date-mp-sel');
10606             this.mpSelMonth = pn.dom.xmonth;
10607         }
10608         else if(pn = el.up('td.x-date-mp-year', 2)){
10609             this.mpYears.removeClass('x-date-mp-sel');
10610             pn.addClass('x-date-mp-sel');
10611             this.mpSelYear = pn.dom.xyear;
10612         }
10613         else if(el.is('a.x-date-mp-prev')){
10614             this.updateMPYear(this.mpyear-10);
10615         }
10616         else if(el.is('a.x-date-mp-next')){
10617             this.updateMPYear(this.mpyear+10);
10618         }
10619     },
10620
10621     onMonthDblClick : function(e, t){
10622         e.stopEvent();
10623         var el = new Roo.Element(t), pn;
10624         if(pn = el.up('td.x-date-mp-month', 2)){
10625             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
10626             this.hideMonthPicker();
10627         }
10628         else if(pn = el.up('td.x-date-mp-year', 2)){
10629             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
10630             this.hideMonthPicker();
10631         }
10632     },
10633
10634     hideMonthPicker : function(disableAnim){
10635         if(this.monthPicker){
10636             if(disableAnim === true){
10637                 this.monthPicker.hide();
10638             }else{
10639                 this.monthPicker.slideOut('t', {duration:.2});
10640             }
10641         }
10642     },
10643
10644     // private
10645     showPrevMonth : function(e){
10646         this.update(this.activeDate.add("mo", -1));
10647     },
10648
10649     // private
10650     showNextMonth : function(e){
10651         this.update(this.activeDate.add("mo", 1));
10652     },
10653
10654     // private
10655     showPrevYear : function(){
10656         this.update(this.activeDate.add("y", -1));
10657     },
10658
10659     // private
10660     showNextYear : function(){
10661         this.update(this.activeDate.add("y", 1));
10662     },
10663
10664     // private
10665     handleMouseWheel : function(e){
10666         var delta = e.getWheelDelta();
10667         if(delta > 0){
10668             this.showPrevMonth();
10669             e.stopEvent();
10670         } else if(delta < 0){
10671             this.showNextMonth();
10672             e.stopEvent();
10673         }
10674     },
10675
10676     // private
10677     handleDateClick : function(e, t){
10678         e.stopEvent();
10679         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
10680             this.setValue(new Date(t.dateValue));
10681             this.fireEvent("select", this, this.value);
10682         }
10683     },
10684
10685     // private
10686     selectToday : function(){
10687         this.setValue(new Date().clearTime());
10688         this.fireEvent("select", this, this.value);
10689     },
10690
10691     // private
10692     update : function(date)
10693     {
10694         var vd = this.activeDate;
10695         this.activeDate = date;
10696         if(vd && this.el){
10697             var t = date.getTime();
10698             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10699                 this.cells.removeClass("x-date-selected");
10700                 this.cells.each(function(c){
10701                    if(c.dom.firstChild.dateValue == t){
10702                        c.addClass("x-date-selected");
10703                        setTimeout(function(){
10704                             try{c.dom.firstChild.focus();}catch(e){}
10705                        }, 50);
10706                        return false;
10707                    }
10708                 });
10709                 return;
10710             }
10711         }
10712         
10713         var days = date.getDaysInMonth();
10714         var firstOfMonth = date.getFirstDateOfMonth();
10715         var startingPos = firstOfMonth.getDay()-this.startDay;
10716
10717         if(startingPos <= this.startDay){
10718             startingPos += 7;
10719         }
10720
10721         var pm = date.add("mo", -1);
10722         var prevStart = pm.getDaysInMonth()-startingPos;
10723
10724         var cells = this.cells.elements;
10725         var textEls = this.textNodes;
10726         days += startingPos;
10727
10728         // convert everything to numbers so it's fast
10729         var day = 86400000;
10730         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10731         var today = new Date().clearTime().getTime();
10732         var sel = date.clearTime().getTime();
10733         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10734         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10735         var ddMatch = this.disabledDatesRE;
10736         var ddText = this.disabledDatesText;
10737         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10738         var ddaysText = this.disabledDaysText;
10739         var format = this.format;
10740
10741         var setCellClass = function(cal, cell){
10742             cell.title = "";
10743             var t = d.getTime();
10744             cell.firstChild.dateValue = t;
10745             if(t == today){
10746                 cell.className += " x-date-today";
10747                 cell.title = cal.todayText;
10748             }
10749             if(t == sel){
10750                 cell.className += " x-date-selected";
10751                 setTimeout(function(){
10752                     try{cell.firstChild.focus();}catch(e){}
10753                 }, 50);
10754             }
10755             // disabling
10756             if(t < min) {
10757                 cell.className = " x-date-disabled";
10758                 cell.title = cal.minText;
10759                 return;
10760             }
10761             if(t > max) {
10762                 cell.className = " x-date-disabled";
10763                 cell.title = cal.maxText;
10764                 return;
10765             }
10766             if(ddays){
10767                 if(ddays.indexOf(d.getDay()) != -1){
10768                     cell.title = ddaysText;
10769                     cell.className = " x-date-disabled";
10770                 }
10771             }
10772             if(ddMatch && format){
10773                 var fvalue = d.dateFormat(format);
10774                 if(ddMatch.test(fvalue)){
10775                     cell.title = ddText.replace("%0", fvalue);
10776                     cell.className = " x-date-disabled";
10777                 }
10778             }
10779         };
10780
10781         var i = 0;
10782         for(; i < startingPos; i++) {
10783             textEls[i].innerHTML = (++prevStart);
10784             d.setDate(d.getDate()+1);
10785             cells[i].className = "x-date-prevday";
10786             setCellClass(this, cells[i]);
10787         }
10788         for(; i < days; i++){
10789             intDay = i - startingPos + 1;
10790             textEls[i].innerHTML = (intDay);
10791             d.setDate(d.getDate()+1);
10792             cells[i].className = "x-date-active";
10793             setCellClass(this, cells[i]);
10794         }
10795         var extraDays = 0;
10796         for(; i < 42; i++) {
10797              textEls[i].innerHTML = (++extraDays);
10798              d.setDate(d.getDate()+1);
10799              cells[i].className = "x-date-nextday";
10800              setCellClass(this, cells[i]);
10801         }
10802
10803         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
10804         this.fireEvent('monthchange', this, date);
10805         
10806         if(!this.internalRender){
10807             var main = this.el.dom.firstChild;
10808             var w = main.offsetWidth;
10809             this.el.setWidth(w + this.el.getBorderWidth("lr"));
10810             Roo.fly(main).setWidth(w);
10811             this.internalRender = true;
10812             // opera does not respect the auto grow header center column
10813             // then, after it gets a width opera refuses to recalculate
10814             // without a second pass
10815             if(Roo.isOpera && !this.secondPass){
10816                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10817                 this.secondPass = true;
10818                 this.update.defer(10, this, [date]);
10819             }
10820         }
10821         
10822         
10823     }
10824 });        /*
10825  * Based on:
10826  * Ext JS Library 1.1.1
10827  * Copyright(c) 2006-2007, Ext JS, LLC.
10828  *
10829  * Originally Released Under LGPL - original licence link has changed is not relivant.
10830  *
10831  * Fork - LGPL
10832  * <script type="text/javascript">
10833  */
10834 /**
10835  * @class Roo.TabPanel
10836  * @extends Roo.util.Observable
10837  * A lightweight tab container.
10838  * <br><br>
10839  * Usage:
10840  * <pre><code>
10841 // basic tabs 1, built from existing content
10842 var tabs = new Roo.TabPanel("tabs1");
10843 tabs.addTab("script", "View Script");
10844 tabs.addTab("markup", "View Markup");
10845 tabs.activate("script");
10846
10847 // more advanced tabs, built from javascript
10848 var jtabs = new Roo.TabPanel("jtabs");
10849 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
10850
10851 // set up the UpdateManager
10852 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
10853 var updater = tab2.getUpdateManager();
10854 updater.setDefaultUrl("ajax1.htm");
10855 tab2.on('activate', updater.refresh, updater, true);
10856
10857 // Use setUrl for Ajax loading
10858 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
10859 tab3.setUrl("ajax2.htm", null, true);
10860
10861 // Disabled tab
10862 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
10863 tab4.disable();
10864
10865 jtabs.activate("jtabs-1");
10866  * </code></pre>
10867  * @constructor
10868  * Create a new TabPanel.
10869  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
10870  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
10871  */
10872 Roo.TabPanel = function(container, config){
10873     /**
10874     * The container element for this TabPanel.
10875     * @type Roo.Element
10876     */
10877     this.el = Roo.get(container, true);
10878     if(config){
10879         if(typeof config == "boolean"){
10880             this.tabPosition = config ? "bottom" : "top";
10881         }else{
10882             Roo.apply(this, config);
10883         }
10884     }
10885     if(this.tabPosition == "bottom"){
10886         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10887         this.el.addClass("x-tabs-bottom");
10888     }
10889     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
10890     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
10891     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
10892     if(Roo.isIE){
10893         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
10894     }
10895     if(this.tabPosition != "bottom"){
10896         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
10897          * @type Roo.Element
10898          */
10899         this.bodyEl = Roo.get(this.createBody(this.el.dom));
10900         this.el.addClass("x-tabs-top");
10901     }
10902     this.items = [];
10903
10904     this.bodyEl.setStyle("position", "relative");
10905
10906     this.active = null;
10907     this.activateDelegate = this.activate.createDelegate(this);
10908
10909     this.addEvents({
10910         /**
10911          * @event tabchange
10912          * Fires when the active tab changes
10913          * @param {Roo.TabPanel} this
10914          * @param {Roo.TabPanelItem} activePanel The new active tab
10915          */
10916         "tabchange": true,
10917         /**
10918          * @event beforetabchange
10919          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
10920          * @param {Roo.TabPanel} this
10921          * @param {Object} e Set cancel to true on this object to cancel the tab change
10922          * @param {Roo.TabPanelItem} tab The tab being changed to
10923          */
10924         "beforetabchange" : true
10925     });
10926
10927     Roo.EventManager.onWindowResize(this.onResize, this);
10928     this.cpad = this.el.getPadding("lr");
10929     this.hiddenCount = 0;
10930
10931
10932     // toolbar on the tabbar support...
10933     if (this.toolbar) {
10934         var tcfg = this.toolbar;
10935         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
10936         this.toolbar = new Roo.Toolbar(tcfg);
10937         if (Roo.isSafari) {
10938             var tbl = tcfg.container.child('table', true);
10939             tbl.setAttribute('width', '100%');
10940         }
10941         
10942     }
10943    
10944
10945
10946     Roo.TabPanel.superclass.constructor.call(this);
10947 };
10948
10949 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
10950     /*
10951      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
10952      */
10953     tabPosition : "top",
10954     /*
10955      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
10956      */
10957     currentTabWidth : 0,
10958     /*
10959      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
10960      */
10961     minTabWidth : 40,
10962     /*
10963      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
10964      */
10965     maxTabWidth : 250,
10966     /*
10967      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
10968      */
10969     preferredTabWidth : 175,
10970     /*
10971      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
10972      */
10973     resizeTabs : false,
10974     /*
10975      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
10976      */
10977     monitorResize : true,
10978     /*
10979      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
10980      */
10981     toolbar : false,
10982
10983     /**
10984      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
10985      * @param {String} id The id of the div to use <b>or create</b>
10986      * @param {String} text The text for the tab
10987      * @param {String} content (optional) Content to put in the TabPanelItem body
10988      * @param {Boolean} closable (optional) True to create a close icon on the tab
10989      * @return {Roo.TabPanelItem} The created TabPanelItem
10990      */
10991     addTab : function(id, text, content, closable){
10992         var item = new Roo.TabPanelItem(this, id, text, closable);
10993         this.addTabItem(item);
10994         if(content){
10995             item.setContent(content);
10996         }
10997         return item;
10998     },
10999
11000     /**
11001      * Returns the {@link Roo.TabPanelItem} with the specified id/index
11002      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
11003      * @return {Roo.TabPanelItem}
11004      */
11005     getTab : function(id){
11006         return this.items[id];
11007     },
11008
11009     /**
11010      * Hides the {@link Roo.TabPanelItem} with the specified id/index
11011      * @param {String/Number} id The id or index of the TabPanelItem to hide.
11012      */
11013     hideTab : function(id){
11014         var t = this.items[id];
11015         if(!t.isHidden()){
11016            t.setHidden(true);
11017            this.hiddenCount++;
11018            this.autoSizeTabs();
11019         }
11020     },
11021
11022     /**
11023      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
11024      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
11025      */
11026     unhideTab : function(id){
11027         var t = this.items[id];
11028         if(t.isHidden()){
11029            t.setHidden(false);
11030            this.hiddenCount--;
11031            this.autoSizeTabs();
11032         }
11033     },
11034
11035     /**
11036      * Adds an existing {@link Roo.TabPanelItem}.
11037      * @param {Roo.TabPanelItem} item The TabPanelItem to add
11038      */
11039     addTabItem : function(item){
11040         this.items[item.id] = item;
11041         this.items.push(item);
11042         if(this.resizeTabs){
11043            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
11044            this.autoSizeTabs();
11045         }else{
11046             item.autoSize();
11047         }
11048     },
11049
11050     /**
11051      * Removes a {@link Roo.TabPanelItem}.
11052      * @param {String/Number} id The id or index of the TabPanelItem to remove.
11053      */
11054     removeTab : function(id){
11055         var items = this.items;
11056         var tab = items[id];
11057         if(!tab) { return; }
11058         var index = items.indexOf(tab);
11059         if(this.active == tab && items.length > 1){
11060             var newTab = this.getNextAvailable(index);
11061             if(newTab) {
11062                 newTab.activate();
11063             }
11064         }
11065         this.stripEl.dom.removeChild(tab.pnode.dom);
11066         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
11067             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
11068         }
11069         items.splice(index, 1);
11070         delete this.items[tab.id];
11071         tab.fireEvent("close", tab);
11072         tab.purgeListeners();
11073         this.autoSizeTabs();
11074     },
11075
11076     getNextAvailable : function(start){
11077         var items = this.items;
11078         var index = start;
11079         // look for a next tab that will slide over to
11080         // replace the one being removed
11081         while(index < items.length){
11082             var item = items[++index];
11083             if(item && !item.isHidden()){
11084                 return item;
11085             }
11086         }
11087         // if one isn't found select the previous tab (on the left)
11088         index = start;
11089         while(index >= 0){
11090             var item = items[--index];
11091             if(item && !item.isHidden()){
11092                 return item;
11093             }
11094         }
11095         return null;
11096     },
11097
11098     /**
11099      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
11100      * @param {String/Number} id The id or index of the TabPanelItem to disable.
11101      */
11102     disableTab : function(id){
11103         var tab = this.items[id];
11104         if(tab && this.active != tab){
11105             tab.disable();
11106         }
11107     },
11108
11109     /**
11110      * Enables a {@link Roo.TabPanelItem} that is disabled.
11111      * @param {String/Number} id The id or index of the TabPanelItem to enable.
11112      */
11113     enableTab : function(id){
11114         var tab = this.items[id];
11115         tab.enable();
11116     },
11117
11118     /**
11119      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
11120      * @param {String/Number} id The id or index of the TabPanelItem to activate.
11121      * @return {Roo.TabPanelItem} The TabPanelItem.
11122      */
11123     activate : function(id){
11124         var tab = this.items[id];
11125         if(!tab){
11126             return null;
11127         }
11128         if(tab == this.active || tab.disabled){
11129             return tab;
11130         }
11131         var e = {};
11132         this.fireEvent("beforetabchange", this, e, tab);
11133         if(e.cancel !== true && !tab.disabled){
11134             if(this.active){
11135                 this.active.hide();
11136             }
11137             this.active = this.items[id];
11138             this.active.show();
11139             this.fireEvent("tabchange", this, this.active);
11140         }
11141         return tab;
11142     },
11143
11144     /**
11145      * Gets the active {@link Roo.TabPanelItem}.
11146      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
11147      */
11148     getActiveTab : function(){
11149         return this.active;
11150     },
11151
11152     /**
11153      * Updates the tab body element to fit the height of the container element
11154      * for overflow scrolling
11155      * @param {Number} targetHeight (optional) Override the starting height from the elements height
11156      */
11157     syncHeight : function(targetHeight){
11158         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
11159         var bm = this.bodyEl.getMargins();
11160         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
11161         this.bodyEl.setHeight(newHeight);
11162         return newHeight;
11163     },
11164
11165     onResize : function(){
11166         if(this.monitorResize){
11167             this.autoSizeTabs();
11168         }
11169     },
11170
11171     /**
11172      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
11173      */
11174     beginUpdate : function(){
11175         this.updating = true;
11176     },
11177
11178     /**
11179      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
11180      */
11181     endUpdate : function(){
11182         this.updating = false;
11183         this.autoSizeTabs();
11184     },
11185
11186     /**
11187      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
11188      */
11189     autoSizeTabs : function(){
11190         var count = this.items.length;
11191         var vcount = count - this.hiddenCount;
11192         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
11193         var w = Math.max(this.el.getWidth() - this.cpad, 10);
11194         var availWidth = Math.floor(w / vcount);
11195         var b = this.stripBody;
11196         if(b.getWidth() > w){
11197             var tabs = this.items;
11198             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
11199             if(availWidth < this.minTabWidth){
11200                 /*if(!this.sleft){    // incomplete scrolling code
11201                     this.createScrollButtons();
11202                 }
11203                 this.showScroll();
11204                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
11205             }
11206         }else{
11207             if(this.currentTabWidth < this.preferredTabWidth){
11208                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
11209             }
11210         }
11211     },
11212
11213     /**
11214      * Returns the number of tabs in this TabPanel.
11215      * @return {Number}
11216      */
11217      getCount : function(){
11218          return this.items.length;
11219      },
11220
11221     /**
11222      * Resizes all the tabs to the passed width
11223      * @param {Number} The new width
11224      */
11225     setTabWidth : function(width){
11226         this.currentTabWidth = width;
11227         for(var i = 0, len = this.items.length; i < len; i++) {
11228                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
11229         }
11230     },
11231
11232     /**
11233      * Destroys this TabPanel
11234      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
11235      */
11236     destroy : function(removeEl){
11237         Roo.EventManager.removeResizeListener(this.onResize, this);
11238         for(var i = 0, len = this.items.length; i < len; i++){
11239             this.items[i].purgeListeners();
11240         }
11241         if(removeEl === true){
11242             this.el.update("");
11243             this.el.remove();
11244         }
11245     }
11246 });
11247
11248 /**
11249  * @class Roo.TabPanelItem
11250  * @extends Roo.util.Observable
11251  * Represents an individual item (tab plus body) in a TabPanel.
11252  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
11253  * @param {String} id The id of this TabPanelItem
11254  * @param {String} text The text for the tab of this TabPanelItem
11255  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
11256  */
11257 Roo.TabPanelItem = function(tabPanel, id, text, closable){
11258     /**
11259      * The {@link Roo.TabPanel} this TabPanelItem belongs to
11260      * @type Roo.TabPanel
11261      */
11262     this.tabPanel = tabPanel;
11263     /**
11264      * The id for this TabPanelItem
11265      * @type String
11266      */
11267     this.id = id;
11268     /** @private */
11269     this.disabled = false;
11270     /** @private */
11271     this.text = text;
11272     /** @private */
11273     this.loaded = false;
11274     this.closable = closable;
11275
11276     /**
11277      * The body element for this TabPanelItem.
11278      * @type Roo.Element
11279      */
11280     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
11281     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
11282     this.bodyEl.setStyle("display", "block");
11283     this.bodyEl.setStyle("zoom", "1");
11284     this.hideAction();
11285
11286     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
11287     /** @private */
11288     this.el = Roo.get(els.el, true);
11289     this.inner = Roo.get(els.inner, true);
11290     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
11291     this.pnode = Roo.get(els.el.parentNode, true);
11292     this.el.on("mousedown", this.onTabMouseDown, this);
11293     this.el.on("click", this.onTabClick, this);
11294     /** @private */
11295     if(closable){
11296         var c = Roo.get(els.close, true);
11297         c.dom.title = this.closeText;
11298         c.addClassOnOver("close-over");
11299         c.on("click", this.closeClick, this);
11300      }
11301
11302     this.addEvents({
11303          /**
11304          * @event activate
11305          * Fires when this tab becomes the active tab.
11306          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11307          * @param {Roo.TabPanelItem} this
11308          */
11309         "activate": true,
11310         /**
11311          * @event beforeclose
11312          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
11313          * @param {Roo.TabPanelItem} this
11314          * @param {Object} e Set cancel to true on this object to cancel the close.
11315          */
11316         "beforeclose": true,
11317         /**
11318          * @event close
11319          * Fires when this tab is closed.
11320          * @param {Roo.TabPanelItem} this
11321          */
11322          "close": true,
11323         /**
11324          * @event deactivate
11325          * Fires when this tab is no longer the active tab.
11326          * @param {Roo.TabPanel} tabPanel The parent TabPanel
11327          * @param {Roo.TabPanelItem} this
11328          */
11329          "deactivate" : true
11330     });
11331     this.hidden = false;
11332
11333     Roo.TabPanelItem.superclass.constructor.call(this);
11334 };
11335
11336 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
11337     purgeListeners : function(){
11338        Roo.util.Observable.prototype.purgeListeners.call(this);
11339        this.el.removeAllListeners();
11340     },
11341     /**
11342      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
11343      */
11344     show : function(){
11345         this.pnode.addClass("on");
11346         this.showAction();
11347         if(Roo.isOpera){
11348             this.tabPanel.stripWrap.repaint();
11349         }
11350         this.fireEvent("activate", this.tabPanel, this);
11351     },
11352
11353     /**
11354      * Returns true if this tab is the active tab.
11355      * @return {Boolean}
11356      */
11357     isActive : function(){
11358         return this.tabPanel.getActiveTab() == this;
11359     },
11360
11361     /**
11362      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
11363      */
11364     hide : function(){
11365         this.pnode.removeClass("on");
11366         this.hideAction();
11367         this.fireEvent("deactivate", this.tabPanel, this);
11368     },
11369
11370     hideAction : function(){
11371         this.bodyEl.hide();
11372         this.bodyEl.setStyle("position", "absolute");
11373         this.bodyEl.setLeft("-20000px");
11374         this.bodyEl.setTop("-20000px");
11375     },
11376
11377     showAction : function(){
11378         this.bodyEl.setStyle("position", "relative");
11379         this.bodyEl.setTop("");
11380         this.bodyEl.setLeft("");
11381         this.bodyEl.show();
11382     },
11383
11384     /**
11385      * Set the tooltip for the tab.
11386      * @param {String} tooltip The tab's tooltip
11387      */
11388     setTooltip : function(text){
11389         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
11390             this.textEl.dom.qtip = text;
11391             this.textEl.dom.removeAttribute('title');
11392         }else{
11393             this.textEl.dom.title = text;
11394         }
11395     },
11396
11397     onTabClick : function(e){
11398         e.preventDefault();
11399         this.tabPanel.activate(this.id);
11400     },
11401
11402     onTabMouseDown : function(e){
11403         e.preventDefault();
11404         this.tabPanel.activate(this.id);
11405     },
11406
11407     getWidth : function(){
11408         return this.inner.getWidth();
11409     },
11410
11411     setWidth : function(width){
11412         var iwidth = width - this.pnode.getPadding("lr");
11413         this.inner.setWidth(iwidth);
11414         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
11415         this.pnode.setWidth(width);
11416     },
11417
11418     /**
11419      * Show or hide the tab
11420      * @param {Boolean} hidden True to hide or false to show.
11421      */
11422     setHidden : function(hidden){
11423         this.hidden = hidden;
11424         this.pnode.setStyle("display", hidden ? "none" : "");
11425     },
11426
11427     /**
11428      * Returns true if this tab is "hidden"
11429      * @return {Boolean}
11430      */
11431     isHidden : function(){
11432         return this.hidden;
11433     },
11434
11435     /**
11436      * Returns the text for this tab
11437      * @return {String}
11438      */
11439     getText : function(){
11440         return this.text;
11441     },
11442
11443     autoSize : function(){
11444         //this.el.beginMeasure();
11445         this.textEl.setWidth(1);
11446         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
11447         //this.el.endMeasure();
11448     },
11449
11450     /**
11451      * Sets the text for the tab (Note: this also sets the tooltip text)
11452      * @param {String} text The tab's text and tooltip
11453      */
11454     setText : function(text){
11455         this.text = text;
11456         this.textEl.update(text);
11457         this.setTooltip(text);
11458         if(!this.tabPanel.resizeTabs){
11459             this.autoSize();
11460         }
11461     },
11462     /**
11463      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
11464      */
11465     activate : function(){
11466         this.tabPanel.activate(this.id);
11467     },
11468
11469     /**
11470      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
11471      */
11472     disable : function(){
11473         if(this.tabPanel.active != this){
11474             this.disabled = true;
11475             this.pnode.addClass("disabled");
11476         }
11477     },
11478
11479     /**
11480      * Enables this TabPanelItem if it was previously disabled.
11481      */
11482     enable : function(){
11483         this.disabled = false;
11484         this.pnode.removeClass("disabled");
11485     },
11486
11487     /**
11488      * Sets the content for this TabPanelItem.
11489      * @param {String} content The content
11490      * @param {Boolean} loadScripts true to look for and load scripts
11491      */
11492     setContent : function(content, loadScripts){
11493         this.bodyEl.update(content, loadScripts);
11494     },
11495
11496     /**
11497      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
11498      * @return {Roo.UpdateManager} The UpdateManager
11499      */
11500     getUpdateManager : function(){
11501         return this.bodyEl.getUpdateManager();
11502     },
11503
11504     /**
11505      * Set a URL to be used to load the content for this TabPanelItem.
11506      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
11507      * @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)
11508      * @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)
11509      * @return {Roo.UpdateManager} The UpdateManager
11510      */
11511     setUrl : function(url, params, loadOnce){
11512         if(this.refreshDelegate){
11513             this.un('activate', this.refreshDelegate);
11514         }
11515         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
11516         this.on("activate", this.refreshDelegate);
11517         return this.bodyEl.getUpdateManager();
11518     },
11519
11520     /** @private */
11521     _handleRefresh : function(url, params, loadOnce){
11522         if(!loadOnce || !this.loaded){
11523             var updater = this.bodyEl.getUpdateManager();
11524             updater.update(url, params, this._setLoaded.createDelegate(this));
11525         }
11526     },
11527
11528     /**
11529      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
11530      *   Will fail silently if the setUrl method has not been called.
11531      *   This does not activate the panel, just updates its content.
11532      */
11533     refresh : function(){
11534         if(this.refreshDelegate){
11535            this.loaded = false;
11536            this.refreshDelegate();
11537         }
11538     },
11539
11540     /** @private */
11541     _setLoaded : function(){
11542         this.loaded = true;
11543     },
11544
11545     /** @private */
11546     closeClick : function(e){
11547         var o = {};
11548         e.stopEvent();
11549         this.fireEvent("beforeclose", this, o);
11550         if(o.cancel !== true){
11551             this.tabPanel.removeTab(this.id);
11552         }
11553     },
11554     /**
11555      * The text displayed in the tooltip for the close icon.
11556      * @type String
11557      */
11558     closeText : "Close this tab"
11559 });
11560
11561 /** @private */
11562 Roo.TabPanel.prototype.createStrip = function(container){
11563     var strip = document.createElement("div");
11564     strip.className = "x-tabs-wrap";
11565     container.appendChild(strip);
11566     return strip;
11567 };
11568 /** @private */
11569 Roo.TabPanel.prototype.createStripList = function(strip){
11570     // div wrapper for retard IE
11571     // returns the "tr" element.
11572     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
11573         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
11574         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
11575     return strip.firstChild.firstChild.firstChild.firstChild;
11576 };
11577 /** @private */
11578 Roo.TabPanel.prototype.createBody = function(container){
11579     var body = document.createElement("div");
11580     Roo.id(body, "tab-body");
11581     Roo.fly(body).addClass("x-tabs-body");
11582     container.appendChild(body);
11583     return body;
11584 };
11585 /** @private */
11586 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
11587     var body = Roo.getDom(id);
11588     if(!body){
11589         body = document.createElement("div");
11590         body.id = id;
11591     }
11592     Roo.fly(body).addClass("x-tabs-item-body");
11593     bodyEl.insertBefore(body, bodyEl.firstChild);
11594     return body;
11595 };
11596 /** @private */
11597 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
11598     var td = document.createElement("td");
11599     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
11600     //stripEl.appendChild(td);
11601     if(closable){
11602         td.className = "x-tabs-closable";
11603         if(!this.closeTpl){
11604             this.closeTpl = new Roo.Template(
11605                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11606                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
11607                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
11608             );
11609         }
11610         var el = this.closeTpl.overwrite(td, {"text": text});
11611         var close = el.getElementsByTagName("div")[0];
11612         var inner = el.getElementsByTagName("em")[0];
11613         return {"el": el, "close": close, "inner": inner};
11614     } else {
11615         if(!this.tabTpl){
11616             this.tabTpl = new Roo.Template(
11617                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
11618                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
11619             );
11620         }
11621         var el = this.tabTpl.overwrite(td, {"text": text});
11622         var inner = el.getElementsByTagName("em")[0];
11623         return {"el": el, "inner": inner};
11624     }
11625 };/*
11626  * Based on:
11627  * Ext JS Library 1.1.1
11628  * Copyright(c) 2006-2007, Ext JS, LLC.
11629  *
11630  * Originally Released Under LGPL - original licence link has changed is not relivant.
11631  *
11632  * Fork - LGPL
11633  * <script type="text/javascript">
11634  */
11635
11636 /**
11637  * @class Roo.Button
11638  * @extends Roo.util.Observable
11639  * Simple Button class
11640  * @cfg {String} text The button text
11641  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
11642  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
11643  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
11644  * @cfg {Object} scope The scope of the handler
11645  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
11646  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
11647  * @cfg {Boolean} hidden True to start hidden (defaults to false)
11648  * @cfg {Boolean} disabled True to start disabled (defaults to false)
11649  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
11650  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
11651    applies if enableToggle = true)
11652  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
11653  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
11654   an {@link Roo.util.ClickRepeater} config object (defaults to false).
11655  * @constructor
11656  * Create a new button
11657  * @param {Object} config The config object
11658  */
11659 Roo.Button = function(renderTo, config)
11660 {
11661     if (!config) {
11662         config = renderTo;
11663         renderTo = config.renderTo || false;
11664     }
11665     
11666     Roo.apply(this, config);
11667     this.addEvents({
11668         /**
11669              * @event click
11670              * Fires when this button is clicked
11671              * @param {Button} this
11672              * @param {EventObject} e The click event
11673              */
11674             "click" : true,
11675         /**
11676              * @event toggle
11677              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
11678              * @param {Button} this
11679              * @param {Boolean} pressed
11680              */
11681             "toggle" : true,
11682         /**
11683              * @event mouseover
11684              * Fires when the mouse hovers over the button
11685              * @param {Button} this
11686              * @param {Event} e The event object
11687              */
11688         'mouseover' : true,
11689         /**
11690              * @event mouseout
11691              * Fires when the mouse exits the button
11692              * @param {Button} this
11693              * @param {Event} e The event object
11694              */
11695         'mouseout': true,
11696          /**
11697              * @event render
11698              * Fires when the button is rendered
11699              * @param {Button} this
11700              */
11701         'render': true
11702     });
11703     if(this.menu){
11704         this.menu = Roo.menu.MenuMgr.get(this.menu);
11705     }
11706     // register listeners first!!  - so render can be captured..
11707     Roo.util.Observable.call(this);
11708     if(renderTo){
11709         this.render(renderTo);
11710     }
11711     
11712   
11713 };
11714
11715 Roo.extend(Roo.Button, Roo.util.Observable, {
11716     /**
11717      * 
11718      */
11719     
11720     /**
11721      * Read-only. True if this button is hidden
11722      * @type Boolean
11723      */
11724     hidden : false,
11725     /**
11726      * Read-only. True if this button is disabled
11727      * @type Boolean
11728      */
11729     disabled : false,
11730     /**
11731      * Read-only. True if this button is pressed (only if enableToggle = true)
11732      * @type Boolean
11733      */
11734     pressed : false,
11735
11736     /**
11737      * @cfg {Number} tabIndex 
11738      * The DOM tabIndex for this button (defaults to undefined)
11739      */
11740     tabIndex : undefined,
11741
11742     /**
11743      * @cfg {Boolean} enableToggle
11744      * True to enable pressed/not pressed toggling (defaults to false)
11745      */
11746     enableToggle: false,
11747     /**
11748      * @cfg {Mixed} menu
11749      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
11750      */
11751     menu : undefined,
11752     /**
11753      * @cfg {String} menuAlign
11754      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
11755      */
11756     menuAlign : "tl-bl?",
11757
11758     /**
11759      * @cfg {String} iconCls
11760      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
11761      */
11762     iconCls : undefined,
11763     /**
11764      * @cfg {String} type
11765      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
11766      */
11767     type : 'button',
11768
11769     // private
11770     menuClassTarget: 'tr',
11771
11772     /**
11773      * @cfg {String} clickEvent
11774      * The type of event to map to the button's event handler (defaults to 'click')
11775      */
11776     clickEvent : 'click',
11777
11778     /**
11779      * @cfg {Boolean} handleMouseEvents
11780      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
11781      */
11782     handleMouseEvents : true,
11783
11784     /**
11785      * @cfg {String} tooltipType
11786      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
11787      */
11788     tooltipType : 'qtip',
11789
11790     /**
11791      * @cfg {String} cls
11792      * A CSS class to apply to the button's main element.
11793      */
11794     
11795     /**
11796      * @cfg {Roo.Template} template (Optional)
11797      * An {@link Roo.Template} with which to create the Button's main element. This Template must
11798      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
11799      * require code modifications if required elements (e.g. a button) aren't present.
11800      */
11801
11802     // private
11803     render : function(renderTo){
11804         var btn;
11805         if(this.hideParent){
11806             this.parentEl = Roo.get(renderTo);
11807         }
11808         if(!this.dhconfig){
11809             if(!this.template){
11810                 if(!Roo.Button.buttonTemplate){
11811                     // hideous table template
11812                     Roo.Button.buttonTemplate = new Roo.Template(
11813                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
11814                         '<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>',
11815                         "</tr></tbody></table>");
11816                 }
11817                 this.template = Roo.Button.buttonTemplate;
11818             }
11819             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
11820             var btnEl = btn.child("button:first");
11821             btnEl.on('focus', this.onFocus, this);
11822             btnEl.on('blur', this.onBlur, this);
11823             if(this.cls){
11824                 btn.addClass(this.cls);
11825             }
11826             if(this.icon){
11827                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
11828             }
11829             if(this.iconCls){
11830                 btnEl.addClass(this.iconCls);
11831                 if(!this.cls){
11832                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
11833                 }
11834             }
11835             if(this.tabIndex !== undefined){
11836                 btnEl.dom.tabIndex = this.tabIndex;
11837             }
11838             if(this.tooltip){
11839                 if(typeof this.tooltip == 'object'){
11840                     Roo.QuickTips.tips(Roo.apply({
11841                           target: btnEl.id
11842                     }, this.tooltip));
11843                 } else {
11844                     btnEl.dom[this.tooltipType] = this.tooltip;
11845                 }
11846             }
11847         }else{
11848             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
11849         }
11850         this.el = btn;
11851         if(this.id){
11852             this.el.dom.id = this.el.id = this.id;
11853         }
11854         if(this.menu){
11855             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
11856             this.menu.on("show", this.onMenuShow, this);
11857             this.menu.on("hide", this.onMenuHide, this);
11858         }
11859         btn.addClass("x-btn");
11860         if(Roo.isIE && !Roo.isIE7){
11861             this.autoWidth.defer(1, this);
11862         }else{
11863             this.autoWidth();
11864         }
11865         if(this.handleMouseEvents){
11866             btn.on("mouseover", this.onMouseOver, this);
11867             btn.on("mouseout", this.onMouseOut, this);
11868             btn.on("mousedown", this.onMouseDown, this);
11869         }
11870         btn.on(this.clickEvent, this.onClick, this);
11871         //btn.on("mouseup", this.onMouseUp, this);
11872         if(this.hidden){
11873             this.hide();
11874         }
11875         if(this.disabled){
11876             this.disable();
11877         }
11878         Roo.ButtonToggleMgr.register(this);
11879         if(this.pressed){
11880             this.el.addClass("x-btn-pressed");
11881         }
11882         if(this.repeat){
11883             var repeater = new Roo.util.ClickRepeater(btn,
11884                 typeof this.repeat == "object" ? this.repeat : {}
11885             );
11886             repeater.on("click", this.onClick,  this);
11887         }
11888         
11889         this.fireEvent('render', this);
11890         
11891     },
11892     /**
11893      * Returns the button's underlying element
11894      * @return {Roo.Element} The element
11895      */
11896     getEl : function(){
11897         return this.el;  
11898     },
11899     
11900     /**
11901      * Destroys this Button and removes any listeners.
11902      */
11903     destroy : function(){
11904         Roo.ButtonToggleMgr.unregister(this);
11905         this.el.removeAllListeners();
11906         this.purgeListeners();
11907         this.el.remove();
11908     },
11909
11910     // private
11911     autoWidth : function(){
11912         if(this.el){
11913             this.el.setWidth("auto");
11914             if(Roo.isIE7 && Roo.isStrict){
11915                 var ib = this.el.child('button');
11916                 if(ib && ib.getWidth() > 20){
11917                     ib.clip();
11918                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
11919                 }
11920             }
11921             if(this.minWidth){
11922                 if(this.hidden){
11923                     this.el.beginMeasure();
11924                 }
11925                 if(this.el.getWidth() < this.minWidth){
11926                     this.el.setWidth(this.minWidth);
11927                 }
11928                 if(this.hidden){
11929                     this.el.endMeasure();
11930                 }
11931             }
11932         }
11933     },
11934
11935     /**
11936      * Assigns this button's click handler
11937      * @param {Function} handler The function to call when the button is clicked
11938      * @param {Object} scope (optional) Scope for the function passed in
11939      */
11940     setHandler : function(handler, scope){
11941         this.handler = handler;
11942         this.scope = scope;  
11943     },
11944     
11945     /**
11946      * Sets this button's text
11947      * @param {String} text The button text
11948      */
11949     setText : function(text){
11950         this.text = text;
11951         if(this.el){
11952             this.el.child("td.x-btn-center button.x-btn-text").update(text);
11953         }
11954         this.autoWidth();
11955     },
11956     
11957     /**
11958      * Gets the text for this button
11959      * @return {String} The button text
11960      */
11961     getText : function(){
11962         return this.text;  
11963     },
11964     
11965     /**
11966      * Show this button
11967      */
11968     show: function(){
11969         this.hidden = false;
11970         if(this.el){
11971             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
11972         }
11973     },
11974     
11975     /**
11976      * Hide this button
11977      */
11978     hide: function(){
11979         this.hidden = true;
11980         if(this.el){
11981             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
11982         }
11983     },
11984     
11985     /**
11986      * Convenience function for boolean show/hide
11987      * @param {Boolean} visible True to show, false to hide
11988      */
11989     setVisible: function(visible){
11990         if(visible) {
11991             this.show();
11992         }else{
11993             this.hide();
11994         }
11995     },
11996     
11997     /**
11998      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
11999      * @param {Boolean} state (optional) Force a particular state
12000      */
12001     toggle : function(state){
12002         state = state === undefined ? !this.pressed : state;
12003         if(state != this.pressed){
12004             if(state){
12005                 this.el.addClass("x-btn-pressed");
12006                 this.pressed = true;
12007                 this.fireEvent("toggle", this, true);
12008             }else{
12009                 this.el.removeClass("x-btn-pressed");
12010                 this.pressed = false;
12011                 this.fireEvent("toggle", this, false);
12012             }
12013             if(this.toggleHandler){
12014                 this.toggleHandler.call(this.scope || this, this, state);
12015             }
12016         }
12017     },
12018     
12019     /**
12020      * Focus the button
12021      */
12022     focus : function(){
12023         this.el.child('button:first').focus();
12024     },
12025     
12026     /**
12027      * Disable this button
12028      */
12029     disable : function(){
12030         if(this.el){
12031             this.el.addClass("x-btn-disabled");
12032         }
12033         this.disabled = true;
12034     },
12035     
12036     /**
12037      * Enable this button
12038      */
12039     enable : function(){
12040         if(this.el){
12041             this.el.removeClass("x-btn-disabled");
12042         }
12043         this.disabled = false;
12044     },
12045
12046     /**
12047      * Convenience function for boolean enable/disable
12048      * @param {Boolean} enabled True to enable, false to disable
12049      */
12050     setDisabled : function(v){
12051         this[v !== true ? "enable" : "disable"]();
12052     },
12053
12054     // private
12055     onClick : function(e){
12056         if(e){
12057             e.preventDefault();
12058         }
12059         if(e.button != 0){
12060             return;
12061         }
12062         if(!this.disabled){
12063             if(this.enableToggle){
12064                 this.toggle();
12065             }
12066             if(this.menu && !this.menu.isVisible()){
12067                 this.menu.show(this.el, this.menuAlign);
12068             }
12069             this.fireEvent("click", this, e);
12070             if(this.handler){
12071                 this.el.removeClass("x-btn-over");
12072                 this.handler.call(this.scope || this, this, e);
12073             }
12074         }
12075     },
12076     // private
12077     onMouseOver : function(e){
12078         if(!this.disabled){
12079             this.el.addClass("x-btn-over");
12080             this.fireEvent('mouseover', this, e);
12081         }
12082     },
12083     // private
12084     onMouseOut : function(e){
12085         if(!e.within(this.el,  true)){
12086             this.el.removeClass("x-btn-over");
12087             this.fireEvent('mouseout', this, e);
12088         }
12089     },
12090     // private
12091     onFocus : function(e){
12092         if(!this.disabled){
12093             this.el.addClass("x-btn-focus");
12094         }
12095     },
12096     // private
12097     onBlur : function(e){
12098         this.el.removeClass("x-btn-focus");
12099     },
12100     // private
12101     onMouseDown : function(e){
12102         if(!this.disabled && e.button == 0){
12103             this.el.addClass("x-btn-click");
12104             Roo.get(document).on('mouseup', this.onMouseUp, this);
12105         }
12106     },
12107     // private
12108     onMouseUp : function(e){
12109         if(e.button == 0){
12110             this.el.removeClass("x-btn-click");
12111             Roo.get(document).un('mouseup', this.onMouseUp, this);
12112         }
12113     },
12114     // private
12115     onMenuShow : function(e){
12116         this.el.addClass("x-btn-menu-active");
12117     },
12118     // private
12119     onMenuHide : function(e){
12120         this.el.removeClass("x-btn-menu-active");
12121     }   
12122 });
12123
12124 // Private utility class used by Button
12125 Roo.ButtonToggleMgr = function(){
12126    var groups = {};
12127    
12128    function toggleGroup(btn, state){
12129        if(state){
12130            var g = groups[btn.toggleGroup];
12131            for(var i = 0, l = g.length; i < l; i++){
12132                if(g[i] != btn){
12133                    g[i].toggle(false);
12134                }
12135            }
12136        }
12137    }
12138    
12139    return {
12140        register : function(btn){
12141            if(!btn.toggleGroup){
12142                return;
12143            }
12144            var g = groups[btn.toggleGroup];
12145            if(!g){
12146                g = groups[btn.toggleGroup] = [];
12147            }
12148            g.push(btn);
12149            btn.on("toggle", toggleGroup);
12150        },
12151        
12152        unregister : function(btn){
12153            if(!btn.toggleGroup){
12154                return;
12155            }
12156            var g = groups[btn.toggleGroup];
12157            if(g){
12158                g.remove(btn);
12159                btn.un("toggle", toggleGroup);
12160            }
12161        }
12162    };
12163 }();/*
12164  * Based on:
12165  * Ext JS Library 1.1.1
12166  * Copyright(c) 2006-2007, Ext JS, LLC.
12167  *
12168  * Originally Released Under LGPL - original licence link has changed is not relivant.
12169  *
12170  * Fork - LGPL
12171  * <script type="text/javascript">
12172  */
12173  
12174 /**
12175  * @class Roo.SplitButton
12176  * @extends Roo.Button
12177  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
12178  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
12179  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
12180  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
12181  * @cfg {String} arrowTooltip The title attribute of the arrow
12182  * @constructor
12183  * Create a new menu button
12184  * @param {String/HTMLElement/Element} renderTo The element to append the button to
12185  * @param {Object} config The config object
12186  */
12187 Roo.SplitButton = function(renderTo, config){
12188     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
12189     /**
12190      * @event arrowclick
12191      * Fires when this button's arrow is clicked
12192      * @param {SplitButton} this
12193      * @param {EventObject} e The click event
12194      */
12195     this.addEvents({"arrowclick":true});
12196 };
12197
12198 Roo.extend(Roo.SplitButton, Roo.Button, {
12199     render : function(renderTo){
12200         // this is one sweet looking template!
12201         var tpl = new Roo.Template(
12202             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
12203             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
12204             '<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>',
12205             "</tbody></table></td><td>",
12206             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
12207             '<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>',
12208             "</tbody></table></td></tr></table>"
12209         );
12210         var btn = tpl.append(renderTo, [this.text, this.type], true);
12211         var btnEl = btn.child("button");
12212         if(this.cls){
12213             btn.addClass(this.cls);
12214         }
12215         if(this.icon){
12216             btnEl.setStyle('background-image', 'url(' +this.icon +')');
12217         }
12218         if(this.iconCls){
12219             btnEl.addClass(this.iconCls);
12220             if(!this.cls){
12221                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
12222             }
12223         }
12224         this.el = btn;
12225         if(this.handleMouseEvents){
12226             btn.on("mouseover", this.onMouseOver, this);
12227             btn.on("mouseout", this.onMouseOut, this);
12228             btn.on("mousedown", this.onMouseDown, this);
12229             btn.on("mouseup", this.onMouseUp, this);
12230         }
12231         btn.on(this.clickEvent, this.onClick, this);
12232         if(this.tooltip){
12233             if(typeof this.tooltip == 'object'){
12234                 Roo.QuickTips.tips(Roo.apply({
12235                       target: btnEl.id
12236                 }, this.tooltip));
12237             } else {
12238                 btnEl.dom[this.tooltipType] = this.tooltip;
12239             }
12240         }
12241         if(this.arrowTooltip){
12242             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
12243         }
12244         if(this.hidden){
12245             this.hide();
12246         }
12247         if(this.disabled){
12248             this.disable();
12249         }
12250         if(this.pressed){
12251             this.el.addClass("x-btn-pressed");
12252         }
12253         if(Roo.isIE && !Roo.isIE7){
12254             this.autoWidth.defer(1, this);
12255         }else{
12256             this.autoWidth();
12257         }
12258         if(this.menu){
12259             this.menu.on("show", this.onMenuShow, this);
12260             this.menu.on("hide", this.onMenuHide, this);
12261         }
12262         this.fireEvent('render', this);
12263     },
12264
12265     // private
12266     autoWidth : function(){
12267         if(this.el){
12268             var tbl = this.el.child("table:first");
12269             var tbl2 = this.el.child("table:last");
12270             this.el.setWidth("auto");
12271             tbl.setWidth("auto");
12272             if(Roo.isIE7 && Roo.isStrict){
12273                 var ib = this.el.child('button:first');
12274                 if(ib && ib.getWidth() > 20){
12275                     ib.clip();
12276                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
12277                 }
12278             }
12279             if(this.minWidth){
12280                 if(this.hidden){
12281                     this.el.beginMeasure();
12282                 }
12283                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
12284                     tbl.setWidth(this.minWidth-tbl2.getWidth());
12285                 }
12286                 if(this.hidden){
12287                     this.el.endMeasure();
12288                 }
12289             }
12290             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
12291         } 
12292     },
12293     /**
12294      * Sets this button's click handler
12295      * @param {Function} handler The function to call when the button is clicked
12296      * @param {Object} scope (optional) Scope for the function passed above
12297      */
12298     setHandler : function(handler, scope){
12299         this.handler = handler;
12300         this.scope = scope;  
12301     },
12302     
12303     /**
12304      * Sets this button's arrow click handler
12305      * @param {Function} handler The function to call when the arrow is clicked
12306      * @param {Object} scope (optional) Scope for the function passed above
12307      */
12308     setArrowHandler : function(handler, scope){
12309         this.arrowHandler = handler;
12310         this.scope = scope;  
12311     },
12312     
12313     /**
12314      * Focus the button
12315      */
12316     focus : function(){
12317         if(this.el){
12318             this.el.child("button:first").focus();
12319         }
12320     },
12321
12322     // private
12323     onClick : function(e){
12324         e.preventDefault();
12325         if(!this.disabled){
12326             if(e.getTarget(".x-btn-menu-arrow-wrap")){
12327                 if(this.menu && !this.menu.isVisible()){
12328                     this.menu.show(this.el, this.menuAlign);
12329                 }
12330                 this.fireEvent("arrowclick", this, e);
12331                 if(this.arrowHandler){
12332                     this.arrowHandler.call(this.scope || this, this, e);
12333                 }
12334             }else{
12335                 this.fireEvent("click", this, e);
12336                 if(this.handler){
12337                     this.handler.call(this.scope || this, this, e);
12338                 }
12339             }
12340         }
12341     },
12342     // private
12343     onMouseDown : function(e){
12344         if(!this.disabled){
12345             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
12346         }
12347     },
12348     // private
12349     onMouseUp : function(e){
12350         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
12351     }   
12352 });
12353
12354
12355 // backwards compat
12356 Roo.MenuButton = Roo.SplitButton;/*
12357  * Based on:
12358  * Ext JS Library 1.1.1
12359  * Copyright(c) 2006-2007, Ext JS, LLC.
12360  *
12361  * Originally Released Under LGPL - original licence link has changed is not relivant.
12362  *
12363  * Fork - LGPL
12364  * <script type="text/javascript">
12365  */
12366
12367 /**
12368  * @class Roo.Toolbar
12369  * Basic Toolbar class.
12370  * @constructor
12371  * Creates a new Toolbar
12372  * @param {Object} container The config object
12373  */ 
12374 Roo.Toolbar = function(container, buttons, config)
12375 {
12376     /// old consturctor format still supported..
12377     if(container instanceof Array){ // omit the container for later rendering
12378         buttons = container;
12379         config = buttons;
12380         container = null;
12381     }
12382     if (typeof(container) == 'object' && container.xtype) {
12383         config = container;
12384         container = config.container;
12385         buttons = config.buttons || []; // not really - use items!!
12386     }
12387     var xitems = [];
12388     if (config && config.items) {
12389         xitems = config.items;
12390         delete config.items;
12391     }
12392     Roo.apply(this, config);
12393     this.buttons = buttons;
12394     
12395     if(container){
12396         this.render(container);
12397     }
12398     this.xitems = xitems;
12399     Roo.each(xitems, function(b) {
12400         this.add(b);
12401     }, this);
12402     
12403 };
12404
12405 Roo.Toolbar.prototype = {
12406     /**
12407      * @cfg {Array} items
12408      * array of button configs or elements to add (will be converted to a MixedCollection)
12409      */
12410     
12411     /**
12412      * @cfg {String/HTMLElement/Element} container
12413      * The id or element that will contain the toolbar
12414      */
12415     // private
12416     render : function(ct){
12417         this.el = Roo.get(ct);
12418         if(this.cls){
12419             this.el.addClass(this.cls);
12420         }
12421         // using a table allows for vertical alignment
12422         // 100% width is needed by Safari...
12423         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
12424         this.tr = this.el.child("tr", true);
12425         var autoId = 0;
12426         this.items = new Roo.util.MixedCollection(false, function(o){
12427             return o.id || ("item" + (++autoId));
12428         });
12429         if(this.buttons){
12430             this.add.apply(this, this.buttons);
12431             delete this.buttons;
12432         }
12433     },
12434
12435     /**
12436      * Adds element(s) to the toolbar -- this function takes a variable number of 
12437      * arguments of mixed type and adds them to the toolbar.
12438      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
12439      * <ul>
12440      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
12441      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
12442      * <li>Field: Any form field (equivalent to {@link #addField})</li>
12443      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
12444      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
12445      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
12446      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
12447      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
12448      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
12449      * </ul>
12450      * @param {Mixed} arg2
12451      * @param {Mixed} etc.
12452      */
12453     add : function(){
12454         var a = arguments, l = a.length;
12455         for(var i = 0; i < l; i++){
12456             this._add(a[i]);
12457         }
12458     },
12459     // private..
12460     _add : function(el) {
12461         
12462         if (el.xtype) {
12463             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
12464         }
12465         
12466         if (el.applyTo){ // some kind of form field
12467             return this.addField(el);
12468         } 
12469         if (el.render){ // some kind of Toolbar.Item
12470             return this.addItem(el);
12471         }
12472         if (typeof el == "string"){ // string
12473             if(el == "separator" || el == "-"){
12474                 return this.addSeparator();
12475             }
12476             if (el == " "){
12477                 return this.addSpacer();
12478             }
12479             if(el == "->"){
12480                 return this.addFill();
12481             }
12482             return this.addText(el);
12483             
12484         }
12485         if(el.tagName){ // element
12486             return this.addElement(el);
12487         }
12488         if(typeof el == "object"){ // must be button config?
12489             return this.addButton(el);
12490         }
12491         // and now what?!?!
12492         return false;
12493         
12494     },
12495     
12496     /**
12497      * Add an Xtype element
12498      * @param {Object} xtype Xtype Object
12499      * @return {Object} created Object
12500      */
12501     addxtype : function(e){
12502         return this.add(e);  
12503     },
12504     
12505     /**
12506      * Returns the Element for this toolbar.
12507      * @return {Roo.Element}
12508      */
12509     getEl : function(){
12510         return this.el;  
12511     },
12512     
12513     /**
12514      * Adds a separator
12515      * @return {Roo.Toolbar.Item} The separator item
12516      */
12517     addSeparator : function(){
12518         return this.addItem(new Roo.Toolbar.Separator());
12519     },
12520
12521     /**
12522      * Adds a spacer element
12523      * @return {Roo.Toolbar.Spacer} The spacer item
12524      */
12525     addSpacer : function(){
12526         return this.addItem(new Roo.Toolbar.Spacer());
12527     },
12528
12529     /**
12530      * Adds a fill element that forces subsequent additions to the right side of the toolbar
12531      * @return {Roo.Toolbar.Fill} The fill item
12532      */
12533     addFill : function(){
12534         return this.addItem(new Roo.Toolbar.Fill());
12535     },
12536
12537     /**
12538      * Adds any standard HTML element to the toolbar
12539      * @param {String/HTMLElement/Element} el The element or id of the element to add
12540      * @return {Roo.Toolbar.Item} The element's item
12541      */
12542     addElement : function(el){
12543         return this.addItem(new Roo.Toolbar.Item(el));
12544     },
12545     /**
12546      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
12547      * @type Roo.util.MixedCollection  
12548      */
12549     items : false,
12550      
12551     /**
12552      * Adds any Toolbar.Item or subclass
12553      * @param {Roo.Toolbar.Item} item
12554      * @return {Roo.Toolbar.Item} The item
12555      */
12556     addItem : function(item){
12557         var td = this.nextBlock();
12558         item.render(td);
12559         this.items.add(item);
12560         return item;
12561     },
12562     
12563     /**
12564      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
12565      * @param {Object/Array} config A button config or array of configs
12566      * @return {Roo.Toolbar.Button/Array}
12567      */
12568     addButton : function(config){
12569         if(config instanceof Array){
12570             var buttons = [];
12571             for(var i = 0, len = config.length; i < len; i++) {
12572                 buttons.push(this.addButton(config[i]));
12573             }
12574             return buttons;
12575         }
12576         var b = config;
12577         if(!(config instanceof Roo.Toolbar.Button)){
12578             b = config.split ?
12579                 new Roo.Toolbar.SplitButton(config) :
12580                 new Roo.Toolbar.Button(config);
12581         }
12582         var td = this.nextBlock();
12583         b.render(td);
12584         this.items.add(b);
12585         return b;
12586     },
12587     
12588     /**
12589      * Adds text to the toolbar
12590      * @param {String} text The text to add
12591      * @return {Roo.Toolbar.Item} The element's item
12592      */
12593     addText : function(text){
12594         return this.addItem(new Roo.Toolbar.TextItem(text));
12595     },
12596     
12597     /**
12598      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
12599      * @param {Number} index The index where the item is to be inserted
12600      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
12601      * @return {Roo.Toolbar.Button/Item}
12602      */
12603     insertButton : function(index, item){
12604         if(item instanceof Array){
12605             var buttons = [];
12606             for(var i = 0, len = item.length; i < len; i++) {
12607                buttons.push(this.insertButton(index + i, item[i]));
12608             }
12609             return buttons;
12610         }
12611         if (!(item instanceof Roo.Toolbar.Button)){
12612            item = new Roo.Toolbar.Button(item);
12613         }
12614         var td = document.createElement("td");
12615         this.tr.insertBefore(td, this.tr.childNodes[index]);
12616         item.render(td);
12617         this.items.insert(index, item);
12618         return item;
12619     },
12620     
12621     /**
12622      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
12623      * @param {Object} config
12624      * @return {Roo.Toolbar.Item} The element's item
12625      */
12626     addDom : function(config, returnEl){
12627         var td = this.nextBlock();
12628         Roo.DomHelper.overwrite(td, config);
12629         var ti = new Roo.Toolbar.Item(td.firstChild);
12630         ti.render(td);
12631         this.items.add(ti);
12632         return ti;
12633     },
12634
12635     /**
12636      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
12637      * @type Roo.util.MixedCollection  
12638      */
12639     fields : false,
12640     
12641     /**
12642      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
12643      * Note: the field should not have been rendered yet. For a field that has already been
12644      * rendered, use {@link #addElement}.
12645      * @param {Roo.form.Field} field
12646      * @return {Roo.ToolbarItem}
12647      */
12648      
12649       
12650     addField : function(field) {
12651         if (!this.fields) {
12652             var autoId = 0;
12653             this.fields = new Roo.util.MixedCollection(false, function(o){
12654                 return o.id || ("item" + (++autoId));
12655             });
12656
12657         }
12658         
12659         var td = this.nextBlock();
12660         field.render(td);
12661         var ti = new Roo.Toolbar.Item(td.firstChild);
12662         ti.render(td);
12663         this.items.add(ti);
12664         this.fields.add(field);
12665         return ti;
12666     },
12667     /**
12668      * Hide the toolbar
12669      * @method hide
12670      */
12671      
12672       
12673     hide : function()
12674     {
12675         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
12676         this.el.child('div').hide();
12677     },
12678     /**
12679      * Show the toolbar
12680      * @method show
12681      */
12682     show : function()
12683     {
12684         this.el.child('div').show();
12685     },
12686       
12687     // private
12688     nextBlock : function(){
12689         var td = document.createElement("td");
12690         this.tr.appendChild(td);
12691         return td;
12692     },
12693
12694     // private
12695     destroy : function(){
12696         if(this.items){ // rendered?
12697             Roo.destroy.apply(Roo, this.items.items);
12698         }
12699         if(this.fields){ // rendered?
12700             Roo.destroy.apply(Roo, this.fields.items);
12701         }
12702         Roo.Element.uncache(this.el, this.tr);
12703     }
12704 };
12705
12706 /**
12707  * @class Roo.Toolbar.Item
12708  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
12709  * @constructor
12710  * Creates a new Item
12711  * @param {HTMLElement} el 
12712  */
12713 Roo.Toolbar.Item = function(el){
12714     this.el = Roo.getDom(el);
12715     this.id = Roo.id(this.el);
12716     this.hidden = false;
12717 };
12718
12719 Roo.Toolbar.Item.prototype = {
12720     
12721     /**
12722      * Get this item's HTML Element
12723      * @return {HTMLElement}
12724      */
12725     getEl : function(){
12726        return this.el;  
12727     },
12728
12729     // private
12730     render : function(td){
12731         this.td = td;
12732         td.appendChild(this.el);
12733     },
12734     
12735     /**
12736      * Removes and destroys this item.
12737      */
12738     destroy : function(){
12739         this.td.parentNode.removeChild(this.td);
12740     },
12741     
12742     /**
12743      * Shows this item.
12744      */
12745     show: function(){
12746         this.hidden = false;
12747         this.td.style.display = "";
12748     },
12749     
12750     /**
12751      * Hides this item.
12752      */
12753     hide: function(){
12754         this.hidden = true;
12755         this.td.style.display = "none";
12756     },
12757     
12758     /**
12759      * Convenience function for boolean show/hide.
12760      * @param {Boolean} visible true to show/false to hide
12761      */
12762     setVisible: function(visible){
12763         if(visible) {
12764             this.show();
12765         }else{
12766             this.hide();
12767         }
12768     },
12769     
12770     /**
12771      * Try to focus this item.
12772      */
12773     focus : function(){
12774         Roo.fly(this.el).focus();
12775     },
12776     
12777     /**
12778      * Disables this item.
12779      */
12780     disable : function(){
12781         Roo.fly(this.td).addClass("x-item-disabled");
12782         this.disabled = true;
12783         this.el.disabled = true;
12784     },
12785     
12786     /**
12787      * Enables this item.
12788      */
12789     enable : function(){
12790         Roo.fly(this.td).removeClass("x-item-disabled");
12791         this.disabled = false;
12792         this.el.disabled = false;
12793     }
12794 };
12795
12796
12797 /**
12798  * @class Roo.Toolbar.Separator
12799  * @extends Roo.Toolbar.Item
12800  * A simple toolbar separator class
12801  * @constructor
12802  * Creates a new Separator
12803  */
12804 Roo.Toolbar.Separator = function(){
12805     var s = document.createElement("span");
12806     s.className = "ytb-sep";
12807     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
12808 };
12809 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
12810     enable:Roo.emptyFn,
12811     disable:Roo.emptyFn,
12812     focus:Roo.emptyFn
12813 });
12814
12815 /**
12816  * @class Roo.Toolbar.Spacer
12817  * @extends Roo.Toolbar.Item
12818  * A simple element that adds extra horizontal space to a toolbar.
12819  * @constructor
12820  * Creates a new Spacer
12821  */
12822 Roo.Toolbar.Spacer = function(){
12823     var s = document.createElement("div");
12824     s.className = "ytb-spacer";
12825     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
12826 };
12827 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
12828     enable:Roo.emptyFn,
12829     disable:Roo.emptyFn,
12830     focus:Roo.emptyFn
12831 });
12832
12833 /**
12834  * @class Roo.Toolbar.Fill
12835  * @extends Roo.Toolbar.Spacer
12836  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
12837  * @constructor
12838  * Creates a new Spacer
12839  */
12840 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
12841     // private
12842     render : function(td){
12843         td.style.width = '100%';
12844         Roo.Toolbar.Fill.superclass.render.call(this, td);
12845     }
12846 });
12847
12848 /**
12849  * @class Roo.Toolbar.TextItem
12850  * @extends Roo.Toolbar.Item
12851  * A simple class that renders text directly into a toolbar.
12852  * @constructor
12853  * Creates a new TextItem
12854  * @param {String} text
12855  */
12856 Roo.Toolbar.TextItem = function(text){
12857     if (typeof(text) == 'object') {
12858         text = text.text;
12859     }
12860     var s = document.createElement("span");
12861     s.className = "ytb-text";
12862     s.innerHTML = text;
12863     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
12864 };
12865 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
12866     enable:Roo.emptyFn,
12867     disable:Roo.emptyFn,
12868     focus:Roo.emptyFn
12869 });
12870
12871 /**
12872  * @class Roo.Toolbar.Button
12873  * @extends Roo.Button
12874  * A button that renders into a toolbar.
12875  * @constructor
12876  * Creates a new Button
12877  * @param {Object} config A standard {@link Roo.Button} config object
12878  */
12879 Roo.Toolbar.Button = function(config){
12880     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
12881 };
12882 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
12883     render : function(td){
12884         this.td = td;
12885         Roo.Toolbar.Button.superclass.render.call(this, td);
12886     },
12887     
12888     /**
12889      * Removes and destroys this button
12890      */
12891     destroy : function(){
12892         Roo.Toolbar.Button.superclass.destroy.call(this);
12893         this.td.parentNode.removeChild(this.td);
12894     },
12895     
12896     /**
12897      * Shows this button
12898      */
12899     show: function(){
12900         this.hidden = false;
12901         this.td.style.display = "";
12902     },
12903     
12904     /**
12905      * Hides this button
12906      */
12907     hide: function(){
12908         this.hidden = true;
12909         this.td.style.display = "none";
12910     },
12911
12912     /**
12913      * Disables this item
12914      */
12915     disable : function(){
12916         Roo.fly(this.td).addClass("x-item-disabled");
12917         this.disabled = true;
12918     },
12919
12920     /**
12921      * Enables this item
12922      */
12923     enable : function(){
12924         Roo.fly(this.td).removeClass("x-item-disabled");
12925         this.disabled = false;
12926     }
12927 });
12928 // backwards compat
12929 Roo.ToolbarButton = Roo.Toolbar.Button;
12930
12931 /**
12932  * @class Roo.Toolbar.SplitButton
12933  * @extends Roo.SplitButton
12934  * A menu button that renders into a toolbar.
12935  * @constructor
12936  * Creates a new SplitButton
12937  * @param {Object} config A standard {@link Roo.SplitButton} config object
12938  */
12939 Roo.Toolbar.SplitButton = function(config){
12940     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
12941 };
12942 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
12943     render : function(td){
12944         this.td = td;
12945         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
12946     },
12947     
12948     /**
12949      * Removes and destroys this button
12950      */
12951     destroy : function(){
12952         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
12953         this.td.parentNode.removeChild(this.td);
12954     },
12955     
12956     /**
12957      * Shows this button
12958      */
12959     show: function(){
12960         this.hidden = false;
12961         this.td.style.display = "";
12962     },
12963     
12964     /**
12965      * Hides this button
12966      */
12967     hide: function(){
12968         this.hidden = true;
12969         this.td.style.display = "none";
12970     }
12971 });
12972
12973 // backwards compat
12974 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
12975  * Based on:
12976  * Ext JS Library 1.1.1
12977  * Copyright(c) 2006-2007, Ext JS, LLC.
12978  *
12979  * Originally Released Under LGPL - original licence link has changed is not relivant.
12980  *
12981  * Fork - LGPL
12982  * <script type="text/javascript">
12983  */
12984  
12985 /**
12986  * @class Roo.PagingToolbar
12987  * @extends Roo.Toolbar
12988  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
12989  * @constructor
12990  * Create a new PagingToolbar
12991  * @param {Object} config The config object
12992  */
12993 Roo.PagingToolbar = function(el, ds, config)
12994 {
12995     // old args format still supported... - xtype is prefered..
12996     if (typeof(el) == 'object' && el.xtype) {
12997         // created from xtype...
12998         config = el;
12999         ds = el.dataSource;
13000         el = config.container;
13001     }
13002     var items = [];
13003     if (config.items) {
13004         items = config.items;
13005         config.items = [];
13006     }
13007     
13008     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
13009     this.ds = ds;
13010     this.cursor = 0;
13011     this.renderButtons(this.el);
13012     this.bind(ds);
13013     
13014     // supprot items array.
13015    
13016     Roo.each(items, function(e) {
13017         this.add(Roo.factory(e));
13018     },this);
13019     
13020 };
13021
13022 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
13023     /**
13024      * @cfg {Roo.data.Store} dataSource
13025      * The underlying data store providing the paged data
13026      */
13027     /**
13028      * @cfg {String/HTMLElement/Element} container
13029      * container The id or element that will contain the toolbar
13030      */
13031     /**
13032      * @cfg {Boolean} displayInfo
13033      * True to display the displayMsg (defaults to false)
13034      */
13035     /**
13036      * @cfg {Number} pageSize
13037      * The number of records to display per page (defaults to 20)
13038      */
13039     pageSize: 20,
13040     /**
13041      * @cfg {String} displayMsg
13042      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
13043      */
13044     displayMsg : 'Displaying {0} - {1} of {2}',
13045     /**
13046      * @cfg {String} emptyMsg
13047      * The message to display when no records are found (defaults to "No data to display")
13048      */
13049     emptyMsg : 'No data to display',
13050     /**
13051      * Customizable piece of the default paging text (defaults to "Page")
13052      * @type String
13053      */
13054     beforePageText : "Page",
13055     /**
13056      * Customizable piece of the default paging text (defaults to "of %0")
13057      * @type String
13058      */
13059     afterPageText : "of {0}",
13060     /**
13061      * Customizable piece of the default paging text (defaults to "First Page")
13062      * @type String
13063      */
13064     firstText : "First Page",
13065     /**
13066      * Customizable piece of the default paging text (defaults to "Previous Page")
13067      * @type String
13068      */
13069     prevText : "Previous Page",
13070     /**
13071      * Customizable piece of the default paging text (defaults to "Next Page")
13072      * @type String
13073      */
13074     nextText : "Next Page",
13075     /**
13076      * Customizable piece of the default paging text (defaults to "Last Page")
13077      * @type String
13078      */
13079     lastText : "Last Page",
13080     /**
13081      * Customizable piece of the default paging text (defaults to "Refresh")
13082      * @type String
13083      */
13084     refreshText : "Refresh",
13085
13086     // private
13087     renderButtons : function(el){
13088         Roo.PagingToolbar.superclass.render.call(this, el);
13089         this.first = this.addButton({
13090             tooltip: this.firstText,
13091             cls: "x-btn-icon x-grid-page-first",
13092             disabled: true,
13093             handler: this.onClick.createDelegate(this, ["first"])
13094         });
13095         this.prev = this.addButton({
13096             tooltip: this.prevText,
13097             cls: "x-btn-icon x-grid-page-prev",
13098             disabled: true,
13099             handler: this.onClick.createDelegate(this, ["prev"])
13100         });
13101         //this.addSeparator();
13102         this.add(this.beforePageText);
13103         this.field = Roo.get(this.addDom({
13104            tag: "input",
13105            type: "text",
13106            size: "3",
13107            value: "1",
13108            cls: "x-grid-page-number"
13109         }).el);
13110         this.field.on("keydown", this.onPagingKeydown, this);
13111         this.field.on("focus", function(){this.dom.select();});
13112         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
13113         this.field.setHeight(18);
13114         //this.addSeparator();
13115         this.next = this.addButton({
13116             tooltip: this.nextText,
13117             cls: "x-btn-icon x-grid-page-next",
13118             disabled: true,
13119             handler: this.onClick.createDelegate(this, ["next"])
13120         });
13121         this.last = this.addButton({
13122             tooltip: this.lastText,
13123             cls: "x-btn-icon x-grid-page-last",
13124             disabled: true,
13125             handler: this.onClick.createDelegate(this, ["last"])
13126         });
13127         //this.addSeparator();
13128         this.loading = this.addButton({
13129             tooltip: this.refreshText,
13130             cls: "x-btn-icon x-grid-loading",
13131             handler: this.onClick.createDelegate(this, ["refresh"])
13132         });
13133
13134         if(this.displayInfo){
13135             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
13136         }
13137     },
13138
13139     // private
13140     updateInfo : function(){
13141         if(this.displayEl){
13142             var count = this.ds.getCount();
13143             var msg = count == 0 ?
13144                 this.emptyMsg :
13145                 String.format(
13146                     this.displayMsg,
13147                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
13148                 );
13149             this.displayEl.update(msg);
13150         }
13151     },
13152
13153     // private
13154     onLoad : function(ds, r, o){
13155        this.cursor = o.params ? o.params.start : 0;
13156        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
13157
13158        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
13159        this.field.dom.value = ap;
13160        this.first.setDisabled(ap == 1);
13161        this.prev.setDisabled(ap == 1);
13162        this.next.setDisabled(ap == ps);
13163        this.last.setDisabled(ap == ps);
13164        this.loading.enable();
13165        this.updateInfo();
13166     },
13167
13168     // private
13169     getPageData : function(){
13170         var total = this.ds.getTotalCount();
13171         return {
13172             total : total,
13173             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
13174             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
13175         };
13176     },
13177
13178     // private
13179     onLoadError : function(){
13180         this.loading.enable();
13181     },
13182
13183     // private
13184     onPagingKeydown : function(e){
13185         var k = e.getKey();
13186         var d = this.getPageData();
13187         if(k == e.RETURN){
13188             var v = this.field.dom.value, pageNum;
13189             if(!v || isNaN(pageNum = parseInt(v, 10))){
13190                 this.field.dom.value = d.activePage;
13191                 return;
13192             }
13193             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
13194             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13195             e.stopEvent();
13196         }
13197         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))
13198         {
13199           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
13200           this.field.dom.value = pageNum;
13201           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
13202           e.stopEvent();
13203         }
13204         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13205         {
13206           var v = this.field.dom.value, pageNum; 
13207           var increment = (e.shiftKey) ? 10 : 1;
13208           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
13209             increment *= -1;
13210           if(!v || isNaN(pageNum = parseInt(v, 10))) {
13211             this.field.dom.value = d.activePage;
13212             return;
13213           }
13214           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
13215           {
13216             this.field.dom.value = parseInt(v, 10) + increment;
13217             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
13218             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
13219           }
13220           e.stopEvent();
13221         }
13222     },
13223
13224     // private
13225     beforeLoad : function(){
13226         if(this.loading){
13227             this.loading.disable();
13228         }
13229     },
13230
13231     // private
13232     onClick : function(which){
13233         var ds = this.ds;
13234         switch(which){
13235             case "first":
13236                 ds.load({params:{start: 0, limit: this.pageSize}});
13237             break;
13238             case "prev":
13239                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
13240             break;
13241             case "next":
13242                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
13243             break;
13244             case "last":
13245                 var total = ds.getTotalCount();
13246                 var extra = total % this.pageSize;
13247                 var lastStart = extra ? (total - extra) : total-this.pageSize;
13248                 ds.load({params:{start: lastStart, limit: this.pageSize}});
13249             break;
13250             case "refresh":
13251                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
13252             break;
13253         }
13254     },
13255
13256     /**
13257      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
13258      * @param {Roo.data.Store} store The data store to unbind
13259      */
13260     unbind : function(ds){
13261         ds.un("beforeload", this.beforeLoad, this);
13262         ds.un("load", this.onLoad, this);
13263         ds.un("loadexception", this.onLoadError, this);
13264         ds.un("remove", this.updateInfo, this);
13265         ds.un("add", this.updateInfo, this);
13266         this.ds = undefined;
13267     },
13268
13269     /**
13270      * Binds the paging toolbar to the specified {@link Roo.data.Store}
13271      * @param {Roo.data.Store} store The data store to bind
13272      */
13273     bind : function(ds){
13274         ds.on("beforeload", this.beforeLoad, this);
13275         ds.on("load", this.onLoad, this);
13276         ds.on("loadexception", this.onLoadError, this);
13277         ds.on("remove", this.updateInfo, this);
13278         ds.on("add", this.updateInfo, this);
13279         this.ds = ds;
13280     }
13281 });/*
13282  * Based on:
13283  * Ext JS Library 1.1.1
13284  * Copyright(c) 2006-2007, Ext JS, LLC.
13285  *
13286  * Originally Released Under LGPL - original licence link has changed is not relivant.
13287  *
13288  * Fork - LGPL
13289  * <script type="text/javascript">
13290  */
13291
13292 /**
13293  * @class Roo.Resizable
13294  * @extends Roo.util.Observable
13295  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
13296  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
13297  * 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
13298  * the element will be wrapped for you automatically.</p>
13299  * <p>Here is the list of valid resize handles:</p>
13300  * <pre>
13301 Value   Description
13302 ------  -------------------
13303  'n'     north
13304  's'     south
13305  'e'     east
13306  'w'     west
13307  'nw'    northwest
13308  'sw'    southwest
13309  'se'    southeast
13310  'ne'    northeast
13311  'hd'    horizontal drag
13312  'all'   all
13313 </pre>
13314  * <p>Here's an example showing the creation of a typical Resizable:</p>
13315  * <pre><code>
13316 var resizer = new Roo.Resizable("element-id", {
13317     handles: 'all',
13318     minWidth: 200,
13319     minHeight: 100,
13320     maxWidth: 500,
13321     maxHeight: 400,
13322     pinned: true
13323 });
13324 resizer.on("resize", myHandler);
13325 </code></pre>
13326  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
13327  * resizer.east.setDisplayed(false);</p>
13328  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
13329  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
13330  * resize operation's new size (defaults to [0, 0])
13331  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
13332  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
13333  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
13334  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
13335  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
13336  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
13337  * @cfg {Number} width The width of the element in pixels (defaults to null)
13338  * @cfg {Number} height The height of the element in pixels (defaults to null)
13339  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
13340  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
13341  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
13342  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
13343  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
13344  * in favor of the handles config option (defaults to false)
13345  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
13346  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
13347  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
13348  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
13349  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
13350  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
13351  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
13352  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
13353  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
13354  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
13355  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
13356  * @constructor
13357  * Create a new resizable component
13358  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
13359  * @param {Object} config configuration options
13360   */
13361 Roo.Resizable = function(el, config)
13362 {
13363     this.el = Roo.get(el);
13364
13365     if(config && config.wrap){
13366         config.resizeChild = this.el;
13367         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
13368         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
13369         this.el.setStyle("overflow", "hidden");
13370         this.el.setPositioning(config.resizeChild.getPositioning());
13371         config.resizeChild.clearPositioning();
13372         if(!config.width || !config.height){
13373             var csize = config.resizeChild.getSize();
13374             this.el.setSize(csize.width, csize.height);
13375         }
13376         if(config.pinned && !config.adjustments){
13377             config.adjustments = "auto";
13378         }
13379     }
13380
13381     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
13382     this.proxy.unselectable();
13383     this.proxy.enableDisplayMode('block');
13384
13385     Roo.apply(this, config);
13386
13387     if(this.pinned){
13388         this.disableTrackOver = true;
13389         this.el.addClass("x-resizable-pinned");
13390     }
13391     // if the element isn't positioned, make it relative
13392     var position = this.el.getStyle("position");
13393     if(position != "absolute" && position != "fixed"){
13394         this.el.setStyle("position", "relative");
13395     }
13396     if(!this.handles){ // no handles passed, must be legacy style
13397         this.handles = 's,e,se';
13398         if(this.multiDirectional){
13399             this.handles += ',n,w';
13400         }
13401     }
13402     if(this.handles == "all"){
13403         this.handles = "n s e w ne nw se sw";
13404     }
13405     var hs = this.handles.split(/\s*?[,;]\s*?| /);
13406     var ps = Roo.Resizable.positions;
13407     for(var i = 0, len = hs.length; i < len; i++){
13408         if(hs[i] && ps[hs[i]]){
13409             var pos = ps[hs[i]];
13410             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
13411         }
13412     }
13413     // legacy
13414     this.corner = this.southeast;
13415     
13416     // updateBox = the box can move..
13417     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
13418         this.updateBox = true;
13419     }
13420
13421     this.activeHandle = null;
13422
13423     if(this.resizeChild){
13424         if(typeof this.resizeChild == "boolean"){
13425             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
13426         }else{
13427             this.resizeChild = Roo.get(this.resizeChild, true);
13428         }
13429     }
13430     
13431     if(this.adjustments == "auto"){
13432         var rc = this.resizeChild;
13433         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
13434         if(rc && (hw || hn)){
13435             rc.position("relative");
13436             rc.setLeft(hw ? hw.el.getWidth() : 0);
13437             rc.setTop(hn ? hn.el.getHeight() : 0);
13438         }
13439         this.adjustments = [
13440             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
13441             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
13442         ];
13443     }
13444
13445     if(this.draggable){
13446         this.dd = this.dynamic ?
13447             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
13448         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
13449     }
13450
13451     // public events
13452     this.addEvents({
13453         /**
13454          * @event beforeresize
13455          * Fired before resize is allowed. Set enabled to false to cancel resize.
13456          * @param {Roo.Resizable} this
13457          * @param {Roo.EventObject} e The mousedown event
13458          */
13459         "beforeresize" : true,
13460         /**
13461          * @event resize
13462          * Fired after a resize.
13463          * @param {Roo.Resizable} this
13464          * @param {Number} width The new width
13465          * @param {Number} height The new height
13466          * @param {Roo.EventObject} e The mouseup event
13467          */
13468         "resize" : true
13469     });
13470
13471     if(this.width !== null && this.height !== null){
13472         this.resizeTo(this.width, this.height);
13473     }else{
13474         this.updateChildSize();
13475     }
13476     if(Roo.isIE){
13477         this.el.dom.style.zoom = 1;
13478     }
13479     Roo.Resizable.superclass.constructor.call(this);
13480 };
13481
13482 Roo.extend(Roo.Resizable, Roo.util.Observable, {
13483         resizeChild : false,
13484         adjustments : [0, 0],
13485         minWidth : 5,
13486         minHeight : 5,
13487         maxWidth : 10000,
13488         maxHeight : 10000,
13489         enabled : true,
13490         animate : false,
13491         duration : .35,
13492         dynamic : false,
13493         handles : false,
13494         multiDirectional : false,
13495         disableTrackOver : false,
13496         easing : 'easeOutStrong',
13497         widthIncrement : 0,
13498         heightIncrement : 0,
13499         pinned : false,
13500         width : null,
13501         height : null,
13502         preserveRatio : false,
13503         transparent: false,
13504         minX: 0,
13505         minY: 0,
13506         draggable: false,
13507
13508         /**
13509          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
13510          */
13511         constrainTo: undefined,
13512         /**
13513          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
13514          */
13515         resizeRegion: undefined,
13516
13517
13518     /**
13519      * Perform a manual resize
13520      * @param {Number} width
13521      * @param {Number} height
13522      */
13523     resizeTo : function(width, height){
13524         this.el.setSize(width, height);
13525         this.updateChildSize();
13526         this.fireEvent("resize", this, width, height, null);
13527     },
13528
13529     // private
13530     startSizing : function(e, handle){
13531         this.fireEvent("beforeresize", this, e);
13532         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
13533
13534             if(!this.overlay){
13535                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
13536                 this.overlay.unselectable();
13537                 this.overlay.enableDisplayMode("block");
13538                 this.overlay.on("mousemove", this.onMouseMove, this);
13539                 this.overlay.on("mouseup", this.onMouseUp, this);
13540             }
13541             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
13542
13543             this.resizing = true;
13544             this.startBox = this.el.getBox();
13545             this.startPoint = e.getXY();
13546             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
13547                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
13548
13549             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
13550             this.overlay.show();
13551
13552             if(this.constrainTo) {
13553                 var ct = Roo.get(this.constrainTo);
13554                 this.resizeRegion = ct.getRegion().adjust(
13555                     ct.getFrameWidth('t'),
13556                     ct.getFrameWidth('l'),
13557                     -ct.getFrameWidth('b'),
13558                     -ct.getFrameWidth('r')
13559                 );
13560             }
13561
13562             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
13563             this.proxy.show();
13564             this.proxy.setBox(this.startBox);
13565             if(!this.dynamic){
13566                 this.proxy.setStyle('visibility', 'visible');
13567             }
13568         }
13569     },
13570
13571     // private
13572     onMouseDown : function(handle, e){
13573         if(this.enabled){
13574             e.stopEvent();
13575             this.activeHandle = handle;
13576             this.startSizing(e, handle);
13577         }
13578     },
13579
13580     // private
13581     onMouseUp : function(e){
13582         var size = this.resizeElement();
13583         this.resizing = false;
13584         this.handleOut();
13585         this.overlay.hide();
13586         this.proxy.hide();
13587         this.fireEvent("resize", this, size.width, size.height, e);
13588     },
13589
13590     // private
13591     updateChildSize : function(){
13592         if(this.resizeChild){
13593             var el = this.el;
13594             var child = this.resizeChild;
13595             var adj = this.adjustments;
13596             if(el.dom.offsetWidth){
13597                 var b = el.getSize(true);
13598                 child.setSize(b.width+adj[0], b.height+adj[1]);
13599             }
13600             // Second call here for IE
13601             // The first call enables instant resizing and
13602             // the second call corrects scroll bars if they
13603             // exist
13604             if(Roo.isIE){
13605                 setTimeout(function(){
13606                     if(el.dom.offsetWidth){
13607                         var b = el.getSize(true);
13608                         child.setSize(b.width+adj[0], b.height+adj[1]);
13609                     }
13610                 }, 10);
13611             }
13612         }
13613     },
13614
13615     // private
13616     snap : function(value, inc, min){
13617         if(!inc || !value) return value;
13618         var newValue = value;
13619         var m = value % inc;
13620         if(m > 0){
13621             if(m > (inc/2)){
13622                 newValue = value + (inc-m);
13623             }else{
13624                 newValue = value - m;
13625             }
13626         }
13627         return Math.max(min, newValue);
13628     },
13629
13630     // private
13631     resizeElement : function(){
13632         var box = this.proxy.getBox();
13633         if(this.updateBox){
13634             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
13635         }else{
13636             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
13637         }
13638         this.updateChildSize();
13639         if(!this.dynamic){
13640             this.proxy.hide();
13641         }
13642         return box;
13643     },
13644
13645     // private
13646     constrain : function(v, diff, m, mx){
13647         if(v - diff < m){
13648             diff = v - m;
13649         }else if(v - diff > mx){
13650             diff = mx - v;
13651         }
13652         return diff;
13653     },
13654
13655     // private
13656     onMouseMove : function(e){
13657         if(this.enabled){
13658             try{// try catch so if something goes wrong the user doesn't get hung
13659
13660             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
13661                 return;
13662             }
13663
13664             //var curXY = this.startPoint;
13665             var curSize = this.curSize || this.startBox;
13666             var x = this.startBox.x, y = this.startBox.y;
13667             var ox = x, oy = y;
13668             var w = curSize.width, h = curSize.height;
13669             var ow = w, oh = h;
13670             var mw = this.minWidth, mh = this.minHeight;
13671             var mxw = this.maxWidth, mxh = this.maxHeight;
13672             var wi = this.widthIncrement;
13673             var hi = this.heightIncrement;
13674
13675             var eventXY = e.getXY();
13676             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
13677             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
13678
13679             var pos = this.activeHandle.position;
13680
13681             switch(pos){
13682                 case "east":
13683                     w += diffX;
13684                     w = Math.min(Math.max(mw, w), mxw);
13685                     break;
13686              
13687                 case "south":
13688                     h += diffY;
13689                     h = Math.min(Math.max(mh, h), mxh);
13690                     break;
13691                 case "southeast":
13692                     w += diffX;
13693                     h += diffY;
13694                     w = Math.min(Math.max(mw, w), mxw);
13695                     h = Math.min(Math.max(mh, h), mxh);
13696                     break;
13697                 case "north":
13698                     diffY = this.constrain(h, diffY, mh, mxh);
13699                     y += diffY;
13700                     h -= diffY;
13701                     break;
13702                 case "hdrag":
13703                     
13704                     if (wi) {
13705                         var adiffX = Math.abs(diffX);
13706                         var sub = (adiffX % wi); // how much 
13707                         if (sub > (wi/2)) { // far enough to snap
13708                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
13709                         } else {
13710                             // remove difference.. 
13711                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
13712                         }
13713                     }
13714                     x += diffX;
13715                     x = Math.max(this.minX, x);
13716                     break;
13717                 case "west":
13718                     diffX = this.constrain(w, diffX, mw, mxw);
13719                     x += diffX;
13720                     w -= diffX;
13721                     break;
13722                 case "northeast":
13723                     w += diffX;
13724                     w = Math.min(Math.max(mw, w), mxw);
13725                     diffY = this.constrain(h, diffY, mh, mxh);
13726                     y += diffY;
13727                     h -= diffY;
13728                     break;
13729                 case "northwest":
13730                     diffX = this.constrain(w, diffX, mw, mxw);
13731                     diffY = this.constrain(h, diffY, mh, mxh);
13732                     y += diffY;
13733                     h -= diffY;
13734                     x += diffX;
13735                     w -= diffX;
13736                     break;
13737                case "southwest":
13738                     diffX = this.constrain(w, diffX, mw, mxw);
13739                     h += diffY;
13740                     h = Math.min(Math.max(mh, h), mxh);
13741                     x += diffX;
13742                     w -= diffX;
13743                     break;
13744             }
13745
13746             var sw = this.snap(w, wi, mw);
13747             var sh = this.snap(h, hi, mh);
13748             if(sw != w || sh != h){
13749                 switch(pos){
13750                     case "northeast":
13751                         y -= sh - h;
13752                     break;
13753                     case "north":
13754                         y -= sh - h;
13755                         break;
13756                     case "southwest":
13757                         x -= sw - w;
13758                     break;
13759                     case "west":
13760                         x -= sw - w;
13761                         break;
13762                     case "northwest":
13763                         x -= sw - w;
13764                         y -= sh - h;
13765                     break;
13766                 }
13767                 w = sw;
13768                 h = sh;
13769             }
13770
13771             if(this.preserveRatio){
13772                 switch(pos){
13773                     case "southeast":
13774                     case "east":
13775                         h = oh * (w/ow);
13776                         h = Math.min(Math.max(mh, h), mxh);
13777                         w = ow * (h/oh);
13778                        break;
13779                     case "south":
13780                         w = ow * (h/oh);
13781                         w = Math.min(Math.max(mw, w), mxw);
13782                         h = oh * (w/ow);
13783                         break;
13784                     case "northeast":
13785                         w = ow * (h/oh);
13786                         w = Math.min(Math.max(mw, w), mxw);
13787                         h = oh * (w/ow);
13788                     break;
13789                     case "north":
13790                         var tw = w;
13791                         w = ow * (h/oh);
13792                         w = Math.min(Math.max(mw, w), mxw);
13793                         h = oh * (w/ow);
13794                         x += (tw - w) / 2;
13795                         break;
13796                     case "southwest":
13797                         h = oh * (w/ow);
13798                         h = Math.min(Math.max(mh, h), mxh);
13799                         var tw = w;
13800                         w = ow * (h/oh);
13801                         x += tw - w;
13802                         break;
13803                     case "west":
13804                         var th = h;
13805                         h = oh * (w/ow);
13806                         h = Math.min(Math.max(mh, h), mxh);
13807                         y += (th - h) / 2;
13808                         var tw = w;
13809                         w = ow * (h/oh);
13810                         x += tw - w;
13811                        break;
13812                     case "northwest":
13813                         var tw = w;
13814                         var th = h;
13815                         h = oh * (w/ow);
13816                         h = Math.min(Math.max(mh, h), mxh);
13817                         w = ow * (h/oh);
13818                         y += th - h;
13819                         x += tw - w;
13820                        break;
13821
13822                 }
13823             }
13824             if (pos == 'hdrag') {
13825                 w = ow;
13826             }
13827             this.proxy.setBounds(x, y, w, h);
13828             if(this.dynamic){
13829                 this.resizeElement();
13830             }
13831             }catch(e){}
13832         }
13833     },
13834
13835     // private
13836     handleOver : function(){
13837         if(this.enabled){
13838             this.el.addClass("x-resizable-over");
13839         }
13840     },
13841
13842     // private
13843     handleOut : function(){
13844         if(!this.resizing){
13845             this.el.removeClass("x-resizable-over");
13846         }
13847     },
13848
13849     /**
13850      * Returns the element this component is bound to.
13851      * @return {Roo.Element}
13852      */
13853     getEl : function(){
13854         return this.el;
13855     },
13856
13857     /**
13858      * Returns the resizeChild element (or null).
13859      * @return {Roo.Element}
13860      */
13861     getResizeChild : function(){
13862         return this.resizeChild;
13863     },
13864
13865     /**
13866      * Destroys this resizable. If the element was wrapped and
13867      * removeEl is not true then the element remains.
13868      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
13869      */
13870     destroy : function(removeEl){
13871         this.proxy.remove();
13872         if(this.overlay){
13873             this.overlay.removeAllListeners();
13874             this.overlay.remove();
13875         }
13876         var ps = Roo.Resizable.positions;
13877         for(var k in ps){
13878             if(typeof ps[k] != "function" && this[ps[k]]){
13879                 var h = this[ps[k]];
13880                 h.el.removeAllListeners();
13881                 h.el.remove();
13882             }
13883         }
13884         if(removeEl){
13885             this.el.update("");
13886             this.el.remove();
13887         }
13888     }
13889 });
13890
13891 // private
13892 // hash to map config positions to true positions
13893 Roo.Resizable.positions = {
13894     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
13895     hd: "hdrag"
13896 };
13897
13898 // private
13899 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
13900     if(!this.tpl){
13901         // only initialize the template if resizable is used
13902         var tpl = Roo.DomHelper.createTemplate(
13903             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
13904         );
13905         tpl.compile();
13906         Roo.Resizable.Handle.prototype.tpl = tpl;
13907     }
13908     this.position = pos;
13909     this.rz = rz;
13910     // show north drag fro topdra
13911     var handlepos = pos == 'hdrag' ? 'north' : pos;
13912     
13913     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
13914     if (pos == 'hdrag') {
13915         this.el.setStyle('cursor', 'pointer');
13916     }
13917     this.el.unselectable();
13918     if(transparent){
13919         this.el.setOpacity(0);
13920     }
13921     this.el.on("mousedown", this.onMouseDown, this);
13922     if(!disableTrackOver){
13923         this.el.on("mouseover", this.onMouseOver, this);
13924         this.el.on("mouseout", this.onMouseOut, this);
13925     }
13926 };
13927
13928 // private
13929 Roo.Resizable.Handle.prototype = {
13930     afterResize : function(rz){
13931         // do nothing
13932     },
13933     // private
13934     onMouseDown : function(e){
13935         this.rz.onMouseDown(this, e);
13936     },
13937     // private
13938     onMouseOver : function(e){
13939         this.rz.handleOver(this, e);
13940     },
13941     // private
13942     onMouseOut : function(e){
13943         this.rz.handleOut(this, e);
13944     }
13945 };/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956 /**
13957  * @class Roo.Editor
13958  * @extends Roo.Component
13959  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
13960  * @constructor
13961  * Create a new Editor
13962  * @param {Roo.form.Field} field The Field object (or descendant)
13963  * @param {Object} config The config object
13964  */
13965 Roo.Editor = function(field, config){
13966     Roo.Editor.superclass.constructor.call(this, config);
13967     this.field = field;
13968     this.addEvents({
13969         /**
13970              * @event beforestartedit
13971              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13972              * false from the handler of this event.
13973              * @param {Editor} this
13974              * @param {Roo.Element} boundEl The underlying element bound to this editor
13975              * @param {Mixed} value The field value being set
13976              */
13977         "beforestartedit" : true,
13978         /**
13979              * @event startedit
13980              * Fires when this editor is displayed
13981              * @param {Roo.Element} boundEl The underlying element bound to this editor
13982              * @param {Mixed} value The starting field value
13983              */
13984         "startedit" : true,
13985         /**
13986              * @event beforecomplete
13987              * Fires after a change has been made to the field, but before the change is reflected in the underlying
13988              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
13989              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
13990              * event will not fire since no edit actually occurred.
13991              * @param {Editor} this
13992              * @param {Mixed} value The current field value
13993              * @param {Mixed} startValue The original field value
13994              */
13995         "beforecomplete" : true,
13996         /**
13997              * @event complete
13998              * Fires after editing is complete and any changed value has been written to the underlying field.
13999              * @param {Editor} this
14000              * @param {Mixed} value The current field value
14001              * @param {Mixed} startValue The original field value
14002              */
14003         "complete" : true,
14004         /**
14005          * @event specialkey
14006          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
14007          * {@link Roo.EventObject#getKey} to determine which key was pressed.
14008          * @param {Roo.form.Field} this
14009          * @param {Roo.EventObject} e The event object
14010          */
14011         "specialkey" : true
14012     });
14013 };
14014
14015 Roo.extend(Roo.Editor, Roo.Component, {
14016     /**
14017      * @cfg {Boolean/String} autosize
14018      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
14019      * or "height" to adopt the height only (defaults to false)
14020      */
14021     /**
14022      * @cfg {Boolean} revertInvalid
14023      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
14024      * validation fails (defaults to true)
14025      */
14026     /**
14027      * @cfg {Boolean} ignoreNoChange
14028      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
14029      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
14030      * will never be ignored.
14031      */
14032     /**
14033      * @cfg {Boolean} hideEl
14034      * False to keep the bound element visible while the editor is displayed (defaults to true)
14035      */
14036     /**
14037      * @cfg {Mixed} value
14038      * The data value of the underlying field (defaults to "")
14039      */
14040     value : "",
14041     /**
14042      * @cfg {String} alignment
14043      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
14044      */
14045     alignment: "c-c?",
14046     /**
14047      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
14048      * for bottom-right shadow (defaults to "frame")
14049      */
14050     shadow : "frame",
14051     /**
14052      * @cfg {Boolean} constrain True to constrain the editor to the viewport
14053      */
14054     constrain : false,
14055     /**
14056      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
14057      */
14058     completeOnEnter : false,
14059     /**
14060      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
14061      */
14062     cancelOnEsc : false,
14063     /**
14064      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
14065      */
14066     updateEl : false,
14067
14068     // private
14069     onRender : function(ct, position){
14070         this.el = new Roo.Layer({
14071             shadow: this.shadow,
14072             cls: "x-editor",
14073             parentEl : ct,
14074             shim : this.shim,
14075             shadowOffset:4,
14076             id: this.id,
14077             constrain: this.constrain
14078         });
14079         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
14080         if(this.field.msgTarget != 'title'){
14081             this.field.msgTarget = 'qtip';
14082         }
14083         this.field.render(this.el);
14084         if(Roo.isGecko){
14085             this.field.el.dom.setAttribute('autocomplete', 'off');
14086         }
14087         this.field.on("specialkey", this.onSpecialKey, this);
14088         if(this.swallowKeys){
14089             this.field.el.swallowEvent(['keydown','keypress']);
14090         }
14091         this.field.show();
14092         this.field.on("blur", this.onBlur, this);
14093         if(this.field.grow){
14094             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
14095         }
14096     },
14097
14098     onSpecialKey : function(field, e)
14099     {
14100         //Roo.log('editor onSpecialKey');
14101         if(this.completeOnEnter && e.getKey() == e.ENTER){
14102             e.stopEvent();
14103             this.completeEdit();
14104             return;
14105         }
14106         // do not fire special key otherwise it might hide close the editor...
14107         if(e.getKey() == e.ENTER){    
14108             return;
14109         }
14110         if(this.cancelOnEsc && e.getKey() == e.ESC){
14111             this.cancelEdit();
14112             return;
14113         } 
14114         this.fireEvent('specialkey', field, e);
14115     
14116     },
14117
14118     /**
14119      * Starts the editing process and shows the editor.
14120      * @param {String/HTMLElement/Element} el The element to edit
14121      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
14122       * to the innerHTML of el.
14123      */
14124     startEdit : function(el, value){
14125         if(this.editing){
14126             this.completeEdit();
14127         }
14128         this.boundEl = Roo.get(el);
14129         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
14130         if(!this.rendered){
14131             this.render(this.parentEl || document.body);
14132         }
14133         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
14134             return;
14135         }
14136         this.startValue = v;
14137         this.field.setValue(v);
14138         if(this.autoSize){
14139             var sz = this.boundEl.getSize();
14140             switch(this.autoSize){
14141                 case "width":
14142                 this.setSize(sz.width,  "");
14143                 break;
14144                 case "height":
14145                 this.setSize("",  sz.height);
14146                 break;
14147                 default:
14148                 this.setSize(sz.width,  sz.height);
14149             }
14150         }
14151         this.el.alignTo(this.boundEl, this.alignment);
14152         this.editing = true;
14153         if(Roo.QuickTips){
14154             Roo.QuickTips.disable();
14155         }
14156         this.show();
14157     },
14158
14159     /**
14160      * Sets the height and width of this editor.
14161      * @param {Number} width The new width
14162      * @param {Number} height The new height
14163      */
14164     setSize : function(w, h){
14165         this.field.setSize(w, h);
14166         if(this.el){
14167             this.el.sync();
14168         }
14169     },
14170
14171     /**
14172      * Realigns the editor to the bound field based on the current alignment config value.
14173      */
14174     realign : function(){
14175         this.el.alignTo(this.boundEl, this.alignment);
14176     },
14177
14178     /**
14179      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
14180      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
14181      */
14182     completeEdit : function(remainVisible){
14183         if(!this.editing){
14184             return;
14185         }
14186         var v = this.getValue();
14187         if(this.revertInvalid !== false && !this.field.isValid()){
14188             v = this.startValue;
14189             this.cancelEdit(true);
14190         }
14191         if(String(v) === String(this.startValue) && this.ignoreNoChange){
14192             this.editing = false;
14193             this.hide();
14194             return;
14195         }
14196         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
14197             this.editing = false;
14198             if(this.updateEl && this.boundEl){
14199                 this.boundEl.update(v);
14200             }
14201             if(remainVisible !== true){
14202                 this.hide();
14203             }
14204             this.fireEvent("complete", this, v, this.startValue);
14205         }
14206     },
14207
14208     // private
14209     onShow : function(){
14210         this.el.show();
14211         if(this.hideEl !== false){
14212             this.boundEl.hide();
14213         }
14214         this.field.show();
14215         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
14216             this.fixIEFocus = true;
14217             this.deferredFocus.defer(50, this);
14218         }else{
14219             this.field.focus();
14220         }
14221         this.fireEvent("startedit", this.boundEl, this.startValue);
14222     },
14223
14224     deferredFocus : function(){
14225         if(this.editing){
14226             this.field.focus();
14227         }
14228     },
14229
14230     /**
14231      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
14232      * reverted to the original starting value.
14233      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
14234      * cancel (defaults to false)
14235      */
14236     cancelEdit : function(remainVisible){
14237         if(this.editing){
14238             this.setValue(this.startValue);
14239             if(remainVisible !== true){
14240                 this.hide();
14241             }
14242         }
14243     },
14244
14245     // private
14246     onBlur : function(){
14247         if(this.allowBlur !== true && this.editing){
14248             this.completeEdit();
14249         }
14250     },
14251
14252     // private
14253     onHide : function(){
14254         if(this.editing){
14255             this.completeEdit();
14256             return;
14257         }
14258         this.field.blur();
14259         if(this.field.collapse){
14260             this.field.collapse();
14261         }
14262         this.el.hide();
14263         if(this.hideEl !== false){
14264             this.boundEl.show();
14265         }
14266         if(Roo.QuickTips){
14267             Roo.QuickTips.enable();
14268         }
14269     },
14270
14271     /**
14272      * Sets the data value of the editor
14273      * @param {Mixed} value Any valid value supported by the underlying field
14274      */
14275     setValue : function(v){
14276         this.field.setValue(v);
14277     },
14278
14279     /**
14280      * Gets the data value of the editor
14281      * @return {Mixed} The data value
14282      */
14283     getValue : function(){
14284         return this.field.getValue();
14285     }
14286 });/*
14287  * Based on:
14288  * Ext JS Library 1.1.1
14289  * Copyright(c) 2006-2007, Ext JS, LLC.
14290  *
14291  * Originally Released Under LGPL - original licence link has changed is not relivant.
14292  *
14293  * Fork - LGPL
14294  * <script type="text/javascript">
14295  */
14296  
14297 /**
14298  * @class Roo.BasicDialog
14299  * @extends Roo.util.Observable
14300  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
14301  * <pre><code>
14302 var dlg = new Roo.BasicDialog("my-dlg", {
14303     height: 200,
14304     width: 300,
14305     minHeight: 100,
14306     minWidth: 150,
14307     modal: true,
14308     proxyDrag: true,
14309     shadow: true
14310 });
14311 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
14312 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
14313 dlg.addButton('Cancel', dlg.hide, dlg);
14314 dlg.show();
14315 </code></pre>
14316   <b>A Dialog should always be a direct child of the body element.</b>
14317  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
14318  * @cfg {String} title Default text to display in the title bar (defaults to null)
14319  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14320  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
14321  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
14322  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
14323  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
14324  * (defaults to null with no animation)
14325  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
14326  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
14327  * property for valid values (defaults to 'all')
14328  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
14329  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
14330  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
14331  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
14332  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
14333  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
14334  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
14335  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
14336  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
14337  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
14338  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
14339  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
14340  * draggable = true (defaults to false)
14341  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
14342  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
14343  * shadow (defaults to false)
14344  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
14345  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
14346  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
14347  * @cfg {Array} buttons Array of buttons
14348  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
14349  * @constructor
14350  * Create a new BasicDialog.
14351  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
14352  * @param {Object} config Configuration options
14353  */
14354 Roo.BasicDialog = function(el, config){
14355     this.el = Roo.get(el);
14356     var dh = Roo.DomHelper;
14357     if(!this.el && config && config.autoCreate){
14358         if(typeof config.autoCreate == "object"){
14359             if(!config.autoCreate.id){
14360                 config.autoCreate.id = el;
14361             }
14362             this.el = dh.append(document.body,
14363                         config.autoCreate, true);
14364         }else{
14365             this.el = dh.append(document.body,
14366                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
14367         }
14368     }
14369     el = this.el;
14370     el.setDisplayed(true);
14371     el.hide = this.hideAction;
14372     this.id = el.id;
14373     el.addClass("x-dlg");
14374
14375     Roo.apply(this, config);
14376
14377     this.proxy = el.createProxy("x-dlg-proxy");
14378     this.proxy.hide = this.hideAction;
14379     this.proxy.setOpacity(.5);
14380     this.proxy.hide();
14381
14382     if(config.width){
14383         el.setWidth(config.width);
14384     }
14385     if(config.height){
14386         el.setHeight(config.height);
14387     }
14388     this.size = el.getSize();
14389     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
14390         this.xy = [config.x,config.y];
14391     }else{
14392         this.xy = el.getCenterXY(true);
14393     }
14394     /** The header element @type Roo.Element */
14395     this.header = el.child("> .x-dlg-hd");
14396     /** The body element @type Roo.Element */
14397     this.body = el.child("> .x-dlg-bd");
14398     /** The footer element @type Roo.Element */
14399     this.footer = el.child("> .x-dlg-ft");
14400
14401     if(!this.header){
14402         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
14403     }
14404     if(!this.body){
14405         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
14406     }
14407
14408     this.header.unselectable();
14409     if(this.title){
14410         this.header.update(this.title);
14411     }
14412     // this element allows the dialog to be focused for keyboard event
14413     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
14414     this.focusEl.swallowEvent("click", true);
14415
14416     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
14417
14418     // wrap the body and footer for special rendering
14419     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
14420     if(this.footer){
14421         this.bwrap.dom.appendChild(this.footer.dom);
14422     }
14423
14424     this.bg = this.el.createChild({
14425         tag: "div", cls:"x-dlg-bg",
14426         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
14427     });
14428     this.centerBg = this.bg.child("div.x-dlg-bg-center");
14429
14430
14431     if(this.autoScroll !== false && !this.autoTabs){
14432         this.body.setStyle("overflow", "auto");
14433     }
14434
14435     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
14436
14437     if(this.closable !== false){
14438         this.el.addClass("x-dlg-closable");
14439         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
14440         this.close.on("click", this.closeClick, this);
14441         this.close.addClassOnOver("x-dlg-close-over");
14442     }
14443     if(this.collapsible !== false){
14444         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
14445         this.collapseBtn.on("click", this.collapseClick, this);
14446         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
14447         this.header.on("dblclick", this.collapseClick, this);
14448     }
14449     if(this.resizable !== false){
14450         this.el.addClass("x-dlg-resizable");
14451         this.resizer = new Roo.Resizable(el, {
14452             minWidth: this.minWidth || 80,
14453             minHeight:this.minHeight || 80,
14454             handles: this.resizeHandles || "all",
14455             pinned: true
14456         });
14457         this.resizer.on("beforeresize", this.beforeResize, this);
14458         this.resizer.on("resize", this.onResize, this);
14459     }
14460     if(this.draggable !== false){
14461         el.addClass("x-dlg-draggable");
14462         if (!this.proxyDrag) {
14463             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
14464         }
14465         else {
14466             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
14467         }
14468         dd.setHandleElId(this.header.id);
14469         dd.endDrag = this.endMove.createDelegate(this);
14470         dd.startDrag = this.startMove.createDelegate(this);
14471         dd.onDrag = this.onDrag.createDelegate(this);
14472         dd.scroll = false;
14473         this.dd = dd;
14474     }
14475     if(this.modal){
14476         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
14477         this.mask.enableDisplayMode("block");
14478         this.mask.hide();
14479         this.el.addClass("x-dlg-modal");
14480     }
14481     if(this.shadow){
14482         this.shadow = new Roo.Shadow({
14483             mode : typeof this.shadow == "string" ? this.shadow : "sides",
14484             offset : this.shadowOffset
14485         });
14486     }else{
14487         this.shadowOffset = 0;
14488     }
14489     if(Roo.useShims && this.shim !== false){
14490         this.shim = this.el.createShim();
14491         this.shim.hide = this.hideAction;
14492         this.shim.hide();
14493     }else{
14494         this.shim = false;
14495     }
14496     if(this.autoTabs){
14497         this.initTabs();
14498     }
14499     if (this.buttons) { 
14500         var bts= this.buttons;
14501         this.buttons = [];
14502         Roo.each(bts, function(b) {
14503             this.addButton(b);
14504         }, this);
14505     }
14506     
14507     
14508     this.addEvents({
14509         /**
14510          * @event keydown
14511          * Fires when a key is pressed
14512          * @param {Roo.BasicDialog} this
14513          * @param {Roo.EventObject} e
14514          */
14515         "keydown" : true,
14516         /**
14517          * @event move
14518          * Fires when this dialog is moved by the user.
14519          * @param {Roo.BasicDialog} this
14520          * @param {Number} x The new page X
14521          * @param {Number} y The new page Y
14522          */
14523         "move" : true,
14524         /**
14525          * @event resize
14526          * Fires when this dialog is resized by the user.
14527          * @param {Roo.BasicDialog} this
14528          * @param {Number} width The new width
14529          * @param {Number} height The new height
14530          */
14531         "resize" : true,
14532         /**
14533          * @event beforehide
14534          * Fires before this dialog is hidden.
14535          * @param {Roo.BasicDialog} this
14536          */
14537         "beforehide" : true,
14538         /**
14539          * @event hide
14540          * Fires when this dialog is hidden.
14541          * @param {Roo.BasicDialog} this
14542          */
14543         "hide" : true,
14544         /**
14545          * @event beforeshow
14546          * Fires before this dialog is shown.
14547          * @param {Roo.BasicDialog} this
14548          */
14549         "beforeshow" : true,
14550         /**
14551          * @event show
14552          * Fires when this dialog is shown.
14553          * @param {Roo.BasicDialog} this
14554          */
14555         "show" : true
14556     });
14557     el.on("keydown", this.onKeyDown, this);
14558     el.on("mousedown", this.toFront, this);
14559     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
14560     this.el.hide();
14561     Roo.DialogManager.register(this);
14562     Roo.BasicDialog.superclass.constructor.call(this);
14563 };
14564
14565 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
14566     shadowOffset: Roo.isIE ? 6 : 5,
14567     minHeight: 80,
14568     minWidth: 200,
14569     minButtonWidth: 75,
14570     defaultButton: null,
14571     buttonAlign: "right",
14572     tabTag: 'div',
14573     firstShow: true,
14574
14575     /**
14576      * Sets the dialog title text
14577      * @param {String} text The title text to display
14578      * @return {Roo.BasicDialog} this
14579      */
14580     setTitle : function(text){
14581         this.header.update(text);
14582         return this;
14583     },
14584
14585     // private
14586     closeClick : function(){
14587         this.hide();
14588     },
14589
14590     // private
14591     collapseClick : function(){
14592         this[this.collapsed ? "expand" : "collapse"]();
14593     },
14594
14595     /**
14596      * Collapses the dialog to its minimized state (only the title bar is visible).
14597      * Equivalent to the user clicking the collapse dialog button.
14598      */
14599     collapse : function(){
14600         if(!this.collapsed){
14601             this.collapsed = true;
14602             this.el.addClass("x-dlg-collapsed");
14603             this.restoreHeight = this.el.getHeight();
14604             this.resizeTo(this.el.getWidth(), this.header.getHeight());
14605         }
14606     },
14607
14608     /**
14609      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
14610      * clicking the expand dialog button.
14611      */
14612     expand : function(){
14613         if(this.collapsed){
14614             this.collapsed = false;
14615             this.el.removeClass("x-dlg-collapsed");
14616             this.resizeTo(this.el.getWidth(), this.restoreHeight);
14617         }
14618     },
14619
14620     /**
14621      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
14622      * @return {Roo.TabPanel} The tabs component
14623      */
14624     initTabs : function(){
14625         var tabs = this.getTabs();
14626         while(tabs.getTab(0)){
14627             tabs.removeTab(0);
14628         }
14629         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
14630             var dom = el.dom;
14631             tabs.addTab(Roo.id(dom), dom.title);
14632             dom.title = "";
14633         });
14634         tabs.activate(0);
14635         return tabs;
14636     },
14637
14638     // private
14639     beforeResize : function(){
14640         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
14641     },
14642
14643     // private
14644     onResize : function(){
14645         this.refreshSize();
14646         this.syncBodyHeight();
14647         this.adjustAssets();
14648         this.focus();
14649         this.fireEvent("resize", this, this.size.width, this.size.height);
14650     },
14651
14652     // private
14653     onKeyDown : function(e){
14654         if(this.isVisible()){
14655             this.fireEvent("keydown", this, e);
14656         }
14657     },
14658
14659     /**
14660      * Resizes the dialog.
14661      * @param {Number} width
14662      * @param {Number} height
14663      * @return {Roo.BasicDialog} this
14664      */
14665     resizeTo : function(width, height){
14666         this.el.setSize(width, height);
14667         this.size = {width: width, height: height};
14668         this.syncBodyHeight();
14669         if(this.fixedcenter){
14670             this.center();
14671         }
14672         if(this.isVisible()){
14673             this.constrainXY();
14674             this.adjustAssets();
14675         }
14676         this.fireEvent("resize", this, width, height);
14677         return this;
14678     },
14679
14680
14681     /**
14682      * Resizes the dialog to fit the specified content size.
14683      * @param {Number} width
14684      * @param {Number} height
14685      * @return {Roo.BasicDialog} this
14686      */
14687     setContentSize : function(w, h){
14688         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
14689         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
14690         //if(!this.el.isBorderBox()){
14691             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
14692             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
14693         //}
14694         if(this.tabs){
14695             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
14696             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
14697         }
14698         this.resizeTo(w, h);
14699         return this;
14700     },
14701
14702     /**
14703      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
14704      * executed in response to a particular key being pressed while the dialog is active.
14705      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
14706      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14707      * @param {Function} fn The function to call
14708      * @param {Object} scope (optional) The scope of the function
14709      * @return {Roo.BasicDialog} this
14710      */
14711     addKeyListener : function(key, fn, scope){
14712         var keyCode, shift, ctrl, alt;
14713         if(typeof key == "object" && !(key instanceof Array)){
14714             keyCode = key["key"];
14715             shift = key["shift"];
14716             ctrl = key["ctrl"];
14717             alt = key["alt"];
14718         }else{
14719             keyCode = key;
14720         }
14721         var handler = function(dlg, e){
14722             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14723                 var k = e.getKey();
14724                 if(keyCode instanceof Array){
14725                     for(var i = 0, len = keyCode.length; i < len; i++){
14726                         if(keyCode[i] == k){
14727                           fn.call(scope || window, dlg, k, e);
14728                           return;
14729                         }
14730                     }
14731                 }else{
14732                     if(k == keyCode){
14733                         fn.call(scope || window, dlg, k, e);
14734                     }
14735                 }
14736             }
14737         };
14738         this.on("keydown", handler);
14739         return this;
14740     },
14741
14742     /**
14743      * Returns the TabPanel component (creates it if it doesn't exist).
14744      * Note: If you wish to simply check for the existence of tabs without creating them,
14745      * check for a null 'tabs' property.
14746      * @return {Roo.TabPanel} The tabs component
14747      */
14748     getTabs : function(){
14749         if(!this.tabs){
14750             this.el.addClass("x-dlg-auto-tabs");
14751             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
14752             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
14753         }
14754         return this.tabs;
14755     },
14756
14757     /**
14758      * Adds a button to the footer section of the dialog.
14759      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
14760      * object or a valid Roo.DomHelper element config
14761      * @param {Function} handler The function called when the button is clicked
14762      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
14763      * @return {Roo.Button} The new button
14764      */
14765     addButton : function(config, handler, scope){
14766         var dh = Roo.DomHelper;
14767         if(!this.footer){
14768             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
14769         }
14770         if(!this.btnContainer){
14771             var tb = this.footer.createChild({
14772
14773                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
14774                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
14775             }, null, true);
14776             this.btnContainer = tb.firstChild.firstChild.firstChild;
14777         }
14778         var bconfig = {
14779             handler: handler,
14780             scope: scope,
14781             minWidth: this.minButtonWidth,
14782             hideParent:true
14783         };
14784         if(typeof config == "string"){
14785             bconfig.text = config;
14786         }else{
14787             if(config.tag){
14788                 bconfig.dhconfig = config;
14789             }else{
14790                 Roo.apply(bconfig, config);
14791             }
14792         }
14793         var fc = false;
14794         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
14795             bconfig.position = Math.max(0, bconfig.position);
14796             fc = this.btnContainer.childNodes[bconfig.position];
14797         }
14798          
14799         var btn = new Roo.Button(
14800             fc ? 
14801                 this.btnContainer.insertBefore(document.createElement("td"),fc)
14802                 : this.btnContainer.appendChild(document.createElement("td")),
14803             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
14804             bconfig
14805         );
14806         this.syncBodyHeight();
14807         if(!this.buttons){
14808             /**
14809              * Array of all the buttons that have been added to this dialog via addButton
14810              * @type Array
14811              */
14812             this.buttons = [];
14813         }
14814         this.buttons.push(btn);
14815         return btn;
14816     },
14817
14818     /**
14819      * Sets the default button to be focused when the dialog is displayed.
14820      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
14821      * @return {Roo.BasicDialog} this
14822      */
14823     setDefaultButton : function(btn){
14824         this.defaultButton = btn;
14825         return this;
14826     },
14827
14828     // private
14829     getHeaderFooterHeight : function(safe){
14830         var height = 0;
14831         if(this.header){
14832            height += this.header.getHeight();
14833         }
14834         if(this.footer){
14835            var fm = this.footer.getMargins();
14836             height += (this.footer.getHeight()+fm.top+fm.bottom);
14837         }
14838         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
14839         height += this.centerBg.getPadding("tb");
14840         return height;
14841     },
14842
14843     // private
14844     syncBodyHeight : function(){
14845         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
14846         var height = this.size.height - this.getHeaderFooterHeight(false);
14847         bd.setHeight(height-bd.getMargins("tb"));
14848         var hh = this.header.getHeight();
14849         var h = this.size.height-hh;
14850         cb.setHeight(h);
14851         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
14852         bw.setHeight(h-cb.getPadding("tb"));
14853         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
14854         bd.setWidth(bw.getWidth(true));
14855         if(this.tabs){
14856             this.tabs.syncHeight();
14857             if(Roo.isIE){
14858                 this.tabs.el.repaint();
14859             }
14860         }
14861     },
14862
14863     /**
14864      * Restores the previous state of the dialog if Roo.state is configured.
14865      * @return {Roo.BasicDialog} this
14866      */
14867     restoreState : function(){
14868         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
14869         if(box && box.width){
14870             this.xy = [box.x, box.y];
14871             this.resizeTo(box.width, box.height);
14872         }
14873         return this;
14874     },
14875
14876     // private
14877     beforeShow : function(){
14878         this.expand();
14879         if(this.fixedcenter){
14880             this.xy = this.el.getCenterXY(true);
14881         }
14882         if(this.modal){
14883             Roo.get(document.body).addClass("x-body-masked");
14884             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14885             this.mask.show();
14886         }
14887         this.constrainXY();
14888     },
14889
14890     // private
14891     animShow : function(){
14892         var b = Roo.get(this.animateTarget).getBox();
14893         this.proxy.setSize(b.width, b.height);
14894         this.proxy.setLocation(b.x, b.y);
14895         this.proxy.show();
14896         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
14897                     true, .35, this.showEl.createDelegate(this));
14898     },
14899
14900     /**
14901      * Shows the dialog.
14902      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
14903      * @return {Roo.BasicDialog} this
14904      */
14905     show : function(animateTarget){
14906         if (this.fireEvent("beforeshow", this) === false){
14907             return;
14908         }
14909         if(this.syncHeightBeforeShow){
14910             this.syncBodyHeight();
14911         }else if(this.firstShow){
14912             this.firstShow = false;
14913             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
14914         }
14915         this.animateTarget = animateTarget || this.animateTarget;
14916         if(!this.el.isVisible()){
14917             this.beforeShow();
14918             if(this.animateTarget && Roo.get(this.animateTarget)){
14919                 this.animShow();
14920             }else{
14921                 this.showEl();
14922             }
14923         }
14924         return this;
14925     },
14926
14927     // private
14928     showEl : function(){
14929         this.proxy.hide();
14930         this.el.setXY(this.xy);
14931         this.el.show();
14932         this.adjustAssets(true);
14933         this.toFront();
14934         this.focus();
14935         // IE peekaboo bug - fix found by Dave Fenwick
14936         if(Roo.isIE){
14937             this.el.repaint();
14938         }
14939         this.fireEvent("show", this);
14940     },
14941
14942     /**
14943      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
14944      * dialog itself will receive focus.
14945      */
14946     focus : function(){
14947         if(this.defaultButton){
14948             this.defaultButton.focus();
14949         }else{
14950             this.focusEl.focus();
14951         }
14952     },
14953
14954     // private
14955     constrainXY : function(){
14956         if(this.constraintoviewport !== false){
14957             if(!this.viewSize){
14958                 if(this.container){
14959                     var s = this.container.getSize();
14960                     this.viewSize = [s.width, s.height];
14961                 }else{
14962                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
14963                 }
14964             }
14965             var s = Roo.get(this.container||document).getScroll();
14966
14967             var x = this.xy[0], y = this.xy[1];
14968             var w = this.size.width, h = this.size.height;
14969             var vw = this.viewSize[0], vh = this.viewSize[1];
14970             // only move it if it needs it
14971             var moved = false;
14972             // first validate right/bottom
14973             if(x + w > vw+s.left){
14974                 x = vw - w;
14975                 moved = true;
14976             }
14977             if(y + h > vh+s.top){
14978                 y = vh - h;
14979                 moved = true;
14980             }
14981             // then make sure top/left isn't negative
14982             if(x < s.left){
14983                 x = s.left;
14984                 moved = true;
14985             }
14986             if(y < s.top){
14987                 y = s.top;
14988                 moved = true;
14989             }
14990             if(moved){
14991                 // cache xy
14992                 this.xy = [x, y];
14993                 if(this.isVisible()){
14994                     this.el.setLocation(x, y);
14995                     this.adjustAssets();
14996                 }
14997             }
14998         }
14999     },
15000
15001     // private
15002     onDrag : function(){
15003         if(!this.proxyDrag){
15004             this.xy = this.el.getXY();
15005             this.adjustAssets();
15006         }
15007     },
15008
15009     // private
15010     adjustAssets : function(doShow){
15011         var x = this.xy[0], y = this.xy[1];
15012         var w = this.size.width, h = this.size.height;
15013         if(doShow === true){
15014             if(this.shadow){
15015                 this.shadow.show(this.el);
15016             }
15017             if(this.shim){
15018                 this.shim.show();
15019             }
15020         }
15021         if(this.shadow && this.shadow.isVisible()){
15022             this.shadow.show(this.el);
15023         }
15024         if(this.shim && this.shim.isVisible()){
15025             this.shim.setBounds(x, y, w, h);
15026         }
15027     },
15028
15029     // private
15030     adjustViewport : function(w, h){
15031         if(!w || !h){
15032             w = Roo.lib.Dom.getViewWidth();
15033             h = Roo.lib.Dom.getViewHeight();
15034         }
15035         // cache the size
15036         this.viewSize = [w, h];
15037         if(this.modal && this.mask.isVisible()){
15038             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
15039             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15040         }
15041         if(this.isVisible()){
15042             this.constrainXY();
15043         }
15044     },
15045
15046     /**
15047      * Destroys this dialog and all its supporting elements (including any tabs, shim,
15048      * shadow, proxy, mask, etc.)  Also removes all event listeners.
15049      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
15050      */
15051     destroy : function(removeEl){
15052         if(this.isVisible()){
15053             this.animateTarget = null;
15054             this.hide();
15055         }
15056         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
15057         if(this.tabs){
15058             this.tabs.destroy(removeEl);
15059         }
15060         Roo.destroy(
15061              this.shim,
15062              this.proxy,
15063              this.resizer,
15064              this.close,
15065              this.mask
15066         );
15067         if(this.dd){
15068             this.dd.unreg();
15069         }
15070         if(this.buttons){
15071            for(var i = 0, len = this.buttons.length; i < len; i++){
15072                this.buttons[i].destroy();
15073            }
15074         }
15075         this.el.removeAllListeners();
15076         if(removeEl === true){
15077             this.el.update("");
15078             this.el.remove();
15079         }
15080         Roo.DialogManager.unregister(this);
15081     },
15082
15083     // private
15084     startMove : function(){
15085         if(this.proxyDrag){
15086             this.proxy.show();
15087         }
15088         if(this.constraintoviewport !== false){
15089             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
15090         }
15091     },
15092
15093     // private
15094     endMove : function(){
15095         if(!this.proxyDrag){
15096             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
15097         }else{
15098             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
15099             this.proxy.hide();
15100         }
15101         this.refreshSize();
15102         this.adjustAssets();
15103         this.focus();
15104         this.fireEvent("move", this, this.xy[0], this.xy[1]);
15105     },
15106
15107     /**
15108      * Brings this dialog to the front of any other visible dialogs
15109      * @return {Roo.BasicDialog} this
15110      */
15111     toFront : function(){
15112         Roo.DialogManager.bringToFront(this);
15113         return this;
15114     },
15115
15116     /**
15117      * Sends this dialog to the back (under) of any other visible dialogs
15118      * @return {Roo.BasicDialog} this
15119      */
15120     toBack : function(){
15121         Roo.DialogManager.sendToBack(this);
15122         return this;
15123     },
15124
15125     /**
15126      * Centers this dialog in the viewport
15127      * @return {Roo.BasicDialog} this
15128      */
15129     center : function(){
15130         var xy = this.el.getCenterXY(true);
15131         this.moveTo(xy[0], xy[1]);
15132         return this;
15133     },
15134
15135     /**
15136      * Moves the dialog's top-left corner to the specified point
15137      * @param {Number} x
15138      * @param {Number} y
15139      * @return {Roo.BasicDialog} this
15140      */
15141     moveTo : function(x, y){
15142         this.xy = [x,y];
15143         if(this.isVisible()){
15144             this.el.setXY(this.xy);
15145             this.adjustAssets();
15146         }
15147         return this;
15148     },
15149
15150     /**
15151      * Aligns the dialog to the specified element
15152      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15153      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
15154      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15155      * @return {Roo.BasicDialog} this
15156      */
15157     alignTo : function(element, position, offsets){
15158         this.xy = this.el.getAlignToXY(element, position, offsets);
15159         if(this.isVisible()){
15160             this.el.setXY(this.xy);
15161             this.adjustAssets();
15162         }
15163         return this;
15164     },
15165
15166     /**
15167      * Anchors an element to another element and realigns it when the window is resized.
15168      * @param {String/HTMLElement/Roo.Element} element The element to align to.
15169      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
15170      * @param {Array} offsets (optional) Offset the positioning by [x, y]
15171      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
15172      * is a number, it is used as the buffer delay (defaults to 50ms).
15173      * @return {Roo.BasicDialog} this
15174      */
15175     anchorTo : function(el, alignment, offsets, monitorScroll){
15176         var action = function(){
15177             this.alignTo(el, alignment, offsets);
15178         };
15179         Roo.EventManager.onWindowResize(action, this);
15180         var tm = typeof monitorScroll;
15181         if(tm != 'undefined'){
15182             Roo.EventManager.on(window, 'scroll', action, this,
15183                 {buffer: tm == 'number' ? monitorScroll : 50});
15184         }
15185         action.call(this);
15186         return this;
15187     },
15188
15189     /**
15190      * Returns true if the dialog is visible
15191      * @return {Boolean}
15192      */
15193     isVisible : function(){
15194         return this.el.isVisible();
15195     },
15196
15197     // private
15198     animHide : function(callback){
15199         var b = Roo.get(this.animateTarget).getBox();
15200         this.proxy.show();
15201         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
15202         this.el.hide();
15203         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
15204                     this.hideEl.createDelegate(this, [callback]));
15205     },
15206
15207     /**
15208      * Hides the dialog.
15209      * @param {Function} callback (optional) Function to call when the dialog is hidden
15210      * @return {Roo.BasicDialog} this
15211      */
15212     hide : function(callback){
15213         if (this.fireEvent("beforehide", this) === false){
15214             return;
15215         }
15216         if(this.shadow){
15217             this.shadow.hide();
15218         }
15219         if(this.shim) {
15220           this.shim.hide();
15221         }
15222         // sometimes animateTarget seems to get set.. causing problems...
15223         // this just double checks..
15224         if(this.animateTarget && Roo.get(this.animateTarget)) {
15225            this.animHide(callback);
15226         }else{
15227             this.el.hide();
15228             this.hideEl(callback);
15229         }
15230         return this;
15231     },
15232
15233     // private
15234     hideEl : function(callback){
15235         this.proxy.hide();
15236         if(this.modal){
15237             this.mask.hide();
15238             Roo.get(document.body).removeClass("x-body-masked");
15239         }
15240         this.fireEvent("hide", this);
15241         if(typeof callback == "function"){
15242             callback();
15243         }
15244     },
15245
15246     // private
15247     hideAction : function(){
15248         this.setLeft("-10000px");
15249         this.setTop("-10000px");
15250         this.setStyle("visibility", "hidden");
15251     },
15252
15253     // private
15254     refreshSize : function(){
15255         this.size = this.el.getSize();
15256         this.xy = this.el.getXY();
15257         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
15258     },
15259
15260     // private
15261     // z-index is managed by the DialogManager and may be overwritten at any time
15262     setZIndex : function(index){
15263         if(this.modal){
15264             this.mask.setStyle("z-index", index);
15265         }
15266         if(this.shim){
15267             this.shim.setStyle("z-index", ++index);
15268         }
15269         if(this.shadow){
15270             this.shadow.setZIndex(++index);
15271         }
15272         this.el.setStyle("z-index", ++index);
15273         if(this.proxy){
15274             this.proxy.setStyle("z-index", ++index);
15275         }
15276         if(this.resizer){
15277             this.resizer.proxy.setStyle("z-index", ++index);
15278         }
15279
15280         this.lastZIndex = index;
15281     },
15282
15283     /**
15284      * Returns the element for this dialog
15285      * @return {Roo.Element} The underlying dialog Element
15286      */
15287     getEl : function(){
15288         return this.el;
15289     }
15290 });
15291
15292 /**
15293  * @class Roo.DialogManager
15294  * Provides global access to BasicDialogs that have been created and
15295  * support for z-indexing (layering) multiple open dialogs.
15296  */
15297 Roo.DialogManager = function(){
15298     var list = {};
15299     var accessList = [];
15300     var front = null;
15301
15302     // private
15303     var sortDialogs = function(d1, d2){
15304         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
15305     };
15306
15307     // private
15308     var orderDialogs = function(){
15309         accessList.sort(sortDialogs);
15310         var seed = Roo.DialogManager.zseed;
15311         for(var i = 0, len = accessList.length; i < len; i++){
15312             var dlg = accessList[i];
15313             if(dlg){
15314                 dlg.setZIndex(seed + (i*10));
15315             }
15316         }
15317     };
15318
15319     return {
15320         /**
15321          * The starting z-index for BasicDialogs (defaults to 9000)
15322          * @type Number The z-index value
15323          */
15324         zseed : 9000,
15325
15326         // private
15327         register : function(dlg){
15328             list[dlg.id] = dlg;
15329             accessList.push(dlg);
15330         },
15331
15332         // private
15333         unregister : function(dlg){
15334             delete list[dlg.id];
15335             var i=0;
15336             var len=0;
15337             if(!accessList.indexOf){
15338                 for(  i = 0, len = accessList.length; i < len; i++){
15339                     if(accessList[i] == dlg){
15340                         accessList.splice(i, 1);
15341                         return;
15342                     }
15343                 }
15344             }else{
15345                  i = accessList.indexOf(dlg);
15346                 if(i != -1){
15347                     accessList.splice(i, 1);
15348                 }
15349             }
15350         },
15351
15352         /**
15353          * Gets a registered dialog by id
15354          * @param {String/Object} id The id of the dialog or a dialog
15355          * @return {Roo.BasicDialog} this
15356          */
15357         get : function(id){
15358             return typeof id == "object" ? id : list[id];
15359         },
15360
15361         /**
15362          * Brings the specified dialog to the front
15363          * @param {String/Object} dlg The id of the dialog or a dialog
15364          * @return {Roo.BasicDialog} this
15365          */
15366         bringToFront : function(dlg){
15367             dlg = this.get(dlg);
15368             if(dlg != front){
15369                 front = dlg;
15370                 dlg._lastAccess = new Date().getTime();
15371                 orderDialogs();
15372             }
15373             return dlg;
15374         },
15375
15376         /**
15377          * Sends the specified dialog to the back
15378          * @param {String/Object} dlg The id of the dialog or a dialog
15379          * @return {Roo.BasicDialog} this
15380          */
15381         sendToBack : function(dlg){
15382             dlg = this.get(dlg);
15383             dlg._lastAccess = -(new Date().getTime());
15384             orderDialogs();
15385             return dlg;
15386         },
15387
15388         /**
15389          * Hides all dialogs
15390          */
15391         hideAll : function(){
15392             for(var id in list){
15393                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
15394                     list[id].hide();
15395                 }
15396             }
15397         }
15398     };
15399 }();
15400
15401 /**
15402  * @class Roo.LayoutDialog
15403  * @extends Roo.BasicDialog
15404  * Dialog which provides adjustments for working with a layout in a Dialog.
15405  * Add your necessary layout config options to the dialog's config.<br>
15406  * Example usage (including a nested layout):
15407  * <pre><code>
15408 if(!dialog){
15409     dialog = new Roo.LayoutDialog("download-dlg", {
15410         modal: true,
15411         width:600,
15412         height:450,
15413         shadow:true,
15414         minWidth:500,
15415         minHeight:350,
15416         autoTabs:true,
15417         proxyDrag:true,
15418         // layout config merges with the dialog config
15419         center:{
15420             tabPosition: "top",
15421             alwaysShowTabs: true
15422         }
15423     });
15424     dialog.addKeyListener(27, dialog.hide, dialog);
15425     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
15426     dialog.addButton("Build It!", this.getDownload, this);
15427
15428     // we can even add nested layouts
15429     var innerLayout = new Roo.BorderLayout("dl-inner", {
15430         east: {
15431             initialSize: 200,
15432             autoScroll:true,
15433             split:true
15434         },
15435         center: {
15436             autoScroll:true
15437         }
15438     });
15439     innerLayout.beginUpdate();
15440     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
15441     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
15442     innerLayout.endUpdate(true);
15443
15444     var layout = dialog.getLayout();
15445     layout.beginUpdate();
15446     layout.add("center", new Roo.ContentPanel("standard-panel",
15447                         {title: "Download the Source", fitToFrame:true}));
15448     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
15449                {title: "Build your own roo.js"}));
15450     layout.getRegion("center").showPanel(sp);
15451     layout.endUpdate();
15452 }
15453 </code></pre>
15454     * @constructor
15455     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
15456     * @param {Object} config configuration options
15457   */
15458 Roo.LayoutDialog = function(el, cfg){
15459     
15460     var config=  cfg;
15461     if (typeof(cfg) == 'undefined') {
15462         config = Roo.apply({}, el);
15463         // not sure why we use documentElement here.. - it should always be body.
15464         // IE7 borks horribly if we use documentElement.
15465         // webkit also does not like documentElement - it creates a body element...
15466         el = Roo.get( document.body || document.documentElement ).createChild();
15467         //config.autoCreate = true;
15468     }
15469     
15470     
15471     config.autoTabs = false;
15472     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
15473     this.body.setStyle({overflow:"hidden", position:"relative"});
15474     this.layout = new Roo.BorderLayout(this.body.dom, config);
15475     this.layout.monitorWindowResize = false;
15476     this.el.addClass("x-dlg-auto-layout");
15477     // fix case when center region overwrites center function
15478     this.center = Roo.BasicDialog.prototype.center;
15479     this.on("show", this.layout.layout, this.layout, true);
15480     if (config.items) {
15481         var xitems = config.items;
15482         delete config.items;
15483         Roo.each(xitems, this.addxtype, this);
15484     }
15485     
15486     
15487 };
15488 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
15489     /**
15490      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
15491      * @deprecated
15492      */
15493     endUpdate : function(){
15494         this.layout.endUpdate();
15495     },
15496
15497     /**
15498      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
15499      *  @deprecated
15500      */
15501     beginUpdate : function(){
15502         this.layout.beginUpdate();
15503     },
15504
15505     /**
15506      * Get the BorderLayout for this dialog
15507      * @return {Roo.BorderLayout}
15508      */
15509     getLayout : function(){
15510         return this.layout;
15511     },
15512
15513     showEl : function(){
15514         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
15515         if(Roo.isIE7){
15516             this.layout.layout();
15517         }
15518     },
15519
15520     // private
15521     // Use the syncHeightBeforeShow config option to control this automatically
15522     syncBodyHeight : function(){
15523         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
15524         if(this.layout){this.layout.layout();}
15525     },
15526     
15527       /**
15528      * Add an xtype element (actually adds to the layout.)
15529      * @return {Object} xdata xtype object data.
15530      */
15531     
15532     addxtype : function(c) {
15533         return this.layout.addxtype(c);
15534     }
15535 });/*
15536  * Based on:
15537  * Ext JS Library 1.1.1
15538  * Copyright(c) 2006-2007, Ext JS, LLC.
15539  *
15540  * Originally Released Under LGPL - original licence link has changed is not relivant.
15541  *
15542  * Fork - LGPL
15543  * <script type="text/javascript">
15544  */
15545  
15546 /**
15547  * @class Roo.MessageBox
15548  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
15549  * Example usage:
15550  *<pre><code>
15551 // Basic alert:
15552 Roo.Msg.alert('Status', 'Changes saved successfully.');
15553
15554 // Prompt for user data:
15555 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
15556     if (btn == 'ok'){
15557         // process text value...
15558     }
15559 });
15560
15561 // Show a dialog using config options:
15562 Roo.Msg.show({
15563    title:'Save Changes?',
15564    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
15565    buttons: Roo.Msg.YESNOCANCEL,
15566    fn: processResult,
15567    animEl: 'elId'
15568 });
15569 </code></pre>
15570  * @singleton
15571  */
15572 Roo.MessageBox = function(){
15573     var dlg, opt, mask, waitTimer;
15574     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
15575     var buttons, activeTextEl, bwidth;
15576
15577     // private
15578     var handleButton = function(button){
15579         dlg.hide();
15580         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
15581     };
15582
15583     // private
15584     var handleHide = function(){
15585         if(opt && opt.cls){
15586             dlg.el.removeClass(opt.cls);
15587         }
15588         if(waitTimer){
15589             Roo.TaskMgr.stop(waitTimer);
15590             waitTimer = null;
15591         }
15592     };
15593
15594     // private
15595     var updateButtons = function(b){
15596         var width = 0;
15597         if(!b){
15598             buttons["ok"].hide();
15599             buttons["cancel"].hide();
15600             buttons["yes"].hide();
15601             buttons["no"].hide();
15602             dlg.footer.dom.style.display = 'none';
15603             return width;
15604         }
15605         dlg.footer.dom.style.display = '';
15606         for(var k in buttons){
15607             if(typeof buttons[k] != "function"){
15608                 if(b[k]){
15609                     buttons[k].show();
15610                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
15611                     width += buttons[k].el.getWidth()+15;
15612                 }else{
15613                     buttons[k].hide();
15614                 }
15615             }
15616         }
15617         return width;
15618     };
15619
15620     // private
15621     var handleEsc = function(d, k, e){
15622         if(opt && opt.closable !== false){
15623             dlg.hide();
15624         }
15625         if(e){
15626             e.stopEvent();
15627         }
15628     };
15629
15630     return {
15631         /**
15632          * Returns a reference to the underlying {@link Roo.BasicDialog} element
15633          * @return {Roo.BasicDialog} The BasicDialog element
15634          */
15635         getDialog : function(){
15636            if(!dlg){
15637                 dlg = new Roo.BasicDialog("x-msg-box", {
15638                     autoCreate : true,
15639                     shadow: true,
15640                     draggable: true,
15641                     resizable:false,
15642                     constraintoviewport:false,
15643                     fixedcenter:true,
15644                     collapsible : false,
15645                     shim:true,
15646                     modal: true,
15647                     width:400, height:100,
15648                     buttonAlign:"center",
15649                     closeClick : function(){
15650                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
15651                             handleButton("no");
15652                         }else{
15653                             handleButton("cancel");
15654                         }
15655                     }
15656                 });
15657                 dlg.on("hide", handleHide);
15658                 mask = dlg.mask;
15659                 dlg.addKeyListener(27, handleEsc);
15660                 buttons = {};
15661                 var bt = this.buttonText;
15662                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
15663                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
15664                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
15665                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
15666                 bodyEl = dlg.body.createChild({
15667
15668                     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>'
15669                 });
15670                 msgEl = bodyEl.dom.firstChild;
15671                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
15672                 textboxEl.enableDisplayMode();
15673                 textboxEl.addKeyListener([10,13], function(){
15674                     if(dlg.isVisible() && opt && opt.buttons){
15675                         if(opt.buttons.ok){
15676                             handleButton("ok");
15677                         }else if(opt.buttons.yes){
15678                             handleButton("yes");
15679                         }
15680                     }
15681                 });
15682                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
15683                 textareaEl.enableDisplayMode();
15684                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
15685                 progressEl.enableDisplayMode();
15686                 var pf = progressEl.dom.firstChild;
15687                 if (pf) {
15688                     pp = Roo.get(pf.firstChild);
15689                     pp.setHeight(pf.offsetHeight);
15690                 }
15691                 
15692             }
15693             return dlg;
15694         },
15695
15696         /**
15697          * Updates the message box body text
15698          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
15699          * the XHTML-compliant non-breaking space character '&amp;#160;')
15700          * @return {Roo.MessageBox} This message box
15701          */
15702         updateText : function(text){
15703             if(!dlg.isVisible() && !opt.width){
15704                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
15705             }
15706             msgEl.innerHTML = text || '&#160;';
15707       
15708             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
15709             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
15710             var w = Math.max(
15711                     Math.min(opt.width || cw , this.maxWidth), 
15712                     Math.max(opt.minWidth || this.minWidth, bwidth)
15713             );
15714             if(opt.prompt){
15715                 activeTextEl.setWidth(w);
15716             }
15717             if(dlg.isVisible()){
15718                 dlg.fixedcenter = false;
15719             }
15720             // to big, make it scroll. = But as usual stupid IE does not support
15721             // !important..
15722             
15723             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
15724                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
15725                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
15726             } else {
15727                 bodyEl.dom.style.height = '';
15728                 bodyEl.dom.style.overflowY = '';
15729             }
15730             if (cw > w) {
15731                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
15732             } else {
15733                 bodyEl.dom.style.overflowX = '';
15734             }
15735             
15736             dlg.setContentSize(w, bodyEl.getHeight());
15737             if(dlg.isVisible()){
15738                 dlg.fixedcenter = true;
15739             }
15740             return this;
15741         },
15742
15743         /**
15744          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
15745          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
15746          * @param {Number} value Any number between 0 and 1 (e.g., .5)
15747          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
15748          * @return {Roo.MessageBox} This message box
15749          */
15750         updateProgress : function(value, text){
15751             if(text){
15752                 this.updateText(text);
15753             }
15754             if (pp) { // weird bug on my firefox - for some reason this is not defined
15755                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
15756             }
15757             return this;
15758         },        
15759
15760         /**
15761          * Returns true if the message box is currently displayed
15762          * @return {Boolean} True if the message box is visible, else false
15763          */
15764         isVisible : function(){
15765             return dlg && dlg.isVisible();  
15766         },
15767
15768         /**
15769          * Hides the message box if it is displayed
15770          */
15771         hide : function(){
15772             if(this.isVisible()){
15773                 dlg.hide();
15774             }  
15775         },
15776
15777         /**
15778          * Displays a new message box, or reinitializes an existing message box, based on the config options
15779          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
15780          * The following config object properties are supported:
15781          * <pre>
15782 Property    Type             Description
15783 ----------  ---------------  ------------------------------------------------------------------------------------
15784 animEl            String/Element   An id or Element from which the message box should animate as it opens and
15785                                    closes (defaults to undefined)
15786 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
15787                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
15788 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
15789                                    progress and wait dialogs will ignore this property and always hide the
15790                                    close button as they can only be closed programmatically.
15791 cls               String           A custom CSS class to apply to the message box element
15792 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
15793                                    displayed (defaults to 75)
15794 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
15795                                    function will be btn (the name of the button that was clicked, if applicable,
15796                                    e.g. "ok"), and text (the value of the active text field, if applicable).
15797                                    Progress and wait dialogs will ignore this option since they do not respond to
15798                                    user actions and can only be closed programmatically, so any required function
15799                                    should be called by the same code after it closes the dialog.
15800 icon              String           A CSS class that provides a background image to be used as an icon for
15801                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
15802 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
15803 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
15804 modal             Boolean          False to allow user interaction with the page while the message box is
15805                                    displayed (defaults to true)
15806 msg               String           A string that will replace the existing message box body text (defaults
15807                                    to the XHTML-compliant non-breaking space character '&#160;')
15808 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
15809 progress          Boolean          True to display a progress bar (defaults to false)
15810 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
15811 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
15812 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
15813 title             String           The title text
15814 value             String           The string value to set into the active textbox element if displayed
15815 wait              Boolean          True to display a progress bar (defaults to false)
15816 width             Number           The width of the dialog in pixels
15817 </pre>
15818          *
15819          * Example usage:
15820          * <pre><code>
15821 Roo.Msg.show({
15822    title: 'Address',
15823    msg: 'Please enter your address:',
15824    width: 300,
15825    buttons: Roo.MessageBox.OKCANCEL,
15826    multiline: true,
15827    fn: saveAddress,
15828    animEl: 'addAddressBtn'
15829 });
15830 </code></pre>
15831          * @param {Object} config Configuration options
15832          * @return {Roo.MessageBox} This message box
15833          */
15834         show : function(options)
15835         {
15836             
15837             // this causes nightmares if you show one dialog after another
15838             // especially on callbacks..
15839              
15840             if(this.isVisible()){
15841                 
15842                 this.hide();
15843                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
15844                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
15845                 Roo.log("New Dialog Message:" +  options.msg )
15846                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
15847                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
15848                 
15849             }
15850             var d = this.getDialog();
15851             opt = options;
15852             d.setTitle(opt.title || "&#160;");
15853             d.close.setDisplayed(opt.closable !== false);
15854             activeTextEl = textboxEl;
15855             opt.prompt = opt.prompt || (opt.multiline ? true : false);
15856             if(opt.prompt){
15857                 if(opt.multiline){
15858                     textboxEl.hide();
15859                     textareaEl.show();
15860                     textareaEl.setHeight(typeof opt.multiline == "number" ?
15861                         opt.multiline : this.defaultTextHeight);
15862                     activeTextEl = textareaEl;
15863                 }else{
15864                     textboxEl.show();
15865                     textareaEl.hide();
15866                 }
15867             }else{
15868                 textboxEl.hide();
15869                 textareaEl.hide();
15870             }
15871             progressEl.setDisplayed(opt.progress === true);
15872             this.updateProgress(0);
15873             activeTextEl.dom.value = opt.value || "";
15874             if(opt.prompt){
15875                 dlg.setDefaultButton(activeTextEl);
15876             }else{
15877                 var bs = opt.buttons;
15878                 var db = null;
15879                 if(bs && bs.ok){
15880                     db = buttons["ok"];
15881                 }else if(bs && bs.yes){
15882                     db = buttons["yes"];
15883                 }
15884                 dlg.setDefaultButton(db);
15885             }
15886             bwidth = updateButtons(opt.buttons);
15887             this.updateText(opt.msg);
15888             if(opt.cls){
15889                 d.el.addClass(opt.cls);
15890             }
15891             d.proxyDrag = opt.proxyDrag === true;
15892             d.modal = opt.modal !== false;
15893             d.mask = opt.modal !== false ? mask : false;
15894             if(!d.isVisible()){
15895                 // force it to the end of the z-index stack so it gets a cursor in FF
15896                 document.body.appendChild(dlg.el.dom);
15897                 d.animateTarget = null;
15898                 d.show(options.animEl);
15899             }
15900             return this;
15901         },
15902
15903         /**
15904          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
15905          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
15906          * and closing the message box when the process is complete.
15907          * @param {String} title The title bar text
15908          * @param {String} msg The message box body text
15909          * @return {Roo.MessageBox} This message box
15910          */
15911         progress : function(title, msg){
15912             this.show({
15913                 title : title,
15914                 msg : msg,
15915                 buttons: false,
15916                 progress:true,
15917                 closable:false,
15918                 minWidth: this.minProgressWidth,
15919                 modal : true
15920             });
15921             return this;
15922         },
15923
15924         /**
15925          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
15926          * If a callback function is passed it will be called after the user clicks the button, and the
15927          * id of the button that was clicked will be passed as the only parameter to the callback
15928          * (could also be the top-right close button).
15929          * @param {String} title The title bar text
15930          * @param {String} msg The message box body text
15931          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15932          * @param {Object} scope (optional) The scope of the callback function
15933          * @return {Roo.MessageBox} This message box
15934          */
15935         alert : function(title, msg, fn, scope){
15936             this.show({
15937                 title : title,
15938                 msg : msg,
15939                 buttons: this.OK,
15940                 fn: fn,
15941                 scope : scope,
15942                 modal : true
15943             });
15944             return this;
15945         },
15946
15947         /**
15948          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
15949          * interaction while waiting for a long-running process to complete that does not have defined intervals.
15950          * You are responsible for closing the message box when the process is complete.
15951          * @param {String} msg The message box body text
15952          * @param {String} title (optional) The title bar text
15953          * @return {Roo.MessageBox} This message box
15954          */
15955         wait : function(msg, title){
15956             this.show({
15957                 title : title,
15958                 msg : msg,
15959                 buttons: false,
15960                 closable:false,
15961                 progress:true,
15962                 modal:true,
15963                 width:300,
15964                 wait:true
15965             });
15966             waitTimer = Roo.TaskMgr.start({
15967                 run: function(i){
15968                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
15969                 },
15970                 interval: 1000
15971             });
15972             return this;
15973         },
15974
15975         /**
15976          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
15977          * If a callback function is passed it will be called after the user clicks either button, and the id of the
15978          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
15979          * @param {String} title The title bar text
15980          * @param {String} msg The message box body text
15981          * @param {Function} fn (optional) The callback function invoked after the message box is closed
15982          * @param {Object} scope (optional) The scope of the callback function
15983          * @return {Roo.MessageBox} This message box
15984          */
15985         confirm : function(title, msg, fn, scope){
15986             this.show({
15987                 title : title,
15988                 msg : msg,
15989                 buttons: this.YESNO,
15990                 fn: fn,
15991                 scope : scope,
15992                 modal : true
15993             });
15994             return this;
15995         },
15996
15997         /**
15998          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
15999          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
16000          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
16001          * (could also be the top-right close button) and the text that was entered will be passed as the two
16002          * parameters to the callback.
16003          * @param {String} title The title bar text
16004          * @param {String} msg The message box body text
16005          * @param {Function} fn (optional) The callback function invoked after the message box is closed
16006          * @param {Object} scope (optional) The scope of the callback function
16007          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
16008          * property, or the height in pixels to create the textbox (defaults to false / single-line)
16009          * @return {Roo.MessageBox} This message box
16010          */
16011         prompt : function(title, msg, fn, scope, multiline){
16012             this.show({
16013                 title : title,
16014                 msg : msg,
16015                 buttons: this.OKCANCEL,
16016                 fn: fn,
16017                 minWidth:250,
16018                 scope : scope,
16019                 prompt:true,
16020                 multiline: multiline,
16021                 modal : true
16022             });
16023             return this;
16024         },
16025
16026         /**
16027          * Button config that displays a single OK button
16028          * @type Object
16029          */
16030         OK : {ok:true},
16031         /**
16032          * Button config that displays Yes and No buttons
16033          * @type Object
16034          */
16035         YESNO : {yes:true, no:true},
16036         /**
16037          * Button config that displays OK and Cancel buttons
16038          * @type Object
16039          */
16040         OKCANCEL : {ok:true, cancel:true},
16041         /**
16042          * Button config that displays Yes, No and Cancel buttons
16043          * @type Object
16044          */
16045         YESNOCANCEL : {yes:true, no:true, cancel:true},
16046
16047         /**
16048          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
16049          * @type Number
16050          */
16051         defaultTextHeight : 75,
16052         /**
16053          * The maximum width in pixels of the message box (defaults to 600)
16054          * @type Number
16055          */
16056         maxWidth : 600,
16057         /**
16058          * The minimum width in pixels of the message box (defaults to 100)
16059          * @type Number
16060          */
16061         minWidth : 100,
16062         /**
16063          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
16064          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
16065          * @type Number
16066          */
16067         minProgressWidth : 250,
16068         /**
16069          * An object containing the default button text strings that can be overriden for localized language support.
16070          * Supported properties are: ok, cancel, yes and no.
16071          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
16072          * @type Object
16073          */
16074         buttonText : {
16075             ok : "OK",
16076             cancel : "Cancel",
16077             yes : "Yes",
16078             no : "No"
16079         }
16080     };
16081 }();
16082
16083 /**
16084  * Shorthand for {@link Roo.MessageBox}
16085  */
16086 Roo.Msg = Roo.MessageBox;/*
16087  * Based on:
16088  * Ext JS Library 1.1.1
16089  * Copyright(c) 2006-2007, Ext JS, LLC.
16090  *
16091  * Originally Released Under LGPL - original licence link has changed is not relivant.
16092  *
16093  * Fork - LGPL
16094  * <script type="text/javascript">
16095  */
16096 /**
16097  * @class Roo.QuickTips
16098  * Provides attractive and customizable tooltips for any element.
16099  * @singleton
16100  */
16101 Roo.QuickTips = function(){
16102     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
16103     var ce, bd, xy, dd;
16104     var visible = false, disabled = true, inited = false;
16105     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
16106     
16107     var onOver = function(e){
16108         if(disabled){
16109             return;
16110         }
16111         var t = e.getTarget();
16112         if(!t || t.nodeType !== 1 || t == document || t == document.body){
16113             return;
16114         }
16115         if(ce && t == ce.el){
16116             clearTimeout(hideProc);
16117             return;
16118         }
16119         if(t && tagEls[t.id]){
16120             tagEls[t.id].el = t;
16121             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
16122             return;
16123         }
16124         var ttp, et = Roo.fly(t);
16125         var ns = cfg.namespace;
16126         if(tm.interceptTitles && t.title){
16127             ttp = t.title;
16128             t.qtip = ttp;
16129             t.removeAttribute("title");
16130             e.preventDefault();
16131         }else{
16132             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
16133         }
16134         if(ttp){
16135             showProc = show.defer(tm.showDelay, tm, [{
16136                 el: t, 
16137                 text: ttp, 
16138                 width: et.getAttributeNS(ns, cfg.width),
16139                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
16140                 title: et.getAttributeNS(ns, cfg.title),
16141                     cls: et.getAttributeNS(ns, cfg.cls)
16142             }]);
16143         }
16144     };
16145     
16146     var onOut = function(e){
16147         clearTimeout(showProc);
16148         var t = e.getTarget();
16149         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
16150             hideProc = setTimeout(hide, tm.hideDelay);
16151         }
16152     };
16153     
16154     var onMove = function(e){
16155         if(disabled){
16156             return;
16157         }
16158         xy = e.getXY();
16159         xy[1] += 18;
16160         if(tm.trackMouse && ce){
16161             el.setXY(xy);
16162         }
16163     };
16164     
16165     var onDown = function(e){
16166         clearTimeout(showProc);
16167         clearTimeout(hideProc);
16168         if(!e.within(el)){
16169             if(tm.hideOnClick){
16170                 hide();
16171                 tm.disable();
16172                 tm.enable.defer(100, tm);
16173             }
16174         }
16175     };
16176     
16177     var getPad = function(){
16178         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
16179     };
16180
16181     var show = function(o){
16182         if(disabled){
16183             return;
16184         }
16185         clearTimeout(dismissProc);
16186         ce = o;
16187         if(removeCls){ // in case manually hidden
16188             el.removeClass(removeCls);
16189             removeCls = null;
16190         }
16191         if(ce.cls){
16192             el.addClass(ce.cls);
16193             removeCls = ce.cls;
16194         }
16195         if(ce.title){
16196             tipTitle.update(ce.title);
16197             tipTitle.show();
16198         }else{
16199             tipTitle.update('');
16200             tipTitle.hide();
16201         }
16202         el.dom.style.width  = tm.maxWidth+'px';
16203         //tipBody.dom.style.width = '';
16204         tipBodyText.update(o.text);
16205         var p = getPad(), w = ce.width;
16206         if(!w){
16207             var td = tipBodyText.dom;
16208             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
16209             if(aw > tm.maxWidth){
16210                 w = tm.maxWidth;
16211             }else if(aw < tm.minWidth){
16212                 w = tm.minWidth;
16213             }else{
16214                 w = aw;
16215             }
16216         }
16217         //tipBody.setWidth(w);
16218         el.setWidth(parseInt(w, 10) + p);
16219         if(ce.autoHide === false){
16220             close.setDisplayed(true);
16221             if(dd){
16222                 dd.unlock();
16223             }
16224         }else{
16225             close.setDisplayed(false);
16226             if(dd){
16227                 dd.lock();
16228             }
16229         }
16230         if(xy){
16231             el.avoidY = xy[1]-18;
16232             el.setXY(xy);
16233         }
16234         if(tm.animate){
16235             el.setOpacity(.1);
16236             el.setStyle("visibility", "visible");
16237             el.fadeIn({callback: afterShow});
16238         }else{
16239             afterShow();
16240         }
16241     };
16242     
16243     var afterShow = function(){
16244         if(ce){
16245             el.show();
16246             esc.enable();
16247             if(tm.autoDismiss && ce.autoHide !== false){
16248                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
16249             }
16250         }
16251     };
16252     
16253     var hide = function(noanim){
16254         clearTimeout(dismissProc);
16255         clearTimeout(hideProc);
16256         ce = null;
16257         if(el.isVisible()){
16258             esc.disable();
16259             if(noanim !== true && tm.animate){
16260                 el.fadeOut({callback: afterHide});
16261             }else{
16262                 afterHide();
16263             } 
16264         }
16265     };
16266     
16267     var afterHide = function(){
16268         el.hide();
16269         if(removeCls){
16270             el.removeClass(removeCls);
16271             removeCls = null;
16272         }
16273     };
16274     
16275     return {
16276         /**
16277         * @cfg {Number} minWidth
16278         * The minimum width of the quick tip (defaults to 40)
16279         */
16280        minWidth : 40,
16281         /**
16282         * @cfg {Number} maxWidth
16283         * The maximum width of the quick tip (defaults to 300)
16284         */
16285        maxWidth : 300,
16286         /**
16287         * @cfg {Boolean} interceptTitles
16288         * True to automatically use the element's DOM title value if available (defaults to false)
16289         */
16290        interceptTitles : false,
16291         /**
16292         * @cfg {Boolean} trackMouse
16293         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
16294         */
16295        trackMouse : false,
16296         /**
16297         * @cfg {Boolean} hideOnClick
16298         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
16299         */
16300        hideOnClick : true,
16301         /**
16302         * @cfg {Number} showDelay
16303         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
16304         */
16305        showDelay : 500,
16306         /**
16307         * @cfg {Number} hideDelay
16308         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
16309         */
16310        hideDelay : 200,
16311         /**
16312         * @cfg {Boolean} autoHide
16313         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
16314         * Used in conjunction with hideDelay.
16315         */
16316        autoHide : true,
16317         /**
16318         * @cfg {Boolean}
16319         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
16320         * (defaults to true).  Used in conjunction with autoDismissDelay.
16321         */
16322        autoDismiss : true,
16323         /**
16324         * @cfg {Number}
16325         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
16326         */
16327        autoDismissDelay : 5000,
16328        /**
16329         * @cfg {Boolean} animate
16330         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
16331         */
16332        animate : false,
16333
16334        /**
16335         * @cfg {String} title
16336         * Title text to display (defaults to '').  This can be any valid HTML markup.
16337         */
16338         title: '',
16339        /**
16340         * @cfg {String} text
16341         * Body text to display (defaults to '').  This can be any valid HTML markup.
16342         */
16343         text : '',
16344        /**
16345         * @cfg {String} cls
16346         * A CSS class to apply to the base quick tip element (defaults to '').
16347         */
16348         cls : '',
16349        /**
16350         * @cfg {Number} width
16351         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
16352         * minWidth or maxWidth.
16353         */
16354         width : null,
16355
16356     /**
16357      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
16358      * or display QuickTips in a page.
16359      */
16360        init : function(){
16361           tm = Roo.QuickTips;
16362           cfg = tm.tagConfig;
16363           if(!inited){
16364               if(!Roo.isReady){ // allow calling of init() before onReady
16365                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
16366                   return;
16367               }
16368               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
16369               el.fxDefaults = {stopFx: true};
16370               // maximum custom styling
16371               //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>');
16372               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>');              
16373               tipTitle = el.child('h3');
16374               tipTitle.enableDisplayMode("block");
16375               tipBody = el.child('div.x-tip-bd');
16376               tipBodyText = el.child('div.x-tip-bd-inner');
16377               //bdLeft = el.child('div.x-tip-bd-left');
16378               //bdRight = el.child('div.x-tip-bd-right');
16379               close = el.child('div.x-tip-close');
16380               close.enableDisplayMode("block");
16381               close.on("click", hide);
16382               var d = Roo.get(document);
16383               d.on("mousedown", onDown);
16384               d.on("mouseover", onOver);
16385               d.on("mouseout", onOut);
16386               d.on("mousemove", onMove);
16387               esc = d.addKeyListener(27, hide);
16388               esc.disable();
16389               if(Roo.dd.DD){
16390                   dd = el.initDD("default", null, {
16391                       onDrag : function(){
16392                           el.sync();  
16393                       }
16394                   });
16395                   dd.setHandleElId(tipTitle.id);
16396                   dd.lock();
16397               }
16398               inited = true;
16399           }
16400           this.enable(); 
16401        },
16402
16403     /**
16404      * Configures a new quick tip instance and assigns it to a target element.  The following config options
16405      * are supported:
16406      * <pre>
16407 Property    Type                   Description
16408 ----------  ---------------------  ------------------------------------------------------------------------
16409 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
16410      * </ul>
16411      * @param {Object} config The config object
16412      */
16413        register : function(config){
16414            var cs = config instanceof Array ? config : arguments;
16415            for(var i = 0, len = cs.length; i < len; i++) {
16416                var c = cs[i];
16417                var target = c.target;
16418                if(target){
16419                    if(target instanceof Array){
16420                        for(var j = 0, jlen = target.length; j < jlen; j++){
16421                            tagEls[target[j]] = c;
16422                        }
16423                    }else{
16424                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
16425                    }
16426                }
16427            }
16428        },
16429
16430     /**
16431      * Removes this quick tip from its element and destroys it.
16432      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
16433      */
16434        unregister : function(el){
16435            delete tagEls[Roo.id(el)];
16436        },
16437
16438     /**
16439      * Enable this quick tip.
16440      */
16441        enable : function(){
16442            if(inited && disabled){
16443                locks.pop();
16444                if(locks.length < 1){
16445                    disabled = false;
16446                }
16447            }
16448        },
16449
16450     /**
16451      * Disable this quick tip.
16452      */
16453        disable : function(){
16454           disabled = true;
16455           clearTimeout(showProc);
16456           clearTimeout(hideProc);
16457           clearTimeout(dismissProc);
16458           if(ce){
16459               hide(true);
16460           }
16461           locks.push(1);
16462        },
16463
16464     /**
16465      * Returns true if the quick tip is enabled, else false.
16466      */
16467        isEnabled : function(){
16468             return !disabled;
16469        },
16470
16471         // private
16472        tagConfig : {
16473            namespace : "ext",
16474            attribute : "qtip",
16475            width : "width",
16476            target : "target",
16477            title : "qtitle",
16478            hide : "hide",
16479            cls : "qclass"
16480        }
16481    };
16482 }();
16483
16484 // backwards compat
16485 Roo.QuickTips.tips = Roo.QuickTips.register;/*
16486  * Based on:
16487  * Ext JS Library 1.1.1
16488  * Copyright(c) 2006-2007, Ext JS, LLC.
16489  *
16490  * Originally Released Under LGPL - original licence link has changed is not relivant.
16491  *
16492  * Fork - LGPL
16493  * <script type="text/javascript">
16494  */
16495  
16496
16497 /**
16498  * @class Roo.tree.TreePanel
16499  * @extends Roo.data.Tree
16500
16501  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
16502  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
16503  * @cfg {Boolean} enableDD true to enable drag and drop
16504  * @cfg {Boolean} enableDrag true to enable just drag
16505  * @cfg {Boolean} enableDrop true to enable just drop
16506  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
16507  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
16508  * @cfg {String} ddGroup The DD group this TreePanel belongs to
16509  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
16510  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
16511  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
16512  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
16513  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
16514  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
16515  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
16516  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
16517  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
16518  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
16519  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
16520  * @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>
16521  * @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>
16522  * 
16523  * @constructor
16524  * @param {String/HTMLElement/Element} el The container element
16525  * @param {Object} config
16526  */
16527 Roo.tree.TreePanel = function(el, config){
16528     var root = false;
16529     var loader = false;
16530     if (config.root) {
16531         root = config.root;
16532         delete config.root;
16533     }
16534     if (config.loader) {
16535         loader = config.loader;
16536         delete config.loader;
16537     }
16538     
16539     Roo.apply(this, config);
16540     Roo.tree.TreePanel.superclass.constructor.call(this);
16541     this.el = Roo.get(el);
16542     this.el.addClass('x-tree');
16543     //console.log(root);
16544     if (root) {
16545         this.setRootNode( Roo.factory(root, Roo.tree));
16546     }
16547     if (loader) {
16548         this.loader = Roo.factory(loader, Roo.tree);
16549     }
16550    /**
16551     * Read-only. The id of the container element becomes this TreePanel's id.
16552     */
16553     this.id = this.el.id;
16554     this.addEvents({
16555         /**
16556         * @event beforeload
16557         * Fires before a node is loaded, return false to cancel
16558         * @param {Node} node The node being loaded
16559         */
16560         "beforeload" : true,
16561         /**
16562         * @event load
16563         * Fires when a node is loaded
16564         * @param {Node} node The node that was loaded
16565         */
16566         "load" : true,
16567         /**
16568         * @event textchange
16569         * Fires when the text for a node is changed
16570         * @param {Node} node The node
16571         * @param {String} text The new text
16572         * @param {String} oldText The old text
16573         */
16574         "textchange" : true,
16575         /**
16576         * @event beforeexpand
16577         * Fires before a node is expanded, return false to cancel.
16578         * @param {Node} node The node
16579         * @param {Boolean} deep
16580         * @param {Boolean} anim
16581         */
16582         "beforeexpand" : true,
16583         /**
16584         * @event beforecollapse
16585         * Fires before a node is collapsed, return false to cancel.
16586         * @param {Node} node The node
16587         * @param {Boolean} deep
16588         * @param {Boolean} anim
16589         */
16590         "beforecollapse" : true,
16591         /**
16592         * @event expand
16593         * Fires when a node is expanded
16594         * @param {Node} node The node
16595         */
16596         "expand" : true,
16597         /**
16598         * @event disabledchange
16599         * Fires when the disabled status of a node changes
16600         * @param {Node} node The node
16601         * @param {Boolean} disabled
16602         */
16603         "disabledchange" : true,
16604         /**
16605         * @event collapse
16606         * Fires when a node is collapsed
16607         * @param {Node} node The node
16608         */
16609         "collapse" : true,
16610         /**
16611         * @event beforeclick
16612         * Fires before click processing on a node. Return false to cancel the default action.
16613         * @param {Node} node The node
16614         * @param {Roo.EventObject} e The event object
16615         */
16616         "beforeclick":true,
16617         /**
16618         * @event checkchange
16619         * Fires when a node with a checkbox's checked property changes
16620         * @param {Node} this This node
16621         * @param {Boolean} checked
16622         */
16623         "checkchange":true,
16624         /**
16625         * @event click
16626         * Fires when a node is clicked
16627         * @param {Node} node The node
16628         * @param {Roo.EventObject} e The event object
16629         */
16630         "click":true,
16631         /**
16632         * @event dblclick
16633         * Fires when a node is double clicked
16634         * @param {Node} node The node
16635         * @param {Roo.EventObject} e The event object
16636         */
16637         "dblclick":true,
16638         /**
16639         * @event contextmenu
16640         * Fires when a node is right clicked
16641         * @param {Node} node The node
16642         * @param {Roo.EventObject} e The event object
16643         */
16644         "contextmenu":true,
16645         /**
16646         * @event beforechildrenrendered
16647         * Fires right before the child nodes for a node are rendered
16648         * @param {Node} node The node
16649         */
16650         "beforechildrenrendered":true,
16651         /**
16652         * @event startdrag
16653         * Fires when a node starts being dragged
16654         * @param {Roo.tree.TreePanel} this
16655         * @param {Roo.tree.TreeNode} node
16656         * @param {event} e The raw browser event
16657         */ 
16658        "startdrag" : true,
16659        /**
16660         * @event enddrag
16661         * Fires when a drag operation is complete
16662         * @param {Roo.tree.TreePanel} this
16663         * @param {Roo.tree.TreeNode} node
16664         * @param {event} e The raw browser event
16665         */
16666        "enddrag" : true,
16667        /**
16668         * @event dragdrop
16669         * Fires when a dragged node is dropped on a valid DD target
16670         * @param {Roo.tree.TreePanel} this
16671         * @param {Roo.tree.TreeNode} node
16672         * @param {DD} dd The dd it was dropped on
16673         * @param {event} e The raw browser event
16674         */
16675        "dragdrop" : true,
16676        /**
16677         * @event beforenodedrop
16678         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
16679         * passed to handlers has the following properties:<br />
16680         * <ul style="padding:5px;padding-left:16px;">
16681         * <li>tree - The TreePanel</li>
16682         * <li>target - The node being targeted for the drop</li>
16683         * <li>data - The drag data from the drag source</li>
16684         * <li>point - The point of the drop - append, above or below</li>
16685         * <li>source - The drag source</li>
16686         * <li>rawEvent - Raw mouse event</li>
16687         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
16688         * to be inserted by setting them on this object.</li>
16689         * <li>cancel - Set this to true to cancel the drop.</li>
16690         * </ul>
16691         * @param {Object} dropEvent
16692         */
16693        "beforenodedrop" : true,
16694        /**
16695         * @event nodedrop
16696         * Fires after a DD object is dropped on a node in this tree. The dropEvent
16697         * passed to handlers has the following properties:<br />
16698         * <ul style="padding:5px;padding-left:16px;">
16699         * <li>tree - The TreePanel</li>
16700         * <li>target - The node being targeted for the drop</li>
16701         * <li>data - The drag data from the drag source</li>
16702         * <li>point - The point of the drop - append, above or below</li>
16703         * <li>source - The drag source</li>
16704         * <li>rawEvent - Raw mouse event</li>
16705         * <li>dropNode - Dropped node(s).</li>
16706         * </ul>
16707         * @param {Object} dropEvent
16708         */
16709        "nodedrop" : true,
16710         /**
16711         * @event nodedragover
16712         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
16713         * passed to handlers has the following properties:<br />
16714         * <ul style="padding:5px;padding-left:16px;">
16715         * <li>tree - The TreePanel</li>
16716         * <li>target - The node being targeted for the drop</li>
16717         * <li>data - The drag data from the drag source</li>
16718         * <li>point - The point of the drop - append, above or below</li>
16719         * <li>source - The drag source</li>
16720         * <li>rawEvent - Raw mouse event</li>
16721         * <li>dropNode - Drop node(s) provided by the source.</li>
16722         * <li>cancel - Set this to true to signal drop not allowed.</li>
16723         * </ul>
16724         * @param {Object} dragOverEvent
16725         */
16726        "nodedragover" : true
16727         
16728     });
16729     if(this.singleExpand){
16730        this.on("beforeexpand", this.restrictExpand, this);
16731     }
16732     if (this.editor) {
16733         this.editor.tree = this;
16734         this.editor = Roo.factory(this.editor, Roo.tree);
16735     }
16736     
16737     if (this.selModel) {
16738         this.selModel = Roo.factory(this.selModel, Roo.tree);
16739     }
16740    
16741 };
16742 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
16743     rootVisible : true,
16744     animate: Roo.enableFx,
16745     lines : true,
16746     enableDD : false,
16747     hlDrop : Roo.enableFx,
16748   
16749     renderer: false,
16750     
16751     rendererTip: false,
16752     // private
16753     restrictExpand : function(node){
16754         var p = node.parentNode;
16755         if(p){
16756             if(p.expandedChild && p.expandedChild.parentNode == p){
16757                 p.expandedChild.collapse();
16758             }
16759             p.expandedChild = node;
16760         }
16761     },
16762
16763     // private override
16764     setRootNode : function(node){
16765         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
16766         if(!this.rootVisible){
16767             node.ui = new Roo.tree.RootTreeNodeUI(node);
16768         }
16769         return node;
16770     },
16771
16772     /**
16773      * Returns the container element for this TreePanel
16774      */
16775     getEl : function(){
16776         return this.el;
16777     },
16778
16779     /**
16780      * Returns the default TreeLoader for this TreePanel
16781      */
16782     getLoader : function(){
16783         return this.loader;
16784     },
16785
16786     /**
16787      * Expand all nodes
16788      */
16789     expandAll : function(){
16790         this.root.expand(true);
16791     },
16792
16793     /**
16794      * Collapse all nodes
16795      */
16796     collapseAll : function(){
16797         this.root.collapse(true);
16798     },
16799
16800     /**
16801      * Returns the selection model used by this TreePanel
16802      */
16803     getSelectionModel : function(){
16804         if(!this.selModel){
16805             this.selModel = new Roo.tree.DefaultSelectionModel();
16806         }
16807         return this.selModel;
16808     },
16809
16810     /**
16811      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
16812      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
16813      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
16814      * @return {Array}
16815      */
16816     getChecked : function(a, startNode){
16817         startNode = startNode || this.root;
16818         var r = [];
16819         var f = function(){
16820             if(this.attributes.checked){
16821                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
16822             }
16823         }
16824         startNode.cascade(f);
16825         return r;
16826     },
16827
16828     /**
16829      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16830      * @param {String} path
16831      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16832      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
16833      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
16834      */
16835     expandPath : function(path, attr, callback){
16836         attr = attr || "id";
16837         var keys = path.split(this.pathSeparator);
16838         var curNode = this.root;
16839         if(curNode.attributes[attr] != keys[1]){ // invalid root
16840             if(callback){
16841                 callback(false, null);
16842             }
16843             return;
16844         }
16845         var index = 1;
16846         var f = function(){
16847             if(++index == keys.length){
16848                 if(callback){
16849                     callback(true, curNode);
16850                 }
16851                 return;
16852             }
16853             var c = curNode.findChild(attr, keys[index]);
16854             if(!c){
16855                 if(callback){
16856                     callback(false, curNode);
16857                 }
16858                 return;
16859             }
16860             curNode = c;
16861             c.expand(false, false, f);
16862         };
16863         curNode.expand(false, false, f);
16864     },
16865
16866     /**
16867      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
16868      * @param {String} path
16869      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
16870      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
16871      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
16872      */
16873     selectPath : function(path, attr, callback){
16874         attr = attr || "id";
16875         var keys = path.split(this.pathSeparator);
16876         var v = keys.pop();
16877         if(keys.length > 0){
16878             var f = function(success, node){
16879                 if(success && node){
16880                     var n = node.findChild(attr, v);
16881                     if(n){
16882                         n.select();
16883                         if(callback){
16884                             callback(true, n);
16885                         }
16886                     }else if(callback){
16887                         callback(false, n);
16888                     }
16889                 }else{
16890                     if(callback){
16891                         callback(false, n);
16892                     }
16893                 }
16894             };
16895             this.expandPath(keys.join(this.pathSeparator), attr, f);
16896         }else{
16897             this.root.select();
16898             if(callback){
16899                 callback(true, this.root);
16900             }
16901         }
16902     },
16903
16904     getTreeEl : function(){
16905         return this.el;
16906     },
16907
16908     /**
16909      * Trigger rendering of this TreePanel
16910      */
16911     render : function(){
16912         if (this.innerCt) {
16913             return this; // stop it rendering more than once!!
16914         }
16915         
16916         this.innerCt = this.el.createChild({tag:"ul",
16917                cls:"x-tree-root-ct " +
16918                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
16919
16920         if(this.containerScroll){
16921             Roo.dd.ScrollManager.register(this.el);
16922         }
16923         if((this.enableDD || this.enableDrop) && !this.dropZone){
16924            /**
16925             * The dropZone used by this tree if drop is enabled
16926             * @type Roo.tree.TreeDropZone
16927             */
16928              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
16929                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
16930            });
16931         }
16932         if((this.enableDD || this.enableDrag) && !this.dragZone){
16933            /**
16934             * The dragZone used by this tree if drag is enabled
16935             * @type Roo.tree.TreeDragZone
16936             */
16937             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
16938                ddGroup: this.ddGroup || "TreeDD",
16939                scroll: this.ddScroll
16940            });
16941         }
16942         this.getSelectionModel().init(this);
16943         if (!this.root) {
16944             Roo.log("ROOT not set in tree");
16945             return this;
16946         }
16947         this.root.render();
16948         if(!this.rootVisible){
16949             this.root.renderChildren();
16950         }
16951         return this;
16952     }
16953 });/*
16954  * Based on:
16955  * Ext JS Library 1.1.1
16956  * Copyright(c) 2006-2007, Ext JS, LLC.
16957  *
16958  * Originally Released Under LGPL - original licence link has changed is not relivant.
16959  *
16960  * Fork - LGPL
16961  * <script type="text/javascript">
16962  */
16963  
16964
16965 /**
16966  * @class Roo.tree.DefaultSelectionModel
16967  * @extends Roo.util.Observable
16968  * The default single selection for a TreePanel.
16969  * @param {Object} cfg Configuration
16970  */
16971 Roo.tree.DefaultSelectionModel = function(cfg){
16972    this.selNode = null;
16973    
16974    
16975    
16976    this.addEvents({
16977        /**
16978         * @event selectionchange
16979         * Fires when the selected node changes
16980         * @param {DefaultSelectionModel} this
16981         * @param {TreeNode} node the new selection
16982         */
16983        "selectionchange" : true,
16984
16985        /**
16986         * @event beforeselect
16987         * Fires before the selected node changes, return false to cancel the change
16988         * @param {DefaultSelectionModel} this
16989         * @param {TreeNode} node the new selection
16990         * @param {TreeNode} node the old selection
16991         */
16992        "beforeselect" : true
16993    });
16994    
16995     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
16996 };
16997
16998 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
16999     init : function(tree){
17000         this.tree = tree;
17001         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17002         tree.on("click", this.onNodeClick, this);
17003     },
17004     
17005     onNodeClick : function(node, e){
17006         if (e.ctrlKey && this.selNode == node)  {
17007             this.unselect(node);
17008             return;
17009         }
17010         this.select(node);
17011     },
17012     
17013     /**
17014      * Select a node.
17015      * @param {TreeNode} node The node to select
17016      * @return {TreeNode} The selected node
17017      */
17018     select : function(node){
17019         var last = this.selNode;
17020         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
17021             if(last){
17022                 last.ui.onSelectedChange(false);
17023             }
17024             this.selNode = node;
17025             node.ui.onSelectedChange(true);
17026             this.fireEvent("selectionchange", this, node, last);
17027         }
17028         return node;
17029     },
17030     
17031     /**
17032      * Deselect a node.
17033      * @param {TreeNode} node The node to unselect
17034      */
17035     unselect : function(node){
17036         if(this.selNode == node){
17037             this.clearSelections();
17038         }    
17039     },
17040     
17041     /**
17042      * Clear all selections
17043      */
17044     clearSelections : function(){
17045         var n = this.selNode;
17046         if(n){
17047             n.ui.onSelectedChange(false);
17048             this.selNode = null;
17049             this.fireEvent("selectionchange", this, null);
17050         }
17051         return n;
17052     },
17053     
17054     /**
17055      * Get the selected node
17056      * @return {TreeNode} The selected node
17057      */
17058     getSelectedNode : function(){
17059         return this.selNode;    
17060     },
17061     
17062     /**
17063      * Returns true if the node is selected
17064      * @param {TreeNode} node The node to check
17065      * @return {Boolean}
17066      */
17067     isSelected : function(node){
17068         return this.selNode == node;  
17069     },
17070
17071     /**
17072      * Selects the node above the selected node in the tree, intelligently walking the nodes
17073      * @return TreeNode The new selection
17074      */
17075     selectPrevious : function(){
17076         var s = this.selNode || this.lastSelNode;
17077         if(!s){
17078             return null;
17079         }
17080         var ps = s.previousSibling;
17081         if(ps){
17082             if(!ps.isExpanded() || ps.childNodes.length < 1){
17083                 return this.select(ps);
17084             } else{
17085                 var lc = ps.lastChild;
17086                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
17087                     lc = lc.lastChild;
17088                 }
17089                 return this.select(lc);
17090             }
17091         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
17092             return this.select(s.parentNode);
17093         }
17094         return null;
17095     },
17096
17097     /**
17098      * Selects the node above the selected node in the tree, intelligently walking the nodes
17099      * @return TreeNode The new selection
17100      */
17101     selectNext : function(){
17102         var s = this.selNode || this.lastSelNode;
17103         if(!s){
17104             return null;
17105         }
17106         if(s.firstChild && s.isExpanded()){
17107              return this.select(s.firstChild);
17108          }else if(s.nextSibling){
17109              return this.select(s.nextSibling);
17110          }else if(s.parentNode){
17111             var newS = null;
17112             s.parentNode.bubble(function(){
17113                 if(this.nextSibling){
17114                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
17115                     return false;
17116                 }
17117             });
17118             return newS;
17119          }
17120         return null;
17121     },
17122
17123     onKeyDown : function(e){
17124         var s = this.selNode || this.lastSelNode;
17125         // undesirable, but required
17126         var sm = this;
17127         if(!s){
17128             return;
17129         }
17130         var k = e.getKey();
17131         switch(k){
17132              case e.DOWN:
17133                  e.stopEvent();
17134                  this.selectNext();
17135              break;
17136              case e.UP:
17137                  e.stopEvent();
17138                  this.selectPrevious();
17139              break;
17140              case e.RIGHT:
17141                  e.preventDefault();
17142                  if(s.hasChildNodes()){
17143                      if(!s.isExpanded()){
17144                          s.expand();
17145                      }else if(s.firstChild){
17146                          this.select(s.firstChild, e);
17147                      }
17148                  }
17149              break;
17150              case e.LEFT:
17151                  e.preventDefault();
17152                  if(s.hasChildNodes() && s.isExpanded()){
17153                      s.collapse();
17154                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
17155                      this.select(s.parentNode, e);
17156                  }
17157              break;
17158         };
17159     }
17160 });
17161
17162 /**
17163  * @class Roo.tree.MultiSelectionModel
17164  * @extends Roo.util.Observable
17165  * Multi selection for a TreePanel.
17166  * @param {Object} cfg Configuration
17167  */
17168 Roo.tree.MultiSelectionModel = function(){
17169    this.selNodes = [];
17170    this.selMap = {};
17171    this.addEvents({
17172        /**
17173         * @event selectionchange
17174         * Fires when the selected nodes change
17175         * @param {MultiSelectionModel} this
17176         * @param {Array} nodes Array of the selected nodes
17177         */
17178        "selectionchange" : true
17179    });
17180    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
17181    
17182 };
17183
17184 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
17185     init : function(tree){
17186         this.tree = tree;
17187         tree.getTreeEl().on("keydown", this.onKeyDown, this);
17188         tree.on("click", this.onNodeClick, this);
17189     },
17190     
17191     onNodeClick : function(node, e){
17192         this.select(node, e, e.ctrlKey);
17193     },
17194     
17195     /**
17196      * Select a node.
17197      * @param {TreeNode} node The node to select
17198      * @param {EventObject} e (optional) An event associated with the selection
17199      * @param {Boolean} keepExisting True to retain existing selections
17200      * @return {TreeNode} The selected node
17201      */
17202     select : function(node, e, keepExisting){
17203         if(keepExisting !== true){
17204             this.clearSelections(true);
17205         }
17206         if(this.isSelected(node)){
17207             this.lastSelNode = node;
17208             return node;
17209         }
17210         this.selNodes.push(node);
17211         this.selMap[node.id] = node;
17212         this.lastSelNode = node;
17213         node.ui.onSelectedChange(true);
17214         this.fireEvent("selectionchange", this, this.selNodes);
17215         return node;
17216     },
17217     
17218     /**
17219      * Deselect a node.
17220      * @param {TreeNode} node The node to unselect
17221      */
17222     unselect : function(node){
17223         if(this.selMap[node.id]){
17224             node.ui.onSelectedChange(false);
17225             var sn = this.selNodes;
17226             var index = -1;
17227             if(sn.indexOf){
17228                 index = sn.indexOf(node);
17229             }else{
17230                 for(var i = 0, len = sn.length; i < len; i++){
17231                     if(sn[i] == node){
17232                         index = i;
17233                         break;
17234                     }
17235                 }
17236             }
17237             if(index != -1){
17238                 this.selNodes.splice(index, 1);
17239             }
17240             delete this.selMap[node.id];
17241             this.fireEvent("selectionchange", this, this.selNodes);
17242         }
17243     },
17244     
17245     /**
17246      * Clear all selections
17247      */
17248     clearSelections : function(suppressEvent){
17249         var sn = this.selNodes;
17250         if(sn.length > 0){
17251             for(var i = 0, len = sn.length; i < len; i++){
17252                 sn[i].ui.onSelectedChange(false);
17253             }
17254             this.selNodes = [];
17255             this.selMap = {};
17256             if(suppressEvent !== true){
17257                 this.fireEvent("selectionchange", this, this.selNodes);
17258             }
17259         }
17260     },
17261     
17262     /**
17263      * Returns true if the node is selected
17264      * @param {TreeNode} node The node to check
17265      * @return {Boolean}
17266      */
17267     isSelected : function(node){
17268         return this.selMap[node.id] ? true : false;  
17269     },
17270     
17271     /**
17272      * Returns an array of the selected nodes
17273      * @return {Array}
17274      */
17275     getSelectedNodes : function(){
17276         return this.selNodes;    
17277     },
17278
17279     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
17280
17281     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
17282
17283     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
17284 });/*
17285  * Based on:
17286  * Ext JS Library 1.1.1
17287  * Copyright(c) 2006-2007, Ext JS, LLC.
17288  *
17289  * Originally Released Under LGPL - original licence link has changed is not relivant.
17290  *
17291  * Fork - LGPL
17292  * <script type="text/javascript">
17293  */
17294  
17295 /**
17296  * @class Roo.tree.TreeNode
17297  * @extends Roo.data.Node
17298  * @cfg {String} text The text for this node
17299  * @cfg {Boolean} expanded true to start the node expanded
17300  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
17301  * @cfg {Boolean} allowDrop false if this node cannot be drop on
17302  * @cfg {Boolean} disabled true to start the node disabled
17303  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
17304  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
17305  * @cfg {String} cls A css class to be added to the node
17306  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
17307  * @cfg {String} href URL of the link used for the node (defaults to #)
17308  * @cfg {String} hrefTarget target frame for the link
17309  * @cfg {String} qtip An Ext QuickTip for the node
17310  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
17311  * @cfg {Boolean} singleClickExpand True for single click expand on this node
17312  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
17313  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
17314  * (defaults to undefined with no checkbox rendered)
17315  * @constructor
17316  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
17317  */
17318 Roo.tree.TreeNode = function(attributes){
17319     attributes = attributes || {};
17320     if(typeof attributes == "string"){
17321         attributes = {text: attributes};
17322     }
17323     this.childrenRendered = false;
17324     this.rendered = false;
17325     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
17326     this.expanded = attributes.expanded === true;
17327     this.isTarget = attributes.isTarget !== false;
17328     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
17329     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
17330
17331     /**
17332      * Read-only. The text for this node. To change it use setText().
17333      * @type String
17334      */
17335     this.text = attributes.text;
17336     /**
17337      * True if this node is disabled.
17338      * @type Boolean
17339      */
17340     this.disabled = attributes.disabled === true;
17341
17342     this.addEvents({
17343         /**
17344         * @event textchange
17345         * Fires when the text for this node is changed
17346         * @param {Node} this This node
17347         * @param {String} text The new text
17348         * @param {String} oldText The old text
17349         */
17350         "textchange" : true,
17351         /**
17352         * @event beforeexpand
17353         * Fires before this node is expanded, return false to cancel.
17354         * @param {Node} this This node
17355         * @param {Boolean} deep
17356         * @param {Boolean} anim
17357         */
17358         "beforeexpand" : true,
17359         /**
17360         * @event beforecollapse
17361         * Fires before this node is collapsed, return false to cancel.
17362         * @param {Node} this This node
17363         * @param {Boolean} deep
17364         * @param {Boolean} anim
17365         */
17366         "beforecollapse" : true,
17367         /**
17368         * @event expand
17369         * Fires when this node is expanded
17370         * @param {Node} this This node
17371         */
17372         "expand" : true,
17373         /**
17374         * @event disabledchange
17375         * Fires when the disabled status of this node changes
17376         * @param {Node} this This node
17377         * @param {Boolean} disabled
17378         */
17379         "disabledchange" : true,
17380         /**
17381         * @event collapse
17382         * Fires when this node is collapsed
17383         * @param {Node} this This node
17384         */
17385         "collapse" : true,
17386         /**
17387         * @event beforeclick
17388         * Fires before click processing. Return false to cancel the default action.
17389         * @param {Node} this This node
17390         * @param {Roo.EventObject} e The event object
17391         */
17392         "beforeclick":true,
17393         /**
17394         * @event checkchange
17395         * Fires when a node with a checkbox's checked property changes
17396         * @param {Node} this This node
17397         * @param {Boolean} checked
17398         */
17399         "checkchange":true,
17400         /**
17401         * @event click
17402         * Fires when this node is clicked
17403         * @param {Node} this This node
17404         * @param {Roo.EventObject} e The event object
17405         */
17406         "click":true,
17407         /**
17408         * @event dblclick
17409         * Fires when this node is double clicked
17410         * @param {Node} this This node
17411         * @param {Roo.EventObject} e The event object
17412         */
17413         "dblclick":true,
17414         /**
17415         * @event contextmenu
17416         * Fires when this node is right clicked
17417         * @param {Node} this This node
17418         * @param {Roo.EventObject} e The event object
17419         */
17420         "contextmenu":true,
17421         /**
17422         * @event beforechildrenrendered
17423         * Fires right before the child nodes for this node are rendered
17424         * @param {Node} this This node
17425         */
17426         "beforechildrenrendered":true
17427     });
17428
17429     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
17430
17431     /**
17432      * Read-only. The UI for this node
17433      * @type TreeNodeUI
17434      */
17435     this.ui = new uiClass(this);
17436     
17437     // finally support items[]
17438     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
17439         return;
17440     }
17441     
17442     
17443     Roo.each(this.attributes.items, function(c) {
17444         this.appendChild(Roo.factory(c,Roo.Tree));
17445     }, this);
17446     delete this.attributes.items;
17447     
17448     
17449     
17450 };
17451 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
17452     preventHScroll: true,
17453     /**
17454      * Returns true if this node is expanded
17455      * @return {Boolean}
17456      */
17457     isExpanded : function(){
17458         return this.expanded;
17459     },
17460
17461     /**
17462      * Returns the UI object for this node
17463      * @return {TreeNodeUI}
17464      */
17465     getUI : function(){
17466         return this.ui;
17467     },
17468
17469     // private override
17470     setFirstChild : function(node){
17471         var of = this.firstChild;
17472         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
17473         if(this.childrenRendered && of && node != of){
17474             of.renderIndent(true, true);
17475         }
17476         if(this.rendered){
17477             this.renderIndent(true, true);
17478         }
17479     },
17480
17481     // private override
17482     setLastChild : function(node){
17483         var ol = this.lastChild;
17484         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
17485         if(this.childrenRendered && ol && node != ol){
17486             ol.renderIndent(true, true);
17487         }
17488         if(this.rendered){
17489             this.renderIndent(true, true);
17490         }
17491     },
17492
17493     // these methods are overridden to provide lazy rendering support
17494     // private override
17495     appendChild : function()
17496     {
17497         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
17498         if(node && this.childrenRendered){
17499             node.render();
17500         }
17501         this.ui.updateExpandIcon();
17502         return node;
17503     },
17504
17505     // private override
17506     removeChild : function(node){
17507         this.ownerTree.getSelectionModel().unselect(node);
17508         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
17509         // if it's been rendered remove dom node
17510         if(this.childrenRendered){
17511             node.ui.remove();
17512         }
17513         if(this.childNodes.length < 1){
17514             this.collapse(false, false);
17515         }else{
17516             this.ui.updateExpandIcon();
17517         }
17518         if(!this.firstChild) {
17519             this.childrenRendered = false;
17520         }
17521         return node;
17522     },
17523
17524     // private override
17525     insertBefore : function(node, refNode){
17526         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
17527         if(newNode && refNode && this.childrenRendered){
17528             node.render();
17529         }
17530         this.ui.updateExpandIcon();
17531         return newNode;
17532     },
17533
17534     /**
17535      * Sets the text for this node
17536      * @param {String} text
17537      */
17538     setText : function(text){
17539         var oldText = this.text;
17540         this.text = text;
17541         this.attributes.text = text;
17542         if(this.rendered){ // event without subscribing
17543             this.ui.onTextChange(this, text, oldText);
17544         }
17545         this.fireEvent("textchange", this, text, oldText);
17546     },
17547
17548     /**
17549      * Triggers selection of this node
17550      */
17551     select : function(){
17552         this.getOwnerTree().getSelectionModel().select(this);
17553     },
17554
17555     /**
17556      * Triggers deselection of this node
17557      */
17558     unselect : function(){
17559         this.getOwnerTree().getSelectionModel().unselect(this);
17560     },
17561
17562     /**
17563      * Returns true if this node is selected
17564      * @return {Boolean}
17565      */
17566     isSelected : function(){
17567         return this.getOwnerTree().getSelectionModel().isSelected(this);
17568     },
17569
17570     /**
17571      * Expand this node.
17572      * @param {Boolean} deep (optional) True to expand all children as well
17573      * @param {Boolean} anim (optional) false to cancel the default animation
17574      * @param {Function} callback (optional) A callback to be called when
17575      * expanding this node completes (does not wait for deep expand to complete).
17576      * Called with 1 parameter, this node.
17577      */
17578     expand : function(deep, anim, callback){
17579         if(!this.expanded){
17580             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
17581                 return;
17582             }
17583             if(!this.childrenRendered){
17584                 this.renderChildren();
17585             }
17586             this.expanded = true;
17587             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
17588                 this.ui.animExpand(function(){
17589                     this.fireEvent("expand", this);
17590                     if(typeof callback == "function"){
17591                         callback(this);
17592                     }
17593                     if(deep === true){
17594                         this.expandChildNodes(true);
17595                     }
17596                 }.createDelegate(this));
17597                 return;
17598             }else{
17599                 this.ui.expand();
17600                 this.fireEvent("expand", this);
17601                 if(typeof callback == "function"){
17602                     callback(this);
17603                 }
17604             }
17605         }else{
17606            if(typeof callback == "function"){
17607                callback(this);
17608            }
17609         }
17610         if(deep === true){
17611             this.expandChildNodes(true);
17612         }
17613     },
17614
17615     isHiddenRoot : function(){
17616         return this.isRoot && !this.getOwnerTree().rootVisible;
17617     },
17618
17619     /**
17620      * Collapse this node.
17621      * @param {Boolean} deep (optional) True to collapse all children as well
17622      * @param {Boolean} anim (optional) false to cancel the default animation
17623      */
17624     collapse : function(deep, anim){
17625         if(this.expanded && !this.isHiddenRoot()){
17626             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
17627                 return;
17628             }
17629             this.expanded = false;
17630             if((this.getOwnerTree().animate && anim !== false) || anim){
17631                 this.ui.animCollapse(function(){
17632                     this.fireEvent("collapse", this);
17633                     if(deep === true){
17634                         this.collapseChildNodes(true);
17635                     }
17636                 }.createDelegate(this));
17637                 return;
17638             }else{
17639                 this.ui.collapse();
17640                 this.fireEvent("collapse", this);
17641             }
17642         }
17643         if(deep === true){
17644             var cs = this.childNodes;
17645             for(var i = 0, len = cs.length; i < len; i++) {
17646                 cs[i].collapse(true, false);
17647             }
17648         }
17649     },
17650
17651     // private
17652     delayedExpand : function(delay){
17653         if(!this.expandProcId){
17654             this.expandProcId = this.expand.defer(delay, this);
17655         }
17656     },
17657
17658     // private
17659     cancelExpand : function(){
17660         if(this.expandProcId){
17661             clearTimeout(this.expandProcId);
17662         }
17663         this.expandProcId = false;
17664     },
17665
17666     /**
17667      * Toggles expanded/collapsed state of the node
17668      */
17669     toggle : function(){
17670         if(this.expanded){
17671             this.collapse();
17672         }else{
17673             this.expand();
17674         }
17675     },
17676
17677     /**
17678      * Ensures all parent nodes are expanded
17679      */
17680     ensureVisible : function(callback){
17681         var tree = this.getOwnerTree();
17682         tree.expandPath(this.parentNode.getPath(), false, function(){
17683             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
17684             Roo.callback(callback);
17685         }.createDelegate(this));
17686     },
17687
17688     /**
17689      * Expand all child nodes
17690      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
17691      */
17692     expandChildNodes : function(deep){
17693         var cs = this.childNodes;
17694         for(var i = 0, len = cs.length; i < len; i++) {
17695                 cs[i].expand(deep);
17696         }
17697     },
17698
17699     /**
17700      * Collapse all child nodes
17701      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
17702      */
17703     collapseChildNodes : function(deep){
17704         var cs = this.childNodes;
17705         for(var i = 0, len = cs.length; i < len; i++) {
17706                 cs[i].collapse(deep);
17707         }
17708     },
17709
17710     /**
17711      * Disables this node
17712      */
17713     disable : function(){
17714         this.disabled = true;
17715         this.unselect();
17716         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17717             this.ui.onDisableChange(this, true);
17718         }
17719         this.fireEvent("disabledchange", this, true);
17720     },
17721
17722     /**
17723      * Enables this node
17724      */
17725     enable : function(){
17726         this.disabled = false;
17727         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
17728             this.ui.onDisableChange(this, false);
17729         }
17730         this.fireEvent("disabledchange", this, false);
17731     },
17732
17733     // private
17734     renderChildren : function(suppressEvent){
17735         if(suppressEvent !== false){
17736             this.fireEvent("beforechildrenrendered", this);
17737         }
17738         var cs = this.childNodes;
17739         for(var i = 0, len = cs.length; i < len; i++){
17740             cs[i].render(true);
17741         }
17742         this.childrenRendered = true;
17743     },
17744
17745     // private
17746     sort : function(fn, scope){
17747         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
17748         if(this.childrenRendered){
17749             var cs = this.childNodes;
17750             for(var i = 0, len = cs.length; i < len; i++){
17751                 cs[i].render(true);
17752             }
17753         }
17754     },
17755
17756     // private
17757     render : function(bulkRender){
17758         this.ui.render(bulkRender);
17759         if(!this.rendered){
17760             this.rendered = true;
17761             if(this.expanded){
17762                 this.expanded = false;
17763                 this.expand(false, false);
17764             }
17765         }
17766     },
17767
17768     // private
17769     renderIndent : function(deep, refresh){
17770         if(refresh){
17771             this.ui.childIndent = null;
17772         }
17773         this.ui.renderIndent();
17774         if(deep === true && this.childrenRendered){
17775             var cs = this.childNodes;
17776             for(var i = 0, len = cs.length; i < len; i++){
17777                 cs[i].renderIndent(true, refresh);
17778             }
17779         }
17780     }
17781 });/*
17782  * Based on:
17783  * Ext JS Library 1.1.1
17784  * Copyright(c) 2006-2007, Ext JS, LLC.
17785  *
17786  * Originally Released Under LGPL - original licence link has changed is not relivant.
17787  *
17788  * Fork - LGPL
17789  * <script type="text/javascript">
17790  */
17791  
17792 /**
17793  * @class Roo.tree.AsyncTreeNode
17794  * @extends Roo.tree.TreeNode
17795  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
17796  * @constructor
17797  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
17798  */
17799  Roo.tree.AsyncTreeNode = function(config){
17800     this.loaded = false;
17801     this.loading = false;
17802     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
17803     /**
17804     * @event beforeload
17805     * Fires before this node is loaded, return false to cancel
17806     * @param {Node} this This node
17807     */
17808     this.addEvents({'beforeload':true, 'load': true});
17809     /**
17810     * @event load
17811     * Fires when this node is loaded
17812     * @param {Node} this This node
17813     */
17814     /**
17815      * The loader used by this node (defaults to using the tree's defined loader)
17816      * @type TreeLoader
17817      * @property loader
17818      */
17819 };
17820 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
17821     expand : function(deep, anim, callback){
17822         if(this.loading){ // if an async load is already running, waiting til it's done
17823             var timer;
17824             var f = function(){
17825                 if(!this.loading){ // done loading
17826                     clearInterval(timer);
17827                     this.expand(deep, anim, callback);
17828                 }
17829             }.createDelegate(this);
17830             timer = setInterval(f, 200);
17831             return;
17832         }
17833         if(!this.loaded){
17834             if(this.fireEvent("beforeload", this) === false){
17835                 return;
17836             }
17837             this.loading = true;
17838             this.ui.beforeLoad(this);
17839             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
17840             if(loader){
17841                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
17842                 return;
17843             }
17844         }
17845         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
17846     },
17847     
17848     /**
17849      * Returns true if this node is currently loading
17850      * @return {Boolean}
17851      */
17852     isLoading : function(){
17853         return this.loading;  
17854     },
17855     
17856     loadComplete : function(deep, anim, callback){
17857         this.loading = false;
17858         this.loaded = true;
17859         this.ui.afterLoad(this);
17860         this.fireEvent("load", this);
17861         this.expand(deep, anim, callback);
17862     },
17863     
17864     /**
17865      * Returns true if this node has been loaded
17866      * @return {Boolean}
17867      */
17868     isLoaded : function(){
17869         return this.loaded;
17870     },
17871     
17872     hasChildNodes : function(){
17873         if(!this.isLeaf() && !this.loaded){
17874             return true;
17875         }else{
17876             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
17877         }
17878     },
17879
17880     /**
17881      * Trigger a reload for this node
17882      * @param {Function} callback
17883      */
17884     reload : function(callback){
17885         this.collapse(false, false);
17886         while(this.firstChild){
17887             this.removeChild(this.firstChild);
17888         }
17889         this.childrenRendered = false;
17890         this.loaded = false;
17891         if(this.isHiddenRoot()){
17892             this.expanded = false;
17893         }
17894         this.expand(false, false, callback);
17895     }
17896 });/*
17897  * Based on:
17898  * Ext JS Library 1.1.1
17899  * Copyright(c) 2006-2007, Ext JS, LLC.
17900  *
17901  * Originally Released Under LGPL - original licence link has changed is not relivant.
17902  *
17903  * Fork - LGPL
17904  * <script type="text/javascript">
17905  */
17906  
17907 /**
17908  * @class Roo.tree.TreeNodeUI
17909  * @constructor
17910  * @param {Object} node The node to render
17911  * The TreeNode UI implementation is separate from the
17912  * tree implementation. Unless you are customizing the tree UI,
17913  * you should never have to use this directly.
17914  */
17915 Roo.tree.TreeNodeUI = function(node){
17916     this.node = node;
17917     this.rendered = false;
17918     this.animating = false;
17919     this.emptyIcon = Roo.BLANK_IMAGE_URL;
17920 };
17921
17922 Roo.tree.TreeNodeUI.prototype = {
17923     removeChild : function(node){
17924         if(this.rendered){
17925             this.ctNode.removeChild(node.ui.getEl());
17926         }
17927     },
17928
17929     beforeLoad : function(){
17930          this.addClass("x-tree-node-loading");
17931     },
17932
17933     afterLoad : function(){
17934          this.removeClass("x-tree-node-loading");
17935     },
17936
17937     onTextChange : function(node, text, oldText){
17938         if(this.rendered){
17939             this.textNode.innerHTML = text;
17940         }
17941     },
17942
17943     onDisableChange : function(node, state){
17944         this.disabled = state;
17945         if(state){
17946             this.addClass("x-tree-node-disabled");
17947         }else{
17948             this.removeClass("x-tree-node-disabled");
17949         }
17950     },
17951
17952     onSelectedChange : function(state){
17953         if(state){
17954             this.focus();
17955             this.addClass("x-tree-selected");
17956         }else{
17957             //this.blur();
17958             this.removeClass("x-tree-selected");
17959         }
17960     },
17961
17962     onMove : function(tree, node, oldParent, newParent, index, refNode){
17963         this.childIndent = null;
17964         if(this.rendered){
17965             var targetNode = newParent.ui.getContainer();
17966             if(!targetNode){//target not rendered
17967                 this.holder = document.createElement("div");
17968                 this.holder.appendChild(this.wrap);
17969                 return;
17970             }
17971             var insertBefore = refNode ? refNode.ui.getEl() : null;
17972             if(insertBefore){
17973                 targetNode.insertBefore(this.wrap, insertBefore);
17974             }else{
17975                 targetNode.appendChild(this.wrap);
17976             }
17977             this.node.renderIndent(true);
17978         }
17979     },
17980
17981     addClass : function(cls){
17982         if(this.elNode){
17983             Roo.fly(this.elNode).addClass(cls);
17984         }
17985     },
17986
17987     removeClass : function(cls){
17988         if(this.elNode){
17989             Roo.fly(this.elNode).removeClass(cls);
17990         }
17991     },
17992
17993     remove : function(){
17994         if(this.rendered){
17995             this.holder = document.createElement("div");
17996             this.holder.appendChild(this.wrap);
17997         }
17998     },
17999
18000     fireEvent : function(){
18001         return this.node.fireEvent.apply(this.node, arguments);
18002     },
18003
18004     initEvents : function(){
18005         this.node.on("move", this.onMove, this);
18006         var E = Roo.EventManager;
18007         var a = this.anchor;
18008
18009         var el = Roo.fly(a, '_treeui');
18010
18011         if(Roo.isOpera){ // opera render bug ignores the CSS
18012             el.setStyle("text-decoration", "none");
18013         }
18014
18015         el.on("click", this.onClick, this);
18016         el.on("dblclick", this.onDblClick, this);
18017
18018         if(this.checkbox){
18019             Roo.EventManager.on(this.checkbox,
18020                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
18021         }
18022
18023         el.on("contextmenu", this.onContextMenu, this);
18024
18025         var icon = Roo.fly(this.iconNode);
18026         icon.on("click", this.onClick, this);
18027         icon.on("dblclick", this.onDblClick, this);
18028         icon.on("contextmenu", this.onContextMenu, this);
18029         E.on(this.ecNode, "click", this.ecClick, this, true);
18030
18031         if(this.node.disabled){
18032             this.addClass("x-tree-node-disabled");
18033         }
18034         if(this.node.hidden){
18035             this.addClass("x-tree-node-disabled");
18036         }
18037         var ot = this.node.getOwnerTree();
18038         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
18039         if(dd && (!this.node.isRoot || ot.rootVisible)){
18040             Roo.dd.Registry.register(this.elNode, {
18041                 node: this.node,
18042                 handles: this.getDDHandles(),
18043                 isHandle: false
18044             });
18045         }
18046     },
18047
18048     getDDHandles : function(){
18049         return [this.iconNode, this.textNode];
18050     },
18051
18052     hide : function(){
18053         if(this.rendered){
18054             this.wrap.style.display = "none";
18055         }
18056     },
18057
18058     show : function(){
18059         if(this.rendered){
18060             this.wrap.style.display = "";
18061         }
18062     },
18063
18064     onContextMenu : function(e){
18065         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
18066             e.preventDefault();
18067             this.focus();
18068             this.fireEvent("contextmenu", this.node, e);
18069         }
18070     },
18071
18072     onClick : function(e){
18073         if(this.dropping){
18074             e.stopEvent();
18075             return;
18076         }
18077         if(this.fireEvent("beforeclick", this.node, e) !== false){
18078             if(!this.disabled && this.node.attributes.href){
18079                 this.fireEvent("click", this.node, e);
18080                 return;
18081             }
18082             e.preventDefault();
18083             if(this.disabled){
18084                 return;
18085             }
18086
18087             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
18088                 this.node.toggle();
18089             }
18090
18091             this.fireEvent("click", this.node, e);
18092         }else{
18093             e.stopEvent();
18094         }
18095     },
18096
18097     onDblClick : function(e){
18098         e.preventDefault();
18099         if(this.disabled){
18100             return;
18101         }
18102         if(this.checkbox){
18103             this.toggleCheck();
18104         }
18105         if(!this.animating && this.node.hasChildNodes()){
18106             this.node.toggle();
18107         }
18108         this.fireEvent("dblclick", this.node, e);
18109     },
18110
18111     onCheckChange : function(){
18112         var checked = this.checkbox.checked;
18113         this.node.attributes.checked = checked;
18114         this.fireEvent('checkchange', this.node, checked);
18115     },
18116
18117     ecClick : function(e){
18118         if(!this.animating && this.node.hasChildNodes()){
18119             this.node.toggle();
18120         }
18121     },
18122
18123     startDrop : function(){
18124         this.dropping = true;
18125     },
18126
18127     // delayed drop so the click event doesn't get fired on a drop
18128     endDrop : function(){
18129        setTimeout(function(){
18130            this.dropping = false;
18131        }.createDelegate(this), 50);
18132     },
18133
18134     expand : function(){
18135         this.updateExpandIcon();
18136         this.ctNode.style.display = "";
18137     },
18138
18139     focus : function(){
18140         if(!this.node.preventHScroll){
18141             try{this.anchor.focus();
18142             }catch(e){}
18143         }else if(!Roo.isIE){
18144             try{
18145                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
18146                 var l = noscroll.scrollLeft;
18147                 this.anchor.focus();
18148                 noscroll.scrollLeft = l;
18149             }catch(e){}
18150         }
18151     },
18152
18153     toggleCheck : function(value){
18154         var cb = this.checkbox;
18155         if(cb){
18156             cb.checked = (value === undefined ? !cb.checked : value);
18157         }
18158     },
18159
18160     blur : function(){
18161         try{
18162             this.anchor.blur();
18163         }catch(e){}
18164     },
18165
18166     animExpand : function(callback){
18167         var ct = Roo.get(this.ctNode);
18168         ct.stopFx();
18169         if(!this.node.hasChildNodes()){
18170             this.updateExpandIcon();
18171             this.ctNode.style.display = "";
18172             Roo.callback(callback);
18173             return;
18174         }
18175         this.animating = true;
18176         this.updateExpandIcon();
18177
18178         ct.slideIn('t', {
18179            callback : function(){
18180                this.animating = false;
18181                Roo.callback(callback);
18182             },
18183             scope: this,
18184             duration: this.node.ownerTree.duration || .25
18185         });
18186     },
18187
18188     highlight : function(){
18189         var tree = this.node.getOwnerTree();
18190         Roo.fly(this.wrap).highlight(
18191             tree.hlColor || "C3DAF9",
18192             {endColor: tree.hlBaseColor}
18193         );
18194     },
18195
18196     collapse : function(){
18197         this.updateExpandIcon();
18198         this.ctNode.style.display = "none";
18199     },
18200
18201     animCollapse : function(callback){
18202         var ct = Roo.get(this.ctNode);
18203         ct.enableDisplayMode('block');
18204         ct.stopFx();
18205
18206         this.animating = true;
18207         this.updateExpandIcon();
18208
18209         ct.slideOut('t', {
18210             callback : function(){
18211                this.animating = false;
18212                Roo.callback(callback);
18213             },
18214             scope: this,
18215             duration: this.node.ownerTree.duration || .25
18216         });
18217     },
18218
18219     getContainer : function(){
18220         return this.ctNode;
18221     },
18222
18223     getEl : function(){
18224         return this.wrap;
18225     },
18226
18227     appendDDGhost : function(ghostNode){
18228         ghostNode.appendChild(this.elNode.cloneNode(true));
18229     },
18230
18231     getDDRepairXY : function(){
18232         return Roo.lib.Dom.getXY(this.iconNode);
18233     },
18234
18235     onRender : function(){
18236         this.render();
18237     },
18238
18239     render : function(bulkRender){
18240         var n = this.node, a = n.attributes;
18241         var targetNode = n.parentNode ?
18242               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
18243
18244         if(!this.rendered){
18245             this.rendered = true;
18246
18247             this.renderElements(n, a, targetNode, bulkRender);
18248
18249             if(a.qtip){
18250                if(this.textNode.setAttributeNS){
18251                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
18252                    if(a.qtipTitle){
18253                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
18254                    }
18255                }else{
18256                    this.textNode.setAttribute("ext:qtip", a.qtip);
18257                    if(a.qtipTitle){
18258                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
18259                    }
18260                }
18261             }else if(a.qtipCfg){
18262                 a.qtipCfg.target = Roo.id(this.textNode);
18263                 Roo.QuickTips.register(a.qtipCfg);
18264             }
18265             this.initEvents();
18266             if(!this.node.expanded){
18267                 this.updateExpandIcon();
18268             }
18269         }else{
18270             if(bulkRender === true) {
18271                 targetNode.appendChild(this.wrap);
18272             }
18273         }
18274     },
18275
18276     renderElements : function(n, a, targetNode, bulkRender)
18277     {
18278         // add some indent caching, this helps performance when rendering a large tree
18279         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
18280         var t = n.getOwnerTree();
18281         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
18282         if (typeof(n.attributes.html) != 'undefined') {
18283             txt = n.attributes.html;
18284         }
18285         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
18286         var cb = typeof a.checked == 'boolean';
18287         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
18288         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
18289             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
18290             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
18291             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
18292             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
18293             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
18294              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
18295                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
18296             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
18297             "</li>"];
18298
18299         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
18300             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
18301                                 n.nextSibling.ui.getEl(), buf.join(""));
18302         }else{
18303             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
18304         }
18305
18306         this.elNode = this.wrap.childNodes[0];
18307         this.ctNode = this.wrap.childNodes[1];
18308         var cs = this.elNode.childNodes;
18309         this.indentNode = cs[0];
18310         this.ecNode = cs[1];
18311         this.iconNode = cs[2];
18312         var index = 3;
18313         if(cb){
18314             this.checkbox = cs[3];
18315             index++;
18316         }
18317         this.anchor = cs[index];
18318         this.textNode = cs[index].firstChild;
18319     },
18320
18321     getAnchor : function(){
18322         return this.anchor;
18323     },
18324
18325     getTextEl : function(){
18326         return this.textNode;
18327     },
18328
18329     getIconEl : function(){
18330         return this.iconNode;
18331     },
18332
18333     isChecked : function(){
18334         return this.checkbox ? this.checkbox.checked : false;
18335     },
18336
18337     updateExpandIcon : function(){
18338         if(this.rendered){
18339             var n = this.node, c1, c2;
18340             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
18341             var hasChild = n.hasChildNodes();
18342             if(hasChild){
18343                 if(n.expanded){
18344                     cls += "-minus";
18345                     c1 = "x-tree-node-collapsed";
18346                     c2 = "x-tree-node-expanded";
18347                 }else{
18348                     cls += "-plus";
18349                     c1 = "x-tree-node-expanded";
18350                     c2 = "x-tree-node-collapsed";
18351                 }
18352                 if(this.wasLeaf){
18353                     this.removeClass("x-tree-node-leaf");
18354                     this.wasLeaf = false;
18355                 }
18356                 if(this.c1 != c1 || this.c2 != c2){
18357                     Roo.fly(this.elNode).replaceClass(c1, c2);
18358                     this.c1 = c1; this.c2 = c2;
18359                 }
18360             }else{
18361                 // this changes non-leafs into leafs if they have no children.
18362                 // it's not very rational behaviour..
18363                 
18364                 if(!this.wasLeaf && this.node.leaf){
18365                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
18366                     delete this.c1;
18367                     delete this.c2;
18368                     this.wasLeaf = true;
18369                 }
18370             }
18371             var ecc = "x-tree-ec-icon "+cls;
18372             if(this.ecc != ecc){
18373                 this.ecNode.className = ecc;
18374                 this.ecc = ecc;
18375             }
18376         }
18377     },
18378
18379     getChildIndent : function(){
18380         if(!this.childIndent){
18381             var buf = [];
18382             var p = this.node;
18383             while(p){
18384                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
18385                     if(!p.isLast()) {
18386                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
18387                     } else {
18388                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
18389                     }
18390                 }
18391                 p = p.parentNode;
18392             }
18393             this.childIndent = buf.join("");
18394         }
18395         return this.childIndent;
18396     },
18397
18398     renderIndent : function(){
18399         if(this.rendered){
18400             var indent = "";
18401             var p = this.node.parentNode;
18402             if(p){
18403                 indent = p.ui.getChildIndent();
18404             }
18405             if(this.indentMarkup != indent){ // don't rerender if not required
18406                 this.indentNode.innerHTML = indent;
18407                 this.indentMarkup = indent;
18408             }
18409             this.updateExpandIcon();
18410         }
18411     }
18412 };
18413
18414 Roo.tree.RootTreeNodeUI = function(){
18415     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
18416 };
18417 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
18418     render : function(){
18419         if(!this.rendered){
18420             var targetNode = this.node.ownerTree.innerCt.dom;
18421             this.node.expanded = true;
18422             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
18423             this.wrap = this.ctNode = targetNode.firstChild;
18424         }
18425     },
18426     collapse : function(){
18427     },
18428     expand : function(){
18429     }
18430 });/*
18431  * Based on:
18432  * Ext JS Library 1.1.1
18433  * Copyright(c) 2006-2007, Ext JS, LLC.
18434  *
18435  * Originally Released Under LGPL - original licence link has changed is not relivant.
18436  *
18437  * Fork - LGPL
18438  * <script type="text/javascript">
18439  */
18440 /**
18441  * @class Roo.tree.TreeLoader
18442  * @extends Roo.util.Observable
18443  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
18444  * nodes from a specified URL. The response must be a javascript Array definition
18445  * who's elements are node definition objects. eg:
18446  * <pre><code>
18447 {  success : true,
18448    data :      [
18449    
18450     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
18451     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
18452     ]
18453 }
18454
18455
18456 </code></pre>
18457  * <br><br>
18458  * The old style respose with just an array is still supported, but not recommended.
18459  * <br><br>
18460  *
18461  * A server request is sent, and child nodes are loaded only when a node is expanded.
18462  * The loading node's id is passed to the server under the parameter name "node" to
18463  * enable the server to produce the correct child nodes.
18464  * <br><br>
18465  * To pass extra parameters, an event handler may be attached to the "beforeload"
18466  * event, and the parameters specified in the TreeLoader's baseParams property:
18467  * <pre><code>
18468     myTreeLoader.on("beforeload", function(treeLoader, node) {
18469         this.baseParams.category = node.attributes.category;
18470     }, this);
18471 </code></pre><
18472  * This would pass an HTTP parameter called "category" to the server containing
18473  * the value of the Node's "category" attribute.
18474  * @constructor
18475  * Creates a new Treeloader.
18476  * @param {Object} config A config object containing config properties.
18477  */
18478 Roo.tree.TreeLoader = function(config){
18479     this.baseParams = {};
18480     this.requestMethod = "POST";
18481     Roo.apply(this, config);
18482
18483     this.addEvents({
18484     
18485         /**
18486          * @event beforeload
18487          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
18488          * @param {Object} This TreeLoader object.
18489          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18490          * @param {Object} callback The callback function specified in the {@link #load} call.
18491          */
18492         beforeload : true,
18493         /**
18494          * @event load
18495          * Fires when the node has been successfuly loaded.
18496          * @param {Object} This TreeLoader object.
18497          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18498          * @param {Object} response The response object containing the data from the server.
18499          */
18500         load : true,
18501         /**
18502          * @event loadexception
18503          * Fires if the network request failed.
18504          * @param {Object} This TreeLoader object.
18505          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
18506          * @param {Object} response The response object containing the data from the server.
18507          */
18508         loadexception : true,
18509         /**
18510          * @event create
18511          * Fires before a node is created, enabling you to return custom Node types 
18512          * @param {Object} This TreeLoader object.
18513          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
18514          */
18515         create : true
18516     });
18517
18518     Roo.tree.TreeLoader.superclass.constructor.call(this);
18519 };
18520
18521 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
18522     /**
18523     * @cfg {String} dataUrl The URL from which to request a Json string which
18524     * specifies an array of node definition object representing the child nodes
18525     * to be loaded.
18526     */
18527     /**
18528     * @cfg {String} requestMethod either GET or POST
18529     * defaults to POST (due to BC)
18530     * to be loaded.
18531     */
18532     /**
18533     * @cfg {Object} baseParams (optional) An object containing properties which
18534     * specify HTTP parameters to be passed to each request for child nodes.
18535     */
18536     /**
18537     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
18538     * created by this loader. If the attributes sent by the server have an attribute in this object,
18539     * they take priority.
18540     */
18541     /**
18542     * @cfg {Object} uiProviders (optional) An object containing properties which
18543     * 
18544     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
18545     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
18546     * <i>uiProvider</i> attribute of a returned child node is a string rather
18547     * than a reference to a TreeNodeUI implementation, this that string value
18548     * is used as a property name in the uiProviders object. You can define the provider named
18549     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
18550     */
18551     uiProviders : {},
18552
18553     /**
18554     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
18555     * child nodes before loading.
18556     */
18557     clearOnLoad : true,
18558
18559     /**
18560     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
18561     * property on loading, rather than expecting an array. (eg. more compatible to a standard
18562     * Grid query { data : [ .....] }
18563     */
18564     
18565     root : false,
18566      /**
18567     * @cfg {String} queryParam (optional) 
18568     * Name of the query as it will be passed on the querystring (defaults to 'node')
18569     * eg. the request will be ?node=[id]
18570     */
18571     
18572     
18573     queryParam: false,
18574     
18575     /**
18576      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
18577      * This is called automatically when a node is expanded, but may be used to reload
18578      * a node (or append new children if the {@link #clearOnLoad} option is false.)
18579      * @param {Roo.tree.TreeNode} node
18580      * @param {Function} callback
18581      */
18582     load : function(node, callback){
18583         if(this.clearOnLoad){
18584             while(node.firstChild){
18585                 node.removeChild(node.firstChild);
18586             }
18587         }
18588         if(node.attributes.children){ // preloaded json children
18589             var cs = node.attributes.children;
18590             for(var i = 0, len = cs.length; i < len; i++){
18591                 node.appendChild(this.createNode(cs[i]));
18592             }
18593             if(typeof callback == "function"){
18594                 callback();
18595             }
18596         }else if(this.dataUrl){
18597             this.requestData(node, callback);
18598         }
18599     },
18600
18601     getParams: function(node){
18602         var buf = [], bp = this.baseParams;
18603         for(var key in bp){
18604             if(typeof bp[key] != "function"){
18605                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
18606             }
18607         }
18608         var n = this.queryParam === false ? 'node' : this.queryParam;
18609         buf.push(n + "=", encodeURIComponent(node.id));
18610         return buf.join("");
18611     },
18612
18613     requestData : function(node, callback){
18614         if(this.fireEvent("beforeload", this, node, callback) !== false){
18615             this.transId = Roo.Ajax.request({
18616                 method:this.requestMethod,
18617                 url: this.dataUrl||this.url,
18618                 success: this.handleResponse,
18619                 failure: this.handleFailure,
18620                 scope: this,
18621                 argument: {callback: callback, node: node},
18622                 params: this.getParams(node)
18623             });
18624         }else{
18625             // if the load is cancelled, make sure we notify
18626             // the node that we are done
18627             if(typeof callback == "function"){
18628                 callback();
18629             }
18630         }
18631     },
18632
18633     isLoading : function(){
18634         return this.transId ? true : false;
18635     },
18636
18637     abort : function(){
18638         if(this.isLoading()){
18639             Roo.Ajax.abort(this.transId);
18640         }
18641     },
18642
18643     // private
18644     createNode : function(attr)
18645     {
18646         // apply baseAttrs, nice idea Corey!
18647         if(this.baseAttrs){
18648             Roo.applyIf(attr, this.baseAttrs);
18649         }
18650         if(this.applyLoader !== false){
18651             attr.loader = this;
18652         }
18653         // uiProvider = depreciated..
18654         
18655         if(typeof(attr.uiProvider) == 'string'){
18656            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
18657                 /**  eval:var:attr */ eval(attr.uiProvider);
18658         }
18659         if(typeof(this.uiProviders['default']) != 'undefined') {
18660             attr.uiProvider = this.uiProviders['default'];
18661         }
18662         
18663         this.fireEvent('create', this, attr);
18664         
18665         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
18666         return(attr.leaf ?
18667                         new Roo.tree.TreeNode(attr) :
18668                         new Roo.tree.AsyncTreeNode(attr));
18669     },
18670
18671     processResponse : function(response, node, callback)
18672     {
18673         var json = response.responseText;
18674         try {
18675             
18676             var o = Roo.decode(json);
18677             
18678             if (this.root === false && typeof(o.success) != undefined) {
18679                 this.root = 'data'; // the default behaviour for list like data..
18680                 }
18681                 
18682             if (this.root !== false &&  !o.success) {
18683                 // it's a failure condition.
18684                 var a = response.argument;
18685                 this.fireEvent("loadexception", this, a.node, response);
18686                 Roo.log("Load failed - should have a handler really");
18687                 return;
18688             }
18689             
18690             
18691             
18692             if (this.root !== false) {
18693                  o = o[this.root];
18694             }
18695             
18696             for(var i = 0, len = o.length; i < len; i++){
18697                 var n = this.createNode(o[i]);
18698                 if(n){
18699                     node.appendChild(n);
18700                 }
18701             }
18702             if(typeof callback == "function"){
18703                 callback(this, node);
18704             }
18705         }catch(e){
18706             this.handleFailure(response);
18707         }
18708     },
18709
18710     handleResponse : function(response){
18711         this.transId = false;
18712         var a = response.argument;
18713         this.processResponse(response, a.node, a.callback);
18714         this.fireEvent("load", this, a.node, response);
18715     },
18716
18717     handleFailure : function(response)
18718     {
18719         // should handle failure better..
18720         this.transId = false;
18721         var a = response.argument;
18722         this.fireEvent("loadexception", this, a.node, response);
18723         if(typeof a.callback == "function"){
18724             a.callback(this, a.node);
18725         }
18726     }
18727 });/*
18728  * Based on:
18729  * Ext JS Library 1.1.1
18730  * Copyright(c) 2006-2007, Ext JS, LLC.
18731  *
18732  * Originally Released Under LGPL - original licence link has changed is not relivant.
18733  *
18734  * Fork - LGPL
18735  * <script type="text/javascript">
18736  */
18737
18738 /**
18739 * @class Roo.tree.TreeFilter
18740 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
18741 * @param {TreePanel} tree
18742 * @param {Object} config (optional)
18743  */
18744 Roo.tree.TreeFilter = function(tree, config){
18745     this.tree = tree;
18746     this.filtered = {};
18747     Roo.apply(this, config);
18748 };
18749
18750 Roo.tree.TreeFilter.prototype = {
18751     clearBlank:false,
18752     reverse:false,
18753     autoClear:false,
18754     remove:false,
18755
18756      /**
18757      * Filter the data by a specific attribute.
18758      * @param {String/RegExp} value Either string that the attribute value
18759      * should start with or a RegExp to test against the attribute
18760      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
18761      * @param {TreeNode} startNode (optional) The node to start the filter at.
18762      */
18763     filter : function(value, attr, startNode){
18764         attr = attr || "text";
18765         var f;
18766         if(typeof value == "string"){
18767             var vlen = value.length;
18768             // auto clear empty filter
18769             if(vlen == 0 && this.clearBlank){
18770                 this.clear();
18771                 return;
18772             }
18773             value = value.toLowerCase();
18774             f = function(n){
18775                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
18776             };
18777         }else if(value.exec){ // regex?
18778             f = function(n){
18779                 return value.test(n.attributes[attr]);
18780             };
18781         }else{
18782             throw 'Illegal filter type, must be string or regex';
18783         }
18784         this.filterBy(f, null, startNode);
18785         },
18786
18787     /**
18788      * Filter by a function. The passed function will be called with each
18789      * node in the tree (or from the startNode). If the function returns true, the node is kept
18790      * otherwise it is filtered. If a node is filtered, its children are also filtered.
18791      * @param {Function} fn The filter function
18792      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
18793      */
18794     filterBy : function(fn, scope, startNode){
18795         startNode = startNode || this.tree.root;
18796         if(this.autoClear){
18797             this.clear();
18798         }
18799         var af = this.filtered, rv = this.reverse;
18800         var f = function(n){
18801             if(n == startNode){
18802                 return true;
18803             }
18804             if(af[n.id]){
18805                 return false;
18806             }
18807             var m = fn.call(scope || n, n);
18808             if(!m || rv){
18809                 af[n.id] = n;
18810                 n.ui.hide();
18811                 return false;
18812             }
18813             return true;
18814         };
18815         startNode.cascade(f);
18816         if(this.remove){
18817            for(var id in af){
18818                if(typeof id != "function"){
18819                    var n = af[id];
18820                    if(n && n.parentNode){
18821                        n.parentNode.removeChild(n);
18822                    }
18823                }
18824            }
18825         }
18826     },
18827
18828     /**
18829      * Clears the current filter. Note: with the "remove" option
18830      * set a filter cannot be cleared.
18831      */
18832     clear : function(){
18833         var t = this.tree;
18834         var af = this.filtered;
18835         for(var id in af){
18836             if(typeof id != "function"){
18837                 var n = af[id];
18838                 if(n){
18839                     n.ui.show();
18840                 }
18841             }
18842         }
18843         this.filtered = {};
18844     }
18845 };
18846 /*
18847  * Based on:
18848  * Ext JS Library 1.1.1
18849  * Copyright(c) 2006-2007, Ext JS, LLC.
18850  *
18851  * Originally Released Under LGPL - original licence link has changed is not relivant.
18852  *
18853  * Fork - LGPL
18854  * <script type="text/javascript">
18855  */
18856  
18857
18858 /**
18859  * @class Roo.tree.TreeSorter
18860  * Provides sorting of nodes in a TreePanel
18861  * 
18862  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
18863  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
18864  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
18865  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
18866  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
18867  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
18868  * @constructor
18869  * @param {TreePanel} tree
18870  * @param {Object} config
18871  */
18872 Roo.tree.TreeSorter = function(tree, config){
18873     Roo.apply(this, config);
18874     tree.on("beforechildrenrendered", this.doSort, this);
18875     tree.on("append", this.updateSort, this);
18876     tree.on("insert", this.updateSort, this);
18877     
18878     var dsc = this.dir && this.dir.toLowerCase() == "desc";
18879     var p = this.property || "text";
18880     var sortType = this.sortType;
18881     var fs = this.folderSort;
18882     var cs = this.caseSensitive === true;
18883     var leafAttr = this.leafAttr || 'leaf';
18884
18885     this.sortFn = function(n1, n2){
18886         if(fs){
18887             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
18888                 return 1;
18889             }
18890             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
18891                 return -1;
18892             }
18893         }
18894         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
18895         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
18896         if(v1 < v2){
18897                         return dsc ? +1 : -1;
18898                 }else if(v1 > v2){
18899                         return dsc ? -1 : +1;
18900         }else{
18901                 return 0;
18902         }
18903     };
18904 };
18905
18906 Roo.tree.TreeSorter.prototype = {
18907     doSort : function(node){
18908         node.sort(this.sortFn);
18909     },
18910     
18911     compareNodes : function(n1, n2){
18912         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
18913     },
18914     
18915     updateSort : function(tree, node){
18916         if(node.childrenRendered){
18917             this.doSort.defer(1, this, [node]);
18918         }
18919     }
18920 };/*
18921  * Based on:
18922  * Ext JS Library 1.1.1
18923  * Copyright(c) 2006-2007, Ext JS, LLC.
18924  *
18925  * Originally Released Under LGPL - original licence link has changed is not relivant.
18926  *
18927  * Fork - LGPL
18928  * <script type="text/javascript">
18929  */
18930
18931 if(Roo.dd.DropZone){
18932     
18933 Roo.tree.TreeDropZone = function(tree, config){
18934     this.allowParentInsert = false;
18935     this.allowContainerDrop = false;
18936     this.appendOnly = false;
18937     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
18938     this.tree = tree;
18939     this.lastInsertClass = "x-tree-no-status";
18940     this.dragOverData = {};
18941 };
18942
18943 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
18944     ddGroup : "TreeDD",
18945     scroll:  true,
18946     
18947     expandDelay : 1000,
18948     
18949     expandNode : function(node){
18950         if(node.hasChildNodes() && !node.isExpanded()){
18951             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
18952         }
18953     },
18954     
18955     queueExpand : function(node){
18956         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
18957     },
18958     
18959     cancelExpand : function(){
18960         if(this.expandProcId){
18961             clearTimeout(this.expandProcId);
18962             this.expandProcId = false;
18963         }
18964     },
18965     
18966     isValidDropPoint : function(n, pt, dd, e, data){
18967         if(!n || !data){ return false; }
18968         var targetNode = n.node;
18969         var dropNode = data.node;
18970         // default drop rules
18971         if(!(targetNode && targetNode.isTarget && pt)){
18972             return false;
18973         }
18974         if(pt == "append" && targetNode.allowChildren === false){
18975             return false;
18976         }
18977         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
18978             return false;
18979         }
18980         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
18981             return false;
18982         }
18983         // reuse the object
18984         var overEvent = this.dragOverData;
18985         overEvent.tree = this.tree;
18986         overEvent.target = targetNode;
18987         overEvent.data = data;
18988         overEvent.point = pt;
18989         overEvent.source = dd;
18990         overEvent.rawEvent = e;
18991         overEvent.dropNode = dropNode;
18992         overEvent.cancel = false;  
18993         var result = this.tree.fireEvent("nodedragover", overEvent);
18994         return overEvent.cancel === false && result !== false;
18995     },
18996     
18997     getDropPoint : function(e, n, dd)
18998     {
18999         var tn = n.node;
19000         if(tn.isRoot){
19001             return tn.allowChildren !== false ? "append" : false; // always append for root
19002         }
19003         var dragEl = n.ddel;
19004         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
19005         var y = Roo.lib.Event.getPageY(e);
19006         //var noAppend = tn.allowChildren === false || tn.isLeaf();
19007         
19008         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
19009         var noAppend = tn.allowChildren === false;
19010         if(this.appendOnly || tn.parentNode.allowChildren === false){
19011             return noAppend ? false : "append";
19012         }
19013         var noBelow = false;
19014         if(!this.allowParentInsert){
19015             noBelow = tn.hasChildNodes() && tn.isExpanded();
19016         }
19017         var q = (b - t) / (noAppend ? 2 : 3);
19018         if(y >= t && y < (t + q)){
19019             return "above";
19020         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
19021             return "below";
19022         }else{
19023             return "append";
19024         }
19025     },
19026     
19027     onNodeEnter : function(n, dd, e, data)
19028     {
19029         this.cancelExpand();
19030     },
19031     
19032     onNodeOver : function(n, dd, e, data)
19033     {
19034        
19035         var pt = this.getDropPoint(e, n, dd);
19036         var node = n.node;
19037         
19038         // auto node expand check
19039         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
19040             this.queueExpand(node);
19041         }else if(pt != "append"){
19042             this.cancelExpand();
19043         }
19044         
19045         // set the insert point style on the target node
19046         var returnCls = this.dropNotAllowed;
19047         if(this.isValidDropPoint(n, pt, dd, e, data)){
19048            if(pt){
19049                var el = n.ddel;
19050                var cls;
19051                if(pt == "above"){
19052                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
19053                    cls = "x-tree-drag-insert-above";
19054                }else if(pt == "below"){
19055                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
19056                    cls = "x-tree-drag-insert-below";
19057                }else{
19058                    returnCls = "x-tree-drop-ok-append";
19059                    cls = "x-tree-drag-append";
19060                }
19061                if(this.lastInsertClass != cls){
19062                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
19063                    this.lastInsertClass = cls;
19064                }
19065            }
19066        }
19067        return returnCls;
19068     },
19069     
19070     onNodeOut : function(n, dd, e, data){
19071         
19072         this.cancelExpand();
19073         this.removeDropIndicators(n);
19074     },
19075     
19076     onNodeDrop : function(n, dd, e, data){
19077         var point = this.getDropPoint(e, n, dd);
19078         var targetNode = n.node;
19079         targetNode.ui.startDrop();
19080         if(!this.isValidDropPoint(n, point, dd, e, data)){
19081             targetNode.ui.endDrop();
19082             return false;
19083         }
19084         // first try to find the drop node
19085         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
19086         var dropEvent = {
19087             tree : this.tree,
19088             target: targetNode,
19089             data: data,
19090             point: point,
19091             source: dd,
19092             rawEvent: e,
19093             dropNode: dropNode,
19094             cancel: !dropNode   
19095         };
19096         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
19097         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
19098             targetNode.ui.endDrop();
19099             return false;
19100         }
19101         // allow target changing
19102         targetNode = dropEvent.target;
19103         if(point == "append" && !targetNode.isExpanded()){
19104             targetNode.expand(false, null, function(){
19105                 this.completeDrop(dropEvent);
19106             }.createDelegate(this));
19107         }else{
19108             this.completeDrop(dropEvent);
19109         }
19110         return true;
19111     },
19112     
19113     completeDrop : function(de){
19114         var ns = de.dropNode, p = de.point, t = de.target;
19115         if(!(ns instanceof Array)){
19116             ns = [ns];
19117         }
19118         var n;
19119         for(var i = 0, len = ns.length; i < len; i++){
19120             n = ns[i];
19121             if(p == "above"){
19122                 t.parentNode.insertBefore(n, t);
19123             }else if(p == "below"){
19124                 t.parentNode.insertBefore(n, t.nextSibling);
19125             }else{
19126                 t.appendChild(n);
19127             }
19128         }
19129         n.ui.focus();
19130         if(this.tree.hlDrop){
19131             n.ui.highlight();
19132         }
19133         t.ui.endDrop();
19134         this.tree.fireEvent("nodedrop", de);
19135     },
19136     
19137     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
19138         if(this.tree.hlDrop){
19139             dropNode.ui.focus();
19140             dropNode.ui.highlight();
19141         }
19142         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
19143     },
19144     
19145     getTree : function(){
19146         return this.tree;
19147     },
19148     
19149     removeDropIndicators : function(n){
19150         if(n && n.ddel){
19151             var el = n.ddel;
19152             Roo.fly(el).removeClass([
19153                     "x-tree-drag-insert-above",
19154                     "x-tree-drag-insert-below",
19155                     "x-tree-drag-append"]);
19156             this.lastInsertClass = "_noclass";
19157         }
19158     },
19159     
19160     beforeDragDrop : function(target, e, id){
19161         this.cancelExpand();
19162         return true;
19163     },
19164     
19165     afterRepair : function(data){
19166         if(data && Roo.enableFx){
19167             data.node.ui.highlight();
19168         }
19169         this.hideProxy();
19170     } 
19171     
19172 });
19173
19174 }
19175 /*
19176  * Based on:
19177  * Ext JS Library 1.1.1
19178  * Copyright(c) 2006-2007, Ext JS, LLC.
19179  *
19180  * Originally Released Under LGPL - original licence link has changed is not relivant.
19181  *
19182  * Fork - LGPL
19183  * <script type="text/javascript">
19184  */
19185  
19186
19187 if(Roo.dd.DragZone){
19188 Roo.tree.TreeDragZone = function(tree, config){
19189     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
19190     this.tree = tree;
19191 };
19192
19193 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
19194     ddGroup : "TreeDD",
19195    
19196     onBeforeDrag : function(data, e){
19197         var n = data.node;
19198         return n && n.draggable && !n.disabled;
19199     },
19200      
19201     
19202     onInitDrag : function(e){
19203         var data = this.dragData;
19204         this.tree.getSelectionModel().select(data.node);
19205         this.proxy.update("");
19206         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
19207         this.tree.fireEvent("startdrag", this.tree, data.node, e);
19208     },
19209     
19210     getRepairXY : function(e, data){
19211         return data.node.ui.getDDRepairXY();
19212     },
19213     
19214     onEndDrag : function(data, e){
19215         this.tree.fireEvent("enddrag", this.tree, data.node, e);
19216         
19217         
19218     },
19219     
19220     onValidDrop : function(dd, e, id){
19221         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
19222         this.hideProxy();
19223     },
19224     
19225     beforeInvalidDrop : function(e, id){
19226         // this scrolls the original position back into view
19227         var sm = this.tree.getSelectionModel();
19228         sm.clearSelections();
19229         sm.select(this.dragData.node);
19230     }
19231 });
19232 }/*
19233  * Based on:
19234  * Ext JS Library 1.1.1
19235  * Copyright(c) 2006-2007, Ext JS, LLC.
19236  *
19237  * Originally Released Under LGPL - original licence link has changed is not relivant.
19238  *
19239  * Fork - LGPL
19240  * <script type="text/javascript">
19241  */
19242 /**
19243  * @class Roo.tree.TreeEditor
19244  * @extends Roo.Editor
19245  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
19246  * as the editor field.
19247  * @constructor
19248  * @param {Object} config (used to be the tree panel.)
19249  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
19250  * 
19251  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
19252  * @cfg {Roo.form.TextField|Object} field The field configuration
19253  *
19254  * 
19255  */
19256 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
19257     var tree = config;
19258     var field;
19259     if (oldconfig) { // old style..
19260         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
19261     } else {
19262         // new style..
19263         tree = config.tree;
19264         config.field = config.field  || {};
19265         config.field.xtype = 'TextField';
19266         field = Roo.factory(config.field, Roo.form);
19267     }
19268     config = config || {};
19269     
19270     
19271     this.addEvents({
19272         /**
19273          * @event beforenodeedit
19274          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
19275          * false from the handler of this event.
19276          * @param {Editor} this
19277          * @param {Roo.tree.Node} node 
19278          */
19279         "beforenodeedit" : true
19280     });
19281     
19282     //Roo.log(config);
19283     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
19284
19285     this.tree = tree;
19286
19287     tree.on('beforeclick', this.beforeNodeClick, this);
19288     tree.getTreeEl().on('mousedown', this.hide, this);
19289     this.on('complete', this.updateNode, this);
19290     this.on('beforestartedit', this.fitToTree, this);
19291     this.on('startedit', this.bindScroll, this, {delay:10});
19292     this.on('specialkey', this.onSpecialKey, this);
19293 };
19294
19295 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
19296     /**
19297      * @cfg {String} alignment
19298      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
19299      */
19300     alignment: "l-l",
19301     // inherit
19302     autoSize: false,
19303     /**
19304      * @cfg {Boolean} hideEl
19305      * True to hide the bound element while the editor is displayed (defaults to false)
19306      */
19307     hideEl : false,
19308     /**
19309      * @cfg {String} cls
19310      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
19311      */
19312     cls: "x-small-editor x-tree-editor",
19313     /**
19314      * @cfg {Boolean} shim
19315      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
19316      */
19317     shim:false,
19318     // inherit
19319     shadow:"frame",
19320     /**
19321      * @cfg {Number} maxWidth
19322      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
19323      * the containing tree element's size, it will be automatically limited for you to the container width, taking
19324      * scroll and client offsets into account prior to each edit.
19325      */
19326     maxWidth: 250,
19327
19328     editDelay : 350,
19329
19330     // private
19331     fitToTree : function(ed, el){
19332         var td = this.tree.getTreeEl().dom, nd = el.dom;
19333         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
19334             td.scrollLeft = nd.offsetLeft;
19335         }
19336         var w = Math.min(
19337                 this.maxWidth,
19338                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
19339         this.setSize(w, '');
19340         
19341         return this.fireEvent('beforenodeedit', this, this.editNode);
19342         
19343     },
19344
19345     // private
19346     triggerEdit : function(node){
19347         this.completeEdit();
19348         this.editNode = node;
19349         this.startEdit(node.ui.textNode, node.text);
19350     },
19351
19352     // private
19353     bindScroll : function(){
19354         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
19355     },
19356
19357     // private
19358     beforeNodeClick : function(node, e){
19359         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
19360         this.lastClick = new Date();
19361         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
19362             e.stopEvent();
19363             this.triggerEdit(node);
19364             return false;
19365         }
19366         return true;
19367     },
19368
19369     // private
19370     updateNode : function(ed, value){
19371         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
19372         this.editNode.setText(value);
19373     },
19374
19375     // private
19376     onHide : function(){
19377         Roo.tree.TreeEditor.superclass.onHide.call(this);
19378         if(this.editNode){
19379             this.editNode.ui.focus();
19380         }
19381     },
19382
19383     // private
19384     onSpecialKey : function(field, e){
19385         var k = e.getKey();
19386         if(k == e.ESC){
19387             e.stopEvent();
19388             this.cancelEdit();
19389         }else if(k == e.ENTER && !e.hasModifier()){
19390             e.stopEvent();
19391             this.completeEdit();
19392         }
19393     }
19394 });//<Script type="text/javascript">
19395 /*
19396  * Based on:
19397  * Ext JS Library 1.1.1
19398  * Copyright(c) 2006-2007, Ext JS, LLC.
19399  *
19400  * Originally Released Under LGPL - original licence link has changed is not relivant.
19401  *
19402  * Fork - LGPL
19403  * <script type="text/javascript">
19404  */
19405  
19406 /**
19407  * Not documented??? - probably should be...
19408  */
19409
19410 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
19411     //focus: Roo.emptyFn, // prevent odd scrolling behavior
19412     
19413     renderElements : function(n, a, targetNode, bulkRender){
19414         //consel.log("renderElements?");
19415         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
19416
19417         var t = n.getOwnerTree();
19418         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
19419         
19420         var cols = t.columns;
19421         var bw = t.borderWidth;
19422         var c = cols[0];
19423         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
19424          var cb = typeof a.checked == "boolean";
19425         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19426         var colcls = 'x-t-' + tid + '-c0';
19427         var buf = [
19428             '<li class="x-tree-node">',
19429             
19430                 
19431                 '<div class="x-tree-node-el ', a.cls,'">',
19432                     // extran...
19433                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
19434                 
19435                 
19436                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
19437                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
19438                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
19439                            (a.icon ? ' x-tree-node-inline-icon' : ''),
19440                            (a.iconCls ? ' '+a.iconCls : ''),
19441                            '" unselectable="on" />',
19442                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
19443                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
19444                              
19445                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19446                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
19447                             '<span unselectable="on" qtip="' + tx + '">',
19448                              tx,
19449                              '</span></a>' ,
19450                     '</div>',
19451                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
19452                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
19453                  ];
19454         for(var i = 1, len = cols.length; i < len; i++){
19455             c = cols[i];
19456             colcls = 'x-t-' + tid + '-c' +i;
19457             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
19458             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
19459                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
19460                       "</div>");
19461          }
19462          
19463          buf.push(
19464             '</a>',
19465             '<div class="x-clear"></div></div>',
19466             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
19467             "</li>");
19468         
19469         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
19470             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
19471                                 n.nextSibling.ui.getEl(), buf.join(""));
19472         }else{
19473             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
19474         }
19475         var el = this.wrap.firstChild;
19476         this.elRow = el;
19477         this.elNode = el.firstChild;
19478         this.ranchor = el.childNodes[1];
19479         this.ctNode = this.wrap.childNodes[1];
19480         var cs = el.firstChild.childNodes;
19481         this.indentNode = cs[0];
19482         this.ecNode = cs[1];
19483         this.iconNode = cs[2];
19484         var index = 3;
19485         if(cb){
19486             this.checkbox = cs[3];
19487             index++;
19488         }
19489         this.anchor = cs[index];
19490         
19491         this.textNode = cs[index].firstChild;
19492         
19493         //el.on("click", this.onClick, this);
19494         //el.on("dblclick", this.onDblClick, this);
19495         
19496         
19497        // console.log(this);
19498     },
19499     initEvents : function(){
19500         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
19501         
19502             
19503         var a = this.ranchor;
19504
19505         var el = Roo.get(a);
19506
19507         if(Roo.isOpera){ // opera render bug ignores the CSS
19508             el.setStyle("text-decoration", "none");
19509         }
19510
19511         el.on("click", this.onClick, this);
19512         el.on("dblclick", this.onDblClick, this);
19513         el.on("contextmenu", this.onContextMenu, this);
19514         
19515     },
19516     
19517     /*onSelectedChange : function(state){
19518         if(state){
19519             this.focus();
19520             this.addClass("x-tree-selected");
19521         }else{
19522             //this.blur();
19523             this.removeClass("x-tree-selected");
19524         }
19525     },*/
19526     addClass : function(cls){
19527         if(this.elRow){
19528             Roo.fly(this.elRow).addClass(cls);
19529         }
19530         
19531     },
19532     
19533     
19534     removeClass : function(cls){
19535         if(this.elRow){
19536             Roo.fly(this.elRow).removeClass(cls);
19537         }
19538     }
19539
19540     
19541     
19542 });//<Script type="text/javascript">
19543
19544 /*
19545  * Based on:
19546  * Ext JS Library 1.1.1
19547  * Copyright(c) 2006-2007, Ext JS, LLC.
19548  *
19549  * Originally Released Under LGPL - original licence link has changed is not relivant.
19550  *
19551  * Fork - LGPL
19552  * <script type="text/javascript">
19553  */
19554  
19555
19556 /**
19557  * @class Roo.tree.ColumnTree
19558  * @extends Roo.data.TreePanel
19559  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
19560  * @cfg {int} borderWidth  compined right/left border allowance
19561  * @constructor
19562  * @param {String/HTMLElement/Element} el The container element
19563  * @param {Object} config
19564  */
19565 Roo.tree.ColumnTree =  function(el, config)
19566 {
19567    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
19568    this.addEvents({
19569         /**
19570         * @event resize
19571         * Fire this event on a container when it resizes
19572         * @param {int} w Width
19573         * @param {int} h Height
19574         */
19575        "resize" : true
19576     });
19577     this.on('resize', this.onResize, this);
19578 };
19579
19580 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
19581     //lines:false,
19582     
19583     
19584     borderWidth: Roo.isBorderBox ? 0 : 2, 
19585     headEls : false,
19586     
19587     render : function(){
19588         // add the header.....
19589        
19590         Roo.tree.ColumnTree.superclass.render.apply(this);
19591         
19592         this.el.addClass('x-column-tree');
19593         
19594         this.headers = this.el.createChild(
19595             {cls:'x-tree-headers'},this.innerCt.dom);
19596    
19597         var cols = this.columns, c;
19598         var totalWidth = 0;
19599         this.headEls = [];
19600         var  len = cols.length;
19601         for(var i = 0; i < len; i++){
19602              c = cols[i];
19603              totalWidth += c.width;
19604             this.headEls.push(this.headers.createChild({
19605                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
19606                  cn: {
19607                      cls:'x-tree-hd-text',
19608                      html: c.header
19609                  },
19610                  style:'width:'+(c.width-this.borderWidth)+'px;'
19611              }));
19612         }
19613         this.headers.createChild({cls:'x-clear'});
19614         // prevent floats from wrapping when clipped
19615         this.headers.setWidth(totalWidth);
19616         //this.innerCt.setWidth(totalWidth);
19617         this.innerCt.setStyle({ overflow: 'auto' });
19618         this.onResize(this.width, this.height);
19619              
19620         
19621     },
19622     onResize : function(w,h)
19623     {
19624         this.height = h;
19625         this.width = w;
19626         // resize cols..
19627         this.innerCt.setWidth(this.width);
19628         this.innerCt.setHeight(this.height-20);
19629         
19630         // headers...
19631         var cols = this.columns, c;
19632         var totalWidth = 0;
19633         var expEl = false;
19634         var len = cols.length;
19635         for(var i = 0; i < len; i++){
19636             c = cols[i];
19637             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
19638                 // it's the expander..
19639                 expEl  = this.headEls[i];
19640                 continue;
19641             }
19642             totalWidth += c.width;
19643             
19644         }
19645         if (expEl) {
19646             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
19647         }
19648         this.headers.setWidth(w-20);
19649
19650         
19651         
19652         
19653     }
19654 });
19655 /*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665  
19666 /**
19667  * @class Roo.menu.Menu
19668  * @extends Roo.util.Observable
19669  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
19670  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
19671  * @constructor
19672  * Creates a new Menu
19673  * @param {Object} config Configuration options
19674  */
19675 Roo.menu.Menu = function(config){
19676     Roo.apply(this, config);
19677     this.id = this.id || Roo.id();
19678     this.addEvents({
19679         /**
19680          * @event beforeshow
19681          * Fires before this menu is displayed
19682          * @param {Roo.menu.Menu} this
19683          */
19684         beforeshow : true,
19685         /**
19686          * @event beforehide
19687          * Fires before this menu is hidden
19688          * @param {Roo.menu.Menu} this
19689          */
19690         beforehide : true,
19691         /**
19692          * @event show
19693          * Fires after this menu is displayed
19694          * @param {Roo.menu.Menu} this
19695          */
19696         show : true,
19697         /**
19698          * @event hide
19699          * Fires after this menu is hidden
19700          * @param {Roo.menu.Menu} this
19701          */
19702         hide : true,
19703         /**
19704          * @event click
19705          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
19706          * @param {Roo.menu.Menu} this
19707          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19708          * @param {Roo.EventObject} e
19709          */
19710         click : true,
19711         /**
19712          * @event mouseover
19713          * Fires when the mouse is hovering over this menu
19714          * @param {Roo.menu.Menu} this
19715          * @param {Roo.EventObject} e
19716          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19717          */
19718         mouseover : true,
19719         /**
19720          * @event mouseout
19721          * Fires when the mouse exits this menu
19722          * @param {Roo.menu.Menu} this
19723          * @param {Roo.EventObject} e
19724          * @param {Roo.menu.Item} menuItem The menu item that was clicked
19725          */
19726         mouseout : true,
19727         /**
19728          * @event itemclick
19729          * Fires when a menu item contained in this menu is clicked
19730          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
19731          * @param {Roo.EventObject} e
19732          */
19733         itemclick: true
19734     });
19735     if (this.registerMenu) {
19736         Roo.menu.MenuMgr.register(this);
19737     }
19738     
19739     var mis = this.items;
19740     this.items = new Roo.util.MixedCollection();
19741     if(mis){
19742         this.add.apply(this, mis);
19743     }
19744 };
19745
19746 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
19747     /**
19748      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
19749      */
19750     minWidth : 120,
19751     /**
19752      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
19753      * for bottom-right shadow (defaults to "sides")
19754      */
19755     shadow : "sides",
19756     /**
19757      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
19758      * this menu (defaults to "tl-tr?")
19759      */
19760     subMenuAlign : "tl-tr?",
19761     /**
19762      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
19763      * relative to its element of origin (defaults to "tl-bl?")
19764      */
19765     defaultAlign : "tl-bl?",
19766     /**
19767      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
19768      */
19769     allowOtherMenus : false,
19770     /**
19771      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
19772      */
19773     registerMenu : true,
19774
19775     hidden:true,
19776
19777     // private
19778     render : function(){
19779         if(this.el){
19780             return;
19781         }
19782         var el = this.el = new Roo.Layer({
19783             cls: "x-menu",
19784             shadow:this.shadow,
19785             constrain: false,
19786             parentEl: this.parentEl || document.body,
19787             zindex:15000
19788         });
19789
19790         this.keyNav = new Roo.menu.MenuNav(this);
19791
19792         if(this.plain){
19793             el.addClass("x-menu-plain");
19794         }
19795         if(this.cls){
19796             el.addClass(this.cls);
19797         }
19798         // generic focus element
19799         this.focusEl = el.createChild({
19800             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
19801         });
19802         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
19803         ul.on("click", this.onClick, this);
19804         ul.on("mouseover", this.onMouseOver, this);
19805         ul.on("mouseout", this.onMouseOut, this);
19806         this.items.each(function(item){
19807             var li = document.createElement("li");
19808             li.className = "x-menu-list-item";
19809             ul.dom.appendChild(li);
19810             item.render(li, this);
19811         }, this);
19812         this.ul = ul;
19813         this.autoWidth();
19814     },
19815
19816     // private
19817     autoWidth : function(){
19818         var el = this.el, ul = this.ul;
19819         if(!el){
19820             return;
19821         }
19822         var w = this.width;
19823         if(w){
19824             el.setWidth(w);
19825         }else if(Roo.isIE){
19826             el.setWidth(this.minWidth);
19827             var t = el.dom.offsetWidth; // force recalc
19828             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
19829         }
19830     },
19831
19832     // private
19833     delayAutoWidth : function(){
19834         if(this.rendered){
19835             if(!this.awTask){
19836                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
19837             }
19838             this.awTask.delay(20);
19839         }
19840     },
19841
19842     // private
19843     findTargetItem : function(e){
19844         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
19845         if(t && t.menuItemId){
19846             return this.items.get(t.menuItemId);
19847         }
19848     },
19849
19850     // private
19851     onClick : function(e){
19852         var t;
19853         if(t = this.findTargetItem(e)){
19854             t.onClick(e);
19855             this.fireEvent("click", this, t, e);
19856         }
19857     },
19858
19859     // private
19860     setActiveItem : function(item, autoExpand){
19861         if(item != this.activeItem){
19862             if(this.activeItem){
19863                 this.activeItem.deactivate();
19864             }
19865             this.activeItem = item;
19866             item.activate(autoExpand);
19867         }else if(autoExpand){
19868             item.expandMenu();
19869         }
19870     },
19871
19872     // private
19873     tryActivate : function(start, step){
19874         var items = this.items;
19875         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
19876             var item = items.get(i);
19877             if(!item.disabled && item.canActivate){
19878                 this.setActiveItem(item, false);
19879                 return item;
19880             }
19881         }
19882         return false;
19883     },
19884
19885     // private
19886     onMouseOver : function(e){
19887         var t;
19888         if(t = this.findTargetItem(e)){
19889             if(t.canActivate && !t.disabled){
19890                 this.setActiveItem(t, true);
19891             }
19892         }
19893         this.fireEvent("mouseover", this, e, t);
19894     },
19895
19896     // private
19897     onMouseOut : function(e){
19898         var t;
19899         if(t = this.findTargetItem(e)){
19900             if(t == this.activeItem && t.shouldDeactivate(e)){
19901                 this.activeItem.deactivate();
19902                 delete this.activeItem;
19903             }
19904         }
19905         this.fireEvent("mouseout", this, e, t);
19906     },
19907
19908     /**
19909      * Read-only.  Returns true if the menu is currently displayed, else false.
19910      * @type Boolean
19911      */
19912     isVisible : function(){
19913         return this.el && !this.hidden;
19914     },
19915
19916     /**
19917      * Displays this menu relative to another element
19918      * @param {String/HTMLElement/Roo.Element} element The element to align to
19919      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
19920      * the element (defaults to this.defaultAlign)
19921      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19922      */
19923     show : function(el, pos, parentMenu){
19924         this.parentMenu = parentMenu;
19925         if(!this.el){
19926             this.render();
19927         }
19928         this.fireEvent("beforeshow", this);
19929         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
19930     },
19931
19932     /**
19933      * Displays this menu at a specific xy position
19934      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
19935      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
19936      */
19937     showAt : function(xy, parentMenu, /* private: */_e){
19938         this.parentMenu = parentMenu;
19939         if(!this.el){
19940             this.render();
19941         }
19942         if(_e !== false){
19943             this.fireEvent("beforeshow", this);
19944             xy = this.el.adjustForConstraints(xy);
19945         }
19946         this.el.setXY(xy);
19947         this.el.show();
19948         this.hidden = false;
19949         this.focus();
19950         this.fireEvent("show", this);
19951     },
19952
19953     focus : function(){
19954         if(!this.hidden){
19955             this.doFocus.defer(50, this);
19956         }
19957     },
19958
19959     doFocus : function(){
19960         if(!this.hidden){
19961             this.focusEl.focus();
19962         }
19963     },
19964
19965     /**
19966      * Hides this menu and optionally all parent menus
19967      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
19968      */
19969     hide : function(deep){
19970         if(this.el && this.isVisible()){
19971             this.fireEvent("beforehide", this);
19972             if(this.activeItem){
19973                 this.activeItem.deactivate();
19974                 this.activeItem = null;
19975             }
19976             this.el.hide();
19977             this.hidden = true;
19978             this.fireEvent("hide", this);
19979         }
19980         if(deep === true && this.parentMenu){
19981             this.parentMenu.hide(true);
19982         }
19983     },
19984
19985     /**
19986      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
19987      * Any of the following are valid:
19988      * <ul>
19989      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
19990      * <li>An HTMLElement object which will be converted to a menu item</li>
19991      * <li>A menu item config object that will be created as a new menu item</li>
19992      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
19993      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
19994      * </ul>
19995      * Usage:
19996      * <pre><code>
19997 // Create the menu
19998 var menu = new Roo.menu.Menu();
19999
20000 // Create a menu item to add by reference
20001 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
20002
20003 // Add a bunch of items at once using different methods.
20004 // Only the last item added will be returned.
20005 var item = menu.add(
20006     menuItem,                // add existing item by ref
20007     'Dynamic Item',          // new TextItem
20008     '-',                     // new separator
20009     { text: 'Config Item' }  // new item by config
20010 );
20011 </code></pre>
20012      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
20013      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
20014      */
20015     add : function(){
20016         var a = arguments, l = a.length, item;
20017         for(var i = 0; i < l; i++){
20018             var el = a[i];
20019             if ((typeof(el) == "object") && el.xtype && el.xns) {
20020                 el = Roo.factory(el, Roo.menu);
20021             }
20022             
20023             if(el.render){ // some kind of Item
20024                 item = this.addItem(el);
20025             }else if(typeof el == "string"){ // string
20026                 if(el == "separator" || el == "-"){
20027                     item = this.addSeparator();
20028                 }else{
20029                     item = this.addText(el);
20030                 }
20031             }else if(el.tagName || el.el){ // element
20032                 item = this.addElement(el);
20033             }else if(typeof el == "object"){ // must be menu item config?
20034                 item = this.addMenuItem(el);
20035             }
20036         }
20037         return item;
20038     },
20039
20040     /**
20041      * Returns this menu's underlying {@link Roo.Element} object
20042      * @return {Roo.Element} The element
20043      */
20044     getEl : function(){
20045         if(!this.el){
20046             this.render();
20047         }
20048         return this.el;
20049     },
20050
20051     /**
20052      * Adds a separator bar to the menu
20053      * @return {Roo.menu.Item} The menu item that was added
20054      */
20055     addSeparator : function(){
20056         return this.addItem(new Roo.menu.Separator());
20057     },
20058
20059     /**
20060      * Adds an {@link Roo.Element} object to the menu
20061      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
20062      * @return {Roo.menu.Item} The menu item that was added
20063      */
20064     addElement : function(el){
20065         return this.addItem(new Roo.menu.BaseItem(el));
20066     },
20067
20068     /**
20069      * Adds an existing object based on {@link Roo.menu.Item} to the menu
20070      * @param {Roo.menu.Item} item The menu item to add
20071      * @return {Roo.menu.Item} The menu item that was added
20072      */
20073     addItem : function(item){
20074         this.items.add(item);
20075         if(this.ul){
20076             var li = document.createElement("li");
20077             li.className = "x-menu-list-item";
20078             this.ul.dom.appendChild(li);
20079             item.render(li, this);
20080             this.delayAutoWidth();
20081         }
20082         return item;
20083     },
20084
20085     /**
20086      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
20087      * @param {Object} config A MenuItem config object
20088      * @return {Roo.menu.Item} The menu item that was added
20089      */
20090     addMenuItem : function(config){
20091         if(!(config instanceof Roo.menu.Item)){
20092             if(typeof config.checked == "boolean"){ // must be check menu item config?
20093                 config = new Roo.menu.CheckItem(config);
20094             }else{
20095                 config = new Roo.menu.Item(config);
20096             }
20097         }
20098         return this.addItem(config);
20099     },
20100
20101     /**
20102      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
20103      * @param {String} text The text to display in the menu item
20104      * @return {Roo.menu.Item} The menu item that was added
20105      */
20106     addText : function(text){
20107         return this.addItem(new Roo.menu.TextItem({ text : text }));
20108     },
20109
20110     /**
20111      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
20112      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
20113      * @param {Roo.menu.Item} item The menu item to add
20114      * @return {Roo.menu.Item} The menu item that was added
20115      */
20116     insert : function(index, item){
20117         this.items.insert(index, item);
20118         if(this.ul){
20119             var li = document.createElement("li");
20120             li.className = "x-menu-list-item";
20121             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
20122             item.render(li, this);
20123             this.delayAutoWidth();
20124         }
20125         return item;
20126     },
20127
20128     /**
20129      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
20130      * @param {Roo.menu.Item} item The menu item to remove
20131      */
20132     remove : function(item){
20133         this.items.removeKey(item.id);
20134         item.destroy();
20135     },
20136
20137     /**
20138      * Removes and destroys all items in the menu
20139      */
20140     removeAll : function(){
20141         var f;
20142         while(f = this.items.first()){
20143             this.remove(f);
20144         }
20145     }
20146 });
20147
20148 // MenuNav is a private utility class used internally by the Menu
20149 Roo.menu.MenuNav = function(menu){
20150     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
20151     this.scope = this.menu = menu;
20152 };
20153
20154 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
20155     doRelay : function(e, h){
20156         var k = e.getKey();
20157         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
20158             this.menu.tryActivate(0, 1);
20159             return false;
20160         }
20161         return h.call(this.scope || this, e, this.menu);
20162     },
20163
20164     up : function(e, m){
20165         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
20166             m.tryActivate(m.items.length-1, -1);
20167         }
20168     },
20169
20170     down : function(e, m){
20171         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
20172             m.tryActivate(0, 1);
20173         }
20174     },
20175
20176     right : function(e, m){
20177         if(m.activeItem){
20178             m.activeItem.expandMenu(true);
20179         }
20180     },
20181
20182     left : function(e, m){
20183         m.hide();
20184         if(m.parentMenu && m.parentMenu.activeItem){
20185             m.parentMenu.activeItem.activate();
20186         }
20187     },
20188
20189     enter : function(e, m){
20190         if(m.activeItem){
20191             e.stopPropagation();
20192             m.activeItem.onClick(e);
20193             m.fireEvent("click", this, m.activeItem);
20194             return true;
20195         }
20196     }
20197 });/*
20198  * Based on:
20199  * Ext JS Library 1.1.1
20200  * Copyright(c) 2006-2007, Ext JS, LLC.
20201  *
20202  * Originally Released Under LGPL - original licence link has changed is not relivant.
20203  *
20204  * Fork - LGPL
20205  * <script type="text/javascript">
20206  */
20207  
20208 /**
20209  * @class Roo.menu.MenuMgr
20210  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
20211  * @singleton
20212  */
20213 Roo.menu.MenuMgr = function(){
20214    var menus, active, groups = {}, attached = false, lastShow = new Date();
20215
20216    // private - called when first menu is created
20217    function init(){
20218        menus = {};
20219        active = new Roo.util.MixedCollection();
20220        Roo.get(document).addKeyListener(27, function(){
20221            if(active.length > 0){
20222                hideAll();
20223            }
20224        });
20225    }
20226
20227    // private
20228    function hideAll(){
20229        if(active && active.length > 0){
20230            var c = active.clone();
20231            c.each(function(m){
20232                m.hide();
20233            });
20234        }
20235    }
20236
20237    // private
20238    function onHide(m){
20239        active.remove(m);
20240        if(active.length < 1){
20241            Roo.get(document).un("mousedown", onMouseDown);
20242            attached = false;
20243        }
20244    }
20245
20246    // private
20247    function onShow(m){
20248        var last = active.last();
20249        lastShow = new Date();
20250        active.add(m);
20251        if(!attached){
20252            Roo.get(document).on("mousedown", onMouseDown);
20253            attached = true;
20254        }
20255        if(m.parentMenu){
20256           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
20257           m.parentMenu.activeChild = m;
20258        }else if(last && last.isVisible()){
20259           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
20260        }
20261    }
20262
20263    // private
20264    function onBeforeHide(m){
20265        if(m.activeChild){
20266            m.activeChild.hide();
20267        }
20268        if(m.autoHideTimer){
20269            clearTimeout(m.autoHideTimer);
20270            delete m.autoHideTimer;
20271        }
20272    }
20273
20274    // private
20275    function onBeforeShow(m){
20276        var pm = m.parentMenu;
20277        if(!pm && !m.allowOtherMenus){
20278            hideAll();
20279        }else if(pm && pm.activeChild && active != m){
20280            pm.activeChild.hide();
20281        }
20282    }
20283
20284    // private
20285    function onMouseDown(e){
20286        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
20287            hideAll();
20288        }
20289    }
20290
20291    // private
20292    function onBeforeCheck(mi, state){
20293        if(state){
20294            var g = groups[mi.group];
20295            for(var i = 0, l = g.length; i < l; i++){
20296                if(g[i] != mi){
20297                    g[i].setChecked(false);
20298                }
20299            }
20300        }
20301    }
20302
20303    return {
20304
20305        /**
20306         * Hides all menus that are currently visible
20307         */
20308        hideAll : function(){
20309             hideAll();  
20310        },
20311
20312        // private
20313        register : function(menu){
20314            if(!menus){
20315                init();
20316            }
20317            menus[menu.id] = menu;
20318            menu.on("beforehide", onBeforeHide);
20319            menu.on("hide", onHide);
20320            menu.on("beforeshow", onBeforeShow);
20321            menu.on("show", onShow);
20322            var g = menu.group;
20323            if(g && menu.events["checkchange"]){
20324                if(!groups[g]){
20325                    groups[g] = [];
20326                }
20327                groups[g].push(menu);
20328                menu.on("checkchange", onCheck);
20329            }
20330        },
20331
20332         /**
20333          * Returns a {@link Roo.menu.Menu} object
20334          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
20335          * be used to generate and return a new Menu instance.
20336          */
20337        get : function(menu){
20338            if(typeof menu == "string"){ // menu id
20339                return menus[menu];
20340            }else if(menu.events){  // menu instance
20341                return menu;
20342            }else if(typeof menu.length == 'number'){ // array of menu items?
20343                return new Roo.menu.Menu({items:menu});
20344            }else{ // otherwise, must be a config
20345                return new Roo.menu.Menu(menu);
20346            }
20347        },
20348
20349        // private
20350        unregister : function(menu){
20351            delete menus[menu.id];
20352            menu.un("beforehide", onBeforeHide);
20353            menu.un("hide", onHide);
20354            menu.un("beforeshow", onBeforeShow);
20355            menu.un("show", onShow);
20356            var g = menu.group;
20357            if(g && menu.events["checkchange"]){
20358                groups[g].remove(menu);
20359                menu.un("checkchange", onCheck);
20360            }
20361        },
20362
20363        // private
20364        registerCheckable : function(menuItem){
20365            var g = menuItem.group;
20366            if(g){
20367                if(!groups[g]){
20368                    groups[g] = [];
20369                }
20370                groups[g].push(menuItem);
20371                menuItem.on("beforecheckchange", onBeforeCheck);
20372            }
20373        },
20374
20375        // private
20376        unregisterCheckable : function(menuItem){
20377            var g = menuItem.group;
20378            if(g){
20379                groups[g].remove(menuItem);
20380                menuItem.un("beforecheckchange", onBeforeCheck);
20381            }
20382        }
20383    };
20384 }();/*
20385  * Based on:
20386  * Ext JS Library 1.1.1
20387  * Copyright(c) 2006-2007, Ext JS, LLC.
20388  *
20389  * Originally Released Under LGPL - original licence link has changed is not relivant.
20390  *
20391  * Fork - LGPL
20392  * <script type="text/javascript">
20393  */
20394  
20395
20396 /**
20397  * @class Roo.menu.BaseItem
20398  * @extends Roo.Component
20399  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
20400  * management and base configuration options shared by all menu components.
20401  * @constructor
20402  * Creates a new BaseItem
20403  * @param {Object} config Configuration options
20404  */
20405 Roo.menu.BaseItem = function(config){
20406     Roo.menu.BaseItem.superclass.constructor.call(this, config);
20407
20408     this.addEvents({
20409         /**
20410          * @event click
20411          * Fires when this item is clicked
20412          * @param {Roo.menu.BaseItem} this
20413          * @param {Roo.EventObject} e
20414          */
20415         click: true,
20416         /**
20417          * @event activate
20418          * Fires when this item is activated
20419          * @param {Roo.menu.BaseItem} this
20420          */
20421         activate : true,
20422         /**
20423          * @event deactivate
20424          * Fires when this item is deactivated
20425          * @param {Roo.menu.BaseItem} this
20426          */
20427         deactivate : true
20428     });
20429
20430     if(this.handler){
20431         this.on("click", this.handler, this.scope, true);
20432     }
20433 };
20434
20435 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
20436     /**
20437      * @cfg {Function} handler
20438      * A function that will handle the click event of this menu item (defaults to undefined)
20439      */
20440     /**
20441      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
20442      */
20443     canActivate : false,
20444     /**
20445      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
20446      */
20447     activeClass : "x-menu-item-active",
20448     /**
20449      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
20450      */
20451     hideOnClick : true,
20452     /**
20453      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
20454      */
20455     hideDelay : 100,
20456
20457     // private
20458     ctype: "Roo.menu.BaseItem",
20459
20460     // private
20461     actionMode : "container",
20462
20463     // private
20464     render : function(container, parentMenu){
20465         this.parentMenu = parentMenu;
20466         Roo.menu.BaseItem.superclass.render.call(this, container);
20467         this.container.menuItemId = this.id;
20468     },
20469
20470     // private
20471     onRender : function(container, position){
20472         this.el = Roo.get(this.el);
20473         container.dom.appendChild(this.el.dom);
20474     },
20475
20476     // private
20477     onClick : function(e){
20478         if(!this.disabled && this.fireEvent("click", this, e) !== false
20479                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
20480             this.handleClick(e);
20481         }else{
20482             e.stopEvent();
20483         }
20484     },
20485
20486     // private
20487     activate : function(){
20488         if(this.disabled){
20489             return false;
20490         }
20491         var li = this.container;
20492         li.addClass(this.activeClass);
20493         this.region = li.getRegion().adjust(2, 2, -2, -2);
20494         this.fireEvent("activate", this);
20495         return true;
20496     },
20497
20498     // private
20499     deactivate : function(){
20500         this.container.removeClass(this.activeClass);
20501         this.fireEvent("deactivate", this);
20502     },
20503
20504     // private
20505     shouldDeactivate : function(e){
20506         return !this.region || !this.region.contains(e.getPoint());
20507     },
20508
20509     // private
20510     handleClick : function(e){
20511         if(this.hideOnClick){
20512             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
20513         }
20514     },
20515
20516     // private
20517     expandMenu : function(autoActivate){
20518         // do nothing
20519     },
20520
20521     // private
20522     hideMenu : function(){
20523         // do nothing
20524     }
20525 });/*
20526  * Based on:
20527  * Ext JS Library 1.1.1
20528  * Copyright(c) 2006-2007, Ext JS, LLC.
20529  *
20530  * Originally Released Under LGPL - original licence link has changed is not relivant.
20531  *
20532  * Fork - LGPL
20533  * <script type="text/javascript">
20534  */
20535  
20536 /**
20537  * @class Roo.menu.Adapter
20538  * @extends Roo.menu.BaseItem
20539  * 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.
20540  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
20541  * @constructor
20542  * Creates a new Adapter
20543  * @param {Object} config Configuration options
20544  */
20545 Roo.menu.Adapter = function(component, config){
20546     Roo.menu.Adapter.superclass.constructor.call(this, config);
20547     this.component = component;
20548 };
20549 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
20550     // private
20551     canActivate : true,
20552
20553     // private
20554     onRender : function(container, position){
20555         this.component.render(container);
20556         this.el = this.component.getEl();
20557     },
20558
20559     // private
20560     activate : function(){
20561         if(this.disabled){
20562             return false;
20563         }
20564         this.component.focus();
20565         this.fireEvent("activate", this);
20566         return true;
20567     },
20568
20569     // private
20570     deactivate : function(){
20571         this.fireEvent("deactivate", this);
20572     },
20573
20574     // private
20575     disable : function(){
20576         this.component.disable();
20577         Roo.menu.Adapter.superclass.disable.call(this);
20578     },
20579
20580     // private
20581     enable : function(){
20582         this.component.enable();
20583         Roo.menu.Adapter.superclass.enable.call(this);
20584     }
20585 });/*
20586  * Based on:
20587  * Ext JS Library 1.1.1
20588  * Copyright(c) 2006-2007, Ext JS, LLC.
20589  *
20590  * Originally Released Under LGPL - original licence link has changed is not relivant.
20591  *
20592  * Fork - LGPL
20593  * <script type="text/javascript">
20594  */
20595
20596 /**
20597  * @class Roo.menu.TextItem
20598  * @extends Roo.menu.BaseItem
20599  * Adds a static text string to a menu, usually used as either a heading or group separator.
20600  * Note: old style constructor with text is still supported.
20601  * 
20602  * @constructor
20603  * Creates a new TextItem
20604  * @param {Object} cfg Configuration
20605  */
20606 Roo.menu.TextItem = function(cfg){
20607     if (typeof(cfg) == 'string') {
20608         this.text = cfg;
20609     } else {
20610         Roo.apply(this,cfg);
20611     }
20612     
20613     Roo.menu.TextItem.superclass.constructor.call(this);
20614 };
20615
20616 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
20617     /**
20618      * @cfg {Boolean} text Text to show on item.
20619      */
20620     text : '',
20621     
20622     /**
20623      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20624      */
20625     hideOnClick : false,
20626     /**
20627      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
20628      */
20629     itemCls : "x-menu-text",
20630
20631     // private
20632     onRender : function(){
20633         var s = document.createElement("span");
20634         s.className = this.itemCls;
20635         s.innerHTML = this.text;
20636         this.el = s;
20637         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
20638     }
20639 });/*
20640  * Based on:
20641  * Ext JS Library 1.1.1
20642  * Copyright(c) 2006-2007, Ext JS, LLC.
20643  *
20644  * Originally Released Under LGPL - original licence link has changed is not relivant.
20645  *
20646  * Fork - LGPL
20647  * <script type="text/javascript">
20648  */
20649
20650 /**
20651  * @class Roo.menu.Separator
20652  * @extends Roo.menu.BaseItem
20653  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
20654  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
20655  * @constructor
20656  * @param {Object} config Configuration options
20657  */
20658 Roo.menu.Separator = function(config){
20659     Roo.menu.Separator.superclass.constructor.call(this, config);
20660 };
20661
20662 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
20663     /**
20664      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
20665      */
20666     itemCls : "x-menu-sep",
20667     /**
20668      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
20669      */
20670     hideOnClick : false,
20671
20672     // private
20673     onRender : function(li){
20674         var s = document.createElement("span");
20675         s.className = this.itemCls;
20676         s.innerHTML = "&#160;";
20677         this.el = s;
20678         li.addClass("x-menu-sep-li");
20679         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
20680     }
20681 });/*
20682  * Based on:
20683  * Ext JS Library 1.1.1
20684  * Copyright(c) 2006-2007, Ext JS, LLC.
20685  *
20686  * Originally Released Under LGPL - original licence link has changed is not relivant.
20687  *
20688  * Fork - LGPL
20689  * <script type="text/javascript">
20690  */
20691 /**
20692  * @class Roo.menu.Item
20693  * @extends Roo.menu.BaseItem
20694  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
20695  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
20696  * activation and click handling.
20697  * @constructor
20698  * Creates a new Item
20699  * @param {Object} config Configuration options
20700  */
20701 Roo.menu.Item = function(config){
20702     Roo.menu.Item.superclass.constructor.call(this, config);
20703     if(this.menu){
20704         this.menu = Roo.menu.MenuMgr.get(this.menu);
20705     }
20706 };
20707 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
20708     
20709     /**
20710      * @cfg {String} text
20711      * The text to show on the menu item.
20712      */
20713     text: '',
20714      /**
20715      * @cfg {String} HTML to render in menu
20716      * The text to show on the menu item (HTML version).
20717      */
20718     html: '',
20719     /**
20720      * @cfg {String} icon
20721      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
20722      */
20723     icon: undefined,
20724     /**
20725      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
20726      */
20727     itemCls : "x-menu-item",
20728     /**
20729      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
20730      */
20731     canActivate : true,
20732     /**
20733      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
20734      */
20735     showDelay: 200,
20736     // doc'd in BaseItem
20737     hideDelay: 200,
20738
20739     // private
20740     ctype: "Roo.menu.Item",
20741     
20742     // private
20743     onRender : function(container, position){
20744         var el = document.createElement("a");
20745         el.hideFocus = true;
20746         el.unselectable = "on";
20747         el.href = this.href || "#";
20748         if(this.hrefTarget){
20749             el.target = this.hrefTarget;
20750         }
20751         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
20752         
20753         var html = this.html.length ? this.html  : String.format('{0}',this.text);
20754         
20755         el.innerHTML = String.format(
20756                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
20757                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
20758         this.el = el;
20759         Roo.menu.Item.superclass.onRender.call(this, container, position);
20760     },
20761
20762     /**
20763      * Sets the text to display in this menu item
20764      * @param {String} text The text to display
20765      * @param {Boolean} isHTML true to indicate text is pure html.
20766      */
20767     setText : function(text, isHTML){
20768         if (isHTML) {
20769             this.html = text;
20770         } else {
20771             this.text = text;
20772             this.html = '';
20773         }
20774         if(this.rendered){
20775             var html = this.html.length ? this.html  : String.format('{0}',this.text);
20776      
20777             this.el.update(String.format(
20778                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
20779                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
20780             this.parentMenu.autoWidth();
20781         }
20782     },
20783
20784     // private
20785     handleClick : function(e){
20786         if(!this.href){ // if no link defined, stop the event automatically
20787             e.stopEvent();
20788         }
20789         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
20790     },
20791
20792     // private
20793     activate : function(autoExpand){
20794         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
20795             this.focus();
20796             if(autoExpand){
20797                 this.expandMenu();
20798             }
20799         }
20800         return true;
20801     },
20802
20803     // private
20804     shouldDeactivate : function(e){
20805         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
20806             if(this.menu && this.menu.isVisible()){
20807                 return !this.menu.getEl().getRegion().contains(e.getPoint());
20808             }
20809             return true;
20810         }
20811         return false;
20812     },
20813
20814     // private
20815     deactivate : function(){
20816         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
20817         this.hideMenu();
20818     },
20819
20820     // private
20821     expandMenu : function(autoActivate){
20822         if(!this.disabled && this.menu){
20823             clearTimeout(this.hideTimer);
20824             delete this.hideTimer;
20825             if(!this.menu.isVisible() && !this.showTimer){
20826                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
20827             }else if (this.menu.isVisible() && autoActivate){
20828                 this.menu.tryActivate(0, 1);
20829             }
20830         }
20831     },
20832
20833     // private
20834     deferExpand : function(autoActivate){
20835         delete this.showTimer;
20836         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
20837         if(autoActivate){
20838             this.menu.tryActivate(0, 1);
20839         }
20840     },
20841
20842     // private
20843     hideMenu : function(){
20844         clearTimeout(this.showTimer);
20845         delete this.showTimer;
20846         if(!this.hideTimer && this.menu && this.menu.isVisible()){
20847             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
20848         }
20849     },
20850
20851     // private
20852     deferHide : function(){
20853         delete this.hideTimer;
20854         this.menu.hide();
20855     }
20856 });/*
20857  * Based on:
20858  * Ext JS Library 1.1.1
20859  * Copyright(c) 2006-2007, Ext JS, LLC.
20860  *
20861  * Originally Released Under LGPL - original licence link has changed is not relivant.
20862  *
20863  * Fork - LGPL
20864  * <script type="text/javascript">
20865  */
20866  
20867 /**
20868  * @class Roo.menu.CheckItem
20869  * @extends Roo.menu.Item
20870  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
20871  * @constructor
20872  * Creates a new CheckItem
20873  * @param {Object} config Configuration options
20874  */
20875 Roo.menu.CheckItem = function(config){
20876     Roo.menu.CheckItem.superclass.constructor.call(this, config);
20877     this.addEvents({
20878         /**
20879          * @event beforecheckchange
20880          * Fires before the checked value is set, providing an opportunity to cancel if needed
20881          * @param {Roo.menu.CheckItem} this
20882          * @param {Boolean} checked The new checked value that will be set
20883          */
20884         "beforecheckchange" : true,
20885         /**
20886          * @event checkchange
20887          * Fires after the checked value has been set
20888          * @param {Roo.menu.CheckItem} this
20889          * @param {Boolean} checked The checked value that was set
20890          */
20891         "checkchange" : true
20892     });
20893     if(this.checkHandler){
20894         this.on('checkchange', this.checkHandler, this.scope);
20895     }
20896 };
20897 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
20898     /**
20899      * @cfg {String} group
20900      * All check items with the same group name will automatically be grouped into a single-select
20901      * radio button group (defaults to '')
20902      */
20903     /**
20904      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
20905      */
20906     itemCls : "x-menu-item x-menu-check-item",
20907     /**
20908      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
20909      */
20910     groupClass : "x-menu-group-item",
20911
20912     /**
20913      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
20914      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
20915      * initialized with checked = true will be rendered as checked.
20916      */
20917     checked: false,
20918
20919     // private
20920     ctype: "Roo.menu.CheckItem",
20921
20922     // private
20923     onRender : function(c){
20924         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
20925         if(this.group){
20926             this.el.addClass(this.groupClass);
20927         }
20928         Roo.menu.MenuMgr.registerCheckable(this);
20929         if(this.checked){
20930             this.checked = false;
20931             this.setChecked(true, true);
20932         }
20933     },
20934
20935     // private
20936     destroy : function(){
20937         if(this.rendered){
20938             Roo.menu.MenuMgr.unregisterCheckable(this);
20939         }
20940         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
20941     },
20942
20943     /**
20944      * Set the checked state of this item
20945      * @param {Boolean} checked The new checked value
20946      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
20947      */
20948     setChecked : function(state, suppressEvent){
20949         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
20950             if(this.container){
20951                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
20952             }
20953             this.checked = state;
20954             if(suppressEvent !== true){
20955                 this.fireEvent("checkchange", this, state);
20956             }
20957         }
20958     },
20959
20960     // private
20961     handleClick : function(e){
20962        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
20963            this.setChecked(!this.checked);
20964        }
20965        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
20966     }
20967 });/*
20968  * Based on:
20969  * Ext JS Library 1.1.1
20970  * Copyright(c) 2006-2007, Ext JS, LLC.
20971  *
20972  * Originally Released Under LGPL - original licence link has changed is not relivant.
20973  *
20974  * Fork - LGPL
20975  * <script type="text/javascript">
20976  */
20977  
20978 /**
20979  * @class Roo.menu.DateItem
20980  * @extends Roo.menu.Adapter
20981  * A menu item that wraps the {@link Roo.DatPicker} component.
20982  * @constructor
20983  * Creates a new DateItem
20984  * @param {Object} config Configuration options
20985  */
20986 Roo.menu.DateItem = function(config){
20987     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
20988     /** The Roo.DatePicker object @type Roo.DatePicker */
20989     this.picker = this.component;
20990     this.addEvents({select: true});
20991     
20992     this.picker.on("render", function(picker){
20993         picker.getEl().swallowEvent("click");
20994         picker.container.addClass("x-menu-date-item");
20995     });
20996
20997     this.picker.on("select", this.onSelect, this);
20998 };
20999
21000 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
21001     // private
21002     onSelect : function(picker, date){
21003         this.fireEvent("select", this, date, picker);
21004         Roo.menu.DateItem.superclass.handleClick.call(this);
21005     }
21006 });/*
21007  * Based on:
21008  * Ext JS Library 1.1.1
21009  * Copyright(c) 2006-2007, Ext JS, LLC.
21010  *
21011  * Originally Released Under LGPL - original licence link has changed is not relivant.
21012  *
21013  * Fork - LGPL
21014  * <script type="text/javascript">
21015  */
21016  
21017 /**
21018  * @class Roo.menu.ColorItem
21019  * @extends Roo.menu.Adapter
21020  * A menu item that wraps the {@link Roo.ColorPalette} component.
21021  * @constructor
21022  * Creates a new ColorItem
21023  * @param {Object} config Configuration options
21024  */
21025 Roo.menu.ColorItem = function(config){
21026     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
21027     /** The Roo.ColorPalette object @type Roo.ColorPalette */
21028     this.palette = this.component;
21029     this.relayEvents(this.palette, ["select"]);
21030     if(this.selectHandler){
21031         this.on('select', this.selectHandler, this.scope);
21032     }
21033 };
21034 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
21035  * Based on:
21036  * Ext JS Library 1.1.1
21037  * Copyright(c) 2006-2007, Ext JS, LLC.
21038  *
21039  * Originally Released Under LGPL - original licence link has changed is not relivant.
21040  *
21041  * Fork - LGPL
21042  * <script type="text/javascript">
21043  */
21044  
21045
21046 /**
21047  * @class Roo.menu.DateMenu
21048  * @extends Roo.menu.Menu
21049  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
21050  * @constructor
21051  * Creates a new DateMenu
21052  * @param {Object} config Configuration options
21053  */
21054 Roo.menu.DateMenu = function(config){
21055     Roo.menu.DateMenu.superclass.constructor.call(this, config);
21056     this.plain = true;
21057     var di = new Roo.menu.DateItem(config);
21058     this.add(di);
21059     /**
21060      * The {@link Roo.DatePicker} instance for this DateMenu
21061      * @type DatePicker
21062      */
21063     this.picker = di.picker;
21064     /**
21065      * @event select
21066      * @param {DatePicker} picker
21067      * @param {Date} date
21068      */
21069     this.relayEvents(di, ["select"]);
21070     this.on('beforeshow', function(){
21071         if(this.picker){
21072             this.picker.hideMonthPicker(false);
21073         }
21074     }, this);
21075 };
21076 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
21077     cls:'x-date-menu'
21078 });/*
21079  * Based on:
21080  * Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  *
21083  * Originally Released Under LGPL - original licence link has changed is not relivant.
21084  *
21085  * Fork - LGPL
21086  * <script type="text/javascript">
21087  */
21088  
21089
21090 /**
21091  * @class Roo.menu.ColorMenu
21092  * @extends Roo.menu.Menu
21093  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
21094  * @constructor
21095  * Creates a new ColorMenu
21096  * @param {Object} config Configuration options
21097  */
21098 Roo.menu.ColorMenu = function(config){
21099     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
21100     this.plain = true;
21101     var ci = new Roo.menu.ColorItem(config);
21102     this.add(ci);
21103     /**
21104      * The {@link Roo.ColorPalette} instance for this ColorMenu
21105      * @type ColorPalette
21106      */
21107     this.palette = ci.palette;
21108     /**
21109      * @event select
21110      * @param {ColorPalette} palette
21111      * @param {String} color
21112      */
21113     this.relayEvents(ci, ["select"]);
21114 };
21115 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
21116  * Based on:
21117  * Ext JS Library 1.1.1
21118  * Copyright(c) 2006-2007, Ext JS, LLC.
21119  *
21120  * Originally Released Under LGPL - original licence link has changed is not relivant.
21121  *
21122  * Fork - LGPL
21123  * <script type="text/javascript">
21124  */
21125  
21126 /**
21127  * @class Roo.form.Field
21128  * @extends Roo.BoxComponent
21129  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
21130  * @constructor
21131  * Creates a new Field
21132  * @param {Object} config Configuration options
21133  */
21134 Roo.form.Field = function(config){
21135     Roo.form.Field.superclass.constructor.call(this, config);
21136 };
21137
21138 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
21139     /**
21140      * @cfg {String} fieldLabel Label to use when rendering a form.
21141      */
21142        /**
21143      * @cfg {String} qtip Mouse over tip
21144      */
21145      
21146     /**
21147      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
21148      */
21149     invalidClass : "x-form-invalid",
21150     /**
21151      * @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")
21152      */
21153     invalidText : "The value in this field is invalid",
21154     /**
21155      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
21156      */
21157     focusClass : "x-form-focus",
21158     /**
21159      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
21160       automatic validation (defaults to "keyup").
21161      */
21162     validationEvent : "keyup",
21163     /**
21164      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
21165      */
21166     validateOnBlur : true,
21167     /**
21168      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
21169      */
21170     validationDelay : 250,
21171     /**
21172      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
21173      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
21174      */
21175     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
21176     /**
21177      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
21178      */
21179     fieldClass : "x-form-field",
21180     /**
21181      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
21182      *<pre>
21183 Value         Description
21184 -----------   ----------------------------------------------------------------------
21185 qtip          Display a quick tip when the user hovers over the field
21186 title         Display a default browser title attribute popup
21187 under         Add a block div beneath the field containing the error text
21188 side          Add an error icon to the right of the field with a popup on hover
21189 [element id]  Add the error text directly to the innerHTML of the specified element
21190 </pre>
21191      */
21192     msgTarget : 'qtip',
21193     /**
21194      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
21195      */
21196     msgFx : 'normal',
21197
21198     /**
21199      * @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.
21200      */
21201     readOnly : false,
21202
21203     /**
21204      * @cfg {Boolean} disabled True to disable the field (defaults to false).
21205      */
21206     disabled : false,
21207
21208     /**
21209      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
21210      */
21211     inputType : undefined,
21212     
21213     /**
21214      * @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).
21215          */
21216         tabIndex : undefined,
21217         
21218     // private
21219     isFormField : true,
21220
21221     // private
21222     hasFocus : false,
21223     /**
21224      * @property {Roo.Element} fieldEl
21225      * Element Containing the rendered Field (with label etc.)
21226      */
21227     /**
21228      * @cfg {Mixed} value A value to initialize this field with.
21229      */
21230     value : undefined,
21231
21232     /**
21233      * @cfg {String} name The field's HTML name attribute.
21234      */
21235     /**
21236      * @cfg {String} cls A CSS class to apply to the field's underlying element.
21237      */
21238
21239         // private ??
21240         initComponent : function(){
21241         Roo.form.Field.superclass.initComponent.call(this);
21242         this.addEvents({
21243             /**
21244              * @event focus
21245              * Fires when this field receives input focus.
21246              * @param {Roo.form.Field} this
21247              */
21248             focus : true,
21249             /**
21250              * @event blur
21251              * Fires when this field loses input focus.
21252              * @param {Roo.form.Field} this
21253              */
21254             blur : true,
21255             /**
21256              * @event specialkey
21257              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
21258              * {@link Roo.EventObject#getKey} to determine which key was pressed.
21259              * @param {Roo.form.Field} this
21260              * @param {Roo.EventObject} e The event object
21261              */
21262             specialkey : true,
21263             /**
21264              * @event change
21265              * Fires just before the field blurs if the field value has changed.
21266              * @param {Roo.form.Field} this
21267              * @param {Mixed} newValue The new value
21268              * @param {Mixed} oldValue The original value
21269              */
21270             change : true,
21271             /**
21272              * @event invalid
21273              * Fires after the field has been marked as invalid.
21274              * @param {Roo.form.Field} this
21275              * @param {String} msg The validation message
21276              */
21277             invalid : true,
21278             /**
21279              * @event valid
21280              * Fires after the field has been validated with no errors.
21281              * @param {Roo.form.Field} this
21282              */
21283             valid : true,
21284              /**
21285              * @event keyup
21286              * Fires after the key up
21287              * @param {Roo.form.Field} this
21288              * @param {Roo.EventObject}  e The event Object
21289              */
21290             keyup : true
21291         });
21292     },
21293
21294     /**
21295      * Returns the name attribute of the field if available
21296      * @return {String} name The field name
21297      */
21298     getName: function(){
21299          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
21300     },
21301
21302     // private
21303     onRender : function(ct, position){
21304         Roo.form.Field.superclass.onRender.call(this, ct, position);
21305         if(!this.el){
21306             var cfg = this.getAutoCreate();
21307             if(!cfg.name){
21308                 cfg.name = this.name || this.id;
21309             }
21310             if(this.inputType){
21311                 cfg.type = this.inputType;
21312             }
21313             this.el = ct.createChild(cfg, position);
21314         }
21315         var type = this.el.dom.type;
21316         if(type){
21317             if(type == 'password'){
21318                 type = 'text';
21319             }
21320             this.el.addClass('x-form-'+type);
21321         }
21322         if(this.readOnly){
21323             this.el.dom.readOnly = true;
21324         }
21325         if(this.tabIndex !== undefined){
21326             this.el.dom.setAttribute('tabIndex', this.tabIndex);
21327         }
21328
21329         this.el.addClass([this.fieldClass, this.cls]);
21330         this.initValue();
21331     },
21332
21333     /**
21334      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
21335      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
21336      * @return {Roo.form.Field} this
21337      */
21338     applyTo : function(target){
21339         this.allowDomMove = false;
21340         this.el = Roo.get(target);
21341         this.render(this.el.dom.parentNode);
21342         return this;
21343     },
21344
21345     // private
21346     initValue : function(){
21347         if(this.value !== undefined){
21348             this.setValue(this.value);
21349         }else if(this.el.dom.value.length > 0){
21350             this.setValue(this.el.dom.value);
21351         }
21352     },
21353
21354     /**
21355      * Returns true if this field has been changed since it was originally loaded and is not disabled.
21356      */
21357     isDirty : function() {
21358         if(this.disabled) {
21359             return false;
21360         }
21361         return String(this.getValue()) !== String(this.originalValue);
21362     },
21363
21364     // private
21365     afterRender : function(){
21366         Roo.form.Field.superclass.afterRender.call(this);
21367         this.initEvents();
21368     },
21369
21370     // private
21371     fireKey : function(e){
21372         //Roo.log('field ' + e.getKey());
21373         if(e.isNavKeyPress()){
21374             this.fireEvent("specialkey", this, e);
21375         }
21376     },
21377
21378     /**
21379      * Resets the current field value to the originally loaded value and clears any validation messages
21380      */
21381     reset : function(){
21382         this.setValue(this.originalValue);
21383         this.clearInvalid();
21384     },
21385
21386     // private
21387     initEvents : function(){
21388         // safari killled keypress - so keydown is now used..
21389         this.el.on("keydown" , this.fireKey,  this);
21390         this.el.on("focus", this.onFocus,  this);
21391         this.el.on("blur", this.onBlur,  this);
21392         this.el.relayEvent('keyup', this);
21393
21394         // reference to original value for reset
21395         this.originalValue = this.getValue();
21396     },
21397
21398     // private
21399     onFocus : function(){
21400         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21401             this.el.addClass(this.focusClass);
21402         }
21403         if(!this.hasFocus){
21404             this.hasFocus = true;
21405             this.startValue = this.getValue();
21406             this.fireEvent("focus", this);
21407         }
21408     },
21409
21410     beforeBlur : Roo.emptyFn,
21411
21412     // private
21413     onBlur : function(){
21414         this.beforeBlur();
21415         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
21416             this.el.removeClass(this.focusClass);
21417         }
21418         this.hasFocus = false;
21419         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
21420             this.validate();
21421         }
21422         var v = this.getValue();
21423         if(String(v) !== String(this.startValue)){
21424             this.fireEvent('change', this, v, this.startValue);
21425         }
21426         this.fireEvent("blur", this);
21427     },
21428
21429     /**
21430      * Returns whether or not the field value is currently valid
21431      * @param {Boolean} preventMark True to disable marking the field invalid
21432      * @return {Boolean} True if the value is valid, else false
21433      */
21434     isValid : function(preventMark){
21435         if(this.disabled){
21436             return true;
21437         }
21438         var restore = this.preventMark;
21439         this.preventMark = preventMark === true;
21440         var v = this.validateValue(this.processValue(this.getRawValue()));
21441         this.preventMark = restore;
21442         return v;
21443     },
21444
21445     /**
21446      * Validates the field value
21447      * @return {Boolean} True if the value is valid, else false
21448      */
21449     validate : function(){
21450         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
21451             this.clearInvalid();
21452             return true;
21453         }
21454         return false;
21455     },
21456
21457     processValue : function(value){
21458         return value;
21459     },
21460
21461     // private
21462     // Subclasses should provide the validation implementation by overriding this
21463     validateValue : function(value){
21464         return true;
21465     },
21466
21467     /**
21468      * Mark this field as invalid
21469      * @param {String} msg The validation message
21470      */
21471     markInvalid : function(msg){
21472         if(!this.rendered || this.preventMark){ // not rendered
21473             return;
21474         }
21475         this.el.addClass(this.invalidClass);
21476         msg = msg || this.invalidText;
21477         switch(this.msgTarget){
21478             case 'qtip':
21479                 this.el.dom.qtip = msg;
21480                 this.el.dom.qclass = 'x-form-invalid-tip';
21481                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
21482                     Roo.QuickTips.enable();
21483                 }
21484                 break;
21485             case 'title':
21486                 this.el.dom.title = msg;
21487                 break;
21488             case 'under':
21489                 if(!this.errorEl){
21490                     var elp = this.el.findParent('.x-form-element', 5, true);
21491                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
21492                     this.errorEl.setWidth(elp.getWidth(true)-20);
21493                 }
21494                 this.errorEl.update(msg);
21495                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
21496                 break;
21497             case 'side':
21498                 if(!this.errorIcon){
21499                     var elp = this.el.findParent('.x-form-element', 5, true);
21500                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
21501                 }
21502                 this.alignErrorIcon();
21503                 this.errorIcon.dom.qtip = msg;
21504                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
21505                 this.errorIcon.show();
21506                 this.on('resize', this.alignErrorIcon, this);
21507                 break;
21508             default:
21509                 var t = Roo.getDom(this.msgTarget);
21510                 t.innerHTML = msg;
21511                 t.style.display = this.msgDisplay;
21512                 break;
21513         }
21514         this.fireEvent('invalid', this, msg);
21515     },
21516
21517     // private
21518     alignErrorIcon : function(){
21519         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
21520     },
21521
21522     /**
21523      * Clear any invalid styles/messages for this field
21524      */
21525     clearInvalid : function(){
21526         if(!this.rendered || this.preventMark){ // not rendered
21527             return;
21528         }
21529         this.el.removeClass(this.invalidClass);
21530         switch(this.msgTarget){
21531             case 'qtip':
21532                 this.el.dom.qtip = '';
21533                 break;
21534             case 'title':
21535                 this.el.dom.title = '';
21536                 break;
21537             case 'under':
21538                 if(this.errorEl){
21539                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
21540                 }
21541                 break;
21542             case 'side':
21543                 if(this.errorIcon){
21544                     this.errorIcon.dom.qtip = '';
21545                     this.errorIcon.hide();
21546                     this.un('resize', this.alignErrorIcon, this);
21547                 }
21548                 break;
21549             default:
21550                 var t = Roo.getDom(this.msgTarget);
21551                 t.innerHTML = '';
21552                 t.style.display = 'none';
21553                 break;
21554         }
21555         this.fireEvent('valid', this);
21556     },
21557
21558     /**
21559      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
21560      * @return {Mixed} value The field value
21561      */
21562     getRawValue : function(){
21563         var v = this.el.getValue();
21564         if(v === this.emptyText){
21565             v = '';
21566         }
21567         return v;
21568     },
21569
21570     /**
21571      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
21572      * @return {Mixed} value The field value
21573      */
21574     getValue : function(){
21575         var v = this.el.getValue();
21576         if(v === this.emptyText || v === undefined){
21577             v = '';
21578         }
21579         return v;
21580     },
21581
21582     /**
21583      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
21584      * @param {Mixed} value The value to set
21585      */
21586     setRawValue : function(v){
21587         return this.el.dom.value = (v === null || v === undefined ? '' : v);
21588     },
21589
21590     /**
21591      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
21592      * @param {Mixed} value The value to set
21593      */
21594     setValue : function(v){
21595         this.value = v;
21596         if(this.rendered){
21597             this.el.dom.value = (v === null || v === undefined ? '' : v);
21598              this.validate();
21599         }
21600     },
21601
21602     adjustSize : function(w, h){
21603         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
21604         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
21605         return s;
21606     },
21607
21608     adjustWidth : function(tag, w){
21609         tag = tag.toLowerCase();
21610         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
21611             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
21612                 if(tag == 'input'){
21613                     return w + 2;
21614                 }
21615                 if(tag = 'textarea'){
21616                     return w-2;
21617                 }
21618             }else if(Roo.isOpera){
21619                 if(tag == 'input'){
21620                     return w + 2;
21621                 }
21622                 if(tag = 'textarea'){
21623                     return w-2;
21624                 }
21625             }
21626         }
21627         return w;
21628     }
21629 });
21630
21631
21632 // anything other than normal should be considered experimental
21633 Roo.form.Field.msgFx = {
21634     normal : {
21635         show: function(msgEl, f){
21636             msgEl.setDisplayed('block');
21637         },
21638
21639         hide : function(msgEl, f){
21640             msgEl.setDisplayed(false).update('');
21641         }
21642     },
21643
21644     slide : {
21645         show: function(msgEl, f){
21646             msgEl.slideIn('t', {stopFx:true});
21647         },
21648
21649         hide : function(msgEl, f){
21650             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
21651         }
21652     },
21653
21654     slideRight : {
21655         show: function(msgEl, f){
21656             msgEl.fixDisplay();
21657             msgEl.alignTo(f.el, 'tl-tr');
21658             msgEl.slideIn('l', {stopFx:true});
21659         },
21660
21661         hide : function(msgEl, f){
21662             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
21663         }
21664     }
21665 };/*
21666  * Based on:
21667  * Ext JS Library 1.1.1
21668  * Copyright(c) 2006-2007, Ext JS, LLC.
21669  *
21670  * Originally Released Under LGPL - original licence link has changed is not relivant.
21671  *
21672  * Fork - LGPL
21673  * <script type="text/javascript">
21674  */
21675  
21676
21677 /**
21678  * @class Roo.form.TextField
21679  * @extends Roo.form.Field
21680  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
21681  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
21682  * @constructor
21683  * Creates a new TextField
21684  * @param {Object} config Configuration options
21685  */
21686 Roo.form.TextField = function(config){
21687     Roo.form.TextField.superclass.constructor.call(this, config);
21688     this.addEvents({
21689         /**
21690          * @event autosize
21691          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
21692          * according to the default logic, but this event provides a hook for the developer to apply additional
21693          * logic at runtime to resize the field if needed.
21694              * @param {Roo.form.Field} this This text field
21695              * @param {Number} width The new field width
21696              */
21697         autosize : true
21698     });
21699 };
21700
21701 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
21702     /**
21703      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
21704      */
21705     grow : false,
21706     /**
21707      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
21708      */
21709     growMin : 30,
21710     /**
21711      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
21712      */
21713     growMax : 800,
21714     /**
21715      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
21716      */
21717     vtype : null,
21718     /**
21719      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
21720      */
21721     maskRe : null,
21722     /**
21723      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
21724      */
21725     disableKeyFilter : false,
21726     /**
21727      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
21728      */
21729     allowBlank : true,
21730     /**
21731      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
21732      */
21733     minLength : 0,
21734     /**
21735      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
21736      */
21737     maxLength : Number.MAX_VALUE,
21738     /**
21739      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
21740      */
21741     minLengthText : "The minimum length for this field is {0}",
21742     /**
21743      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
21744      */
21745     maxLengthText : "The maximum length for this field is {0}",
21746     /**
21747      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
21748      */
21749     selectOnFocus : false,
21750     /**
21751      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
21752      */
21753     blankText : "This field is required",
21754     /**
21755      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
21756      * If available, this function will be called only after the basic validators all return true, and will be passed the
21757      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
21758      */
21759     validator : null,
21760     /**
21761      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
21762      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
21763      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
21764      */
21765     regex : null,
21766     /**
21767      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
21768      */
21769     regexText : "",
21770     /**
21771      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
21772      */
21773     emptyText : null,
21774     /**
21775      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
21776      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
21777      */
21778     emptyClass : 'x-form-empty-field',
21779
21780     // private
21781     initEvents : function(){
21782         Roo.form.TextField.superclass.initEvents.call(this);
21783         if(this.validationEvent == 'keyup'){
21784             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
21785             this.el.on('keyup', this.filterValidation, this);
21786         }
21787         else if(this.validationEvent !== false){
21788             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
21789         }
21790         if(this.selectOnFocus || this.emptyText){
21791             this.on("focus", this.preFocus, this);
21792             if(this.emptyText){
21793                 this.on('blur', this.postBlur, this);
21794                 this.applyEmptyText();
21795             }
21796         }
21797         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
21798             this.el.on("keypress", this.filterKeys, this);
21799         }
21800         if(this.grow){
21801             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
21802             this.el.on("click", this.autoSize,  this);
21803         }
21804     },
21805
21806     processValue : function(value){
21807         if(this.stripCharsRe){
21808             var newValue = value.replace(this.stripCharsRe, '');
21809             if(newValue !== value){
21810                 this.setRawValue(newValue);
21811                 return newValue;
21812             }
21813         }
21814         return value;
21815     },
21816
21817     filterValidation : function(e){
21818         if(!e.isNavKeyPress()){
21819             this.validationTask.delay(this.validationDelay);
21820         }
21821     },
21822
21823     // private
21824     onKeyUp : function(e){
21825         if(!e.isNavKeyPress()){
21826             this.autoSize();
21827         }
21828     },
21829
21830     /**
21831      * Resets the current field value to the originally-loaded value and clears any validation messages.
21832      * Also adds emptyText and emptyClass if the original value was blank.
21833      */
21834     reset : function(){
21835         Roo.form.TextField.superclass.reset.call(this);
21836         this.applyEmptyText();
21837     },
21838
21839     applyEmptyText : function(){
21840         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
21841             this.setRawValue(this.emptyText);
21842             this.el.addClass(this.emptyClass);
21843         }
21844     },
21845
21846     // private
21847     preFocus : function(){
21848         if(this.emptyText){
21849             if(this.el.dom.value == this.emptyText){
21850                 this.setRawValue('');
21851             }
21852             this.el.removeClass(this.emptyClass);
21853         }
21854         if(this.selectOnFocus){
21855             this.el.dom.select();
21856         }
21857     },
21858
21859     // private
21860     postBlur : function(){
21861         this.applyEmptyText();
21862     },
21863
21864     // private
21865     filterKeys : function(e){
21866         var k = e.getKey();
21867         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
21868             return;
21869         }
21870         var c = e.getCharCode(), cc = String.fromCharCode(c);
21871         if(Roo.isIE && (e.isSpecialKey() || !cc)){
21872             return;
21873         }
21874         if(!this.maskRe.test(cc)){
21875             e.stopEvent();
21876         }
21877     },
21878
21879     setValue : function(v){
21880         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
21881             this.el.removeClass(this.emptyClass);
21882         }
21883         Roo.form.TextField.superclass.setValue.apply(this, arguments);
21884         this.applyEmptyText();
21885         this.autoSize();
21886     },
21887
21888     /**
21889      * Validates a value according to the field's validation rules and marks the field as invalid
21890      * if the validation fails
21891      * @param {Mixed} value The value to validate
21892      * @return {Boolean} True if the value is valid, else false
21893      */
21894     validateValue : function(value){
21895         if(value.length < 1 || value === this.emptyText){ // if it's blank
21896              if(this.allowBlank){
21897                 this.clearInvalid();
21898                 return true;
21899              }else{
21900                 this.markInvalid(this.blankText);
21901                 return false;
21902              }
21903         }
21904         if(value.length < this.minLength){
21905             this.markInvalid(String.format(this.minLengthText, this.minLength));
21906             return false;
21907         }
21908         if(value.length > this.maxLength){
21909             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
21910             return false;
21911         }
21912         if(this.vtype){
21913             var vt = Roo.form.VTypes;
21914             if(!vt[this.vtype](value, this)){
21915                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
21916                 return false;
21917             }
21918         }
21919         if(typeof this.validator == "function"){
21920             var msg = this.validator(value);
21921             if(msg !== true){
21922                 this.markInvalid(msg);
21923                 return false;
21924             }
21925         }
21926         if(this.regex && !this.regex.test(value)){
21927             this.markInvalid(this.regexText);
21928             return false;
21929         }
21930         return true;
21931     },
21932
21933     /**
21934      * Selects text in this field
21935      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
21936      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
21937      */
21938     selectText : function(start, end){
21939         var v = this.getRawValue();
21940         if(v.length > 0){
21941             start = start === undefined ? 0 : start;
21942             end = end === undefined ? v.length : end;
21943             var d = this.el.dom;
21944             if(d.setSelectionRange){
21945                 d.setSelectionRange(start, end);
21946             }else if(d.createTextRange){
21947                 var range = d.createTextRange();
21948                 range.moveStart("character", start);
21949                 range.moveEnd("character", v.length-end);
21950                 range.select();
21951             }
21952         }
21953     },
21954
21955     /**
21956      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
21957      * This only takes effect if grow = true, and fires the autosize event.
21958      */
21959     autoSize : function(){
21960         if(!this.grow || !this.rendered){
21961             return;
21962         }
21963         if(!this.metrics){
21964             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
21965         }
21966         var el = this.el;
21967         var v = el.dom.value;
21968         var d = document.createElement('div');
21969         d.appendChild(document.createTextNode(v));
21970         v = d.innerHTML;
21971         d = null;
21972         v += "&#160;";
21973         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
21974         this.el.setWidth(w);
21975         this.fireEvent("autosize", this, w);
21976     }
21977 });/*
21978  * Based on:
21979  * Ext JS Library 1.1.1
21980  * Copyright(c) 2006-2007, Ext JS, LLC.
21981  *
21982  * Originally Released Under LGPL - original licence link has changed is not relivant.
21983  *
21984  * Fork - LGPL
21985  * <script type="text/javascript">
21986  */
21987  
21988 /**
21989  * @class Roo.form.Hidden
21990  * @extends Roo.form.TextField
21991  * Simple Hidden element used on forms 
21992  * 
21993  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
21994  * 
21995  * @constructor
21996  * Creates a new Hidden form element.
21997  * @param {Object} config Configuration options
21998  */
21999
22000
22001
22002 // easy hidden field...
22003 Roo.form.Hidden = function(config){
22004     Roo.form.Hidden.superclass.constructor.call(this, config);
22005 };
22006   
22007 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
22008     fieldLabel:      '',
22009     inputType:      'hidden',
22010     width:          50,
22011     allowBlank:     true,
22012     labelSeparator: '',
22013     hidden:         true,
22014     itemCls :       'x-form-item-display-none'
22015
22016
22017 });
22018
22019
22020 /*
22021  * Based on:
22022  * Ext JS Library 1.1.1
22023  * Copyright(c) 2006-2007, Ext JS, LLC.
22024  *
22025  * Originally Released Under LGPL - original licence link has changed is not relivant.
22026  *
22027  * Fork - LGPL
22028  * <script type="text/javascript">
22029  */
22030  
22031 /**
22032  * @class Roo.form.TriggerField
22033  * @extends Roo.form.TextField
22034  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
22035  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
22036  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
22037  * for which you can provide a custom implementation.  For example:
22038  * <pre><code>
22039 var trigger = new Roo.form.TriggerField();
22040 trigger.onTriggerClick = myTriggerFn;
22041 trigger.applyTo('my-field');
22042 </code></pre>
22043  *
22044  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
22045  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
22046  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
22047  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
22048  * @constructor
22049  * Create a new TriggerField.
22050  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
22051  * to the base TextField)
22052  */
22053 Roo.form.TriggerField = function(config){
22054     this.mimicing = false;
22055     Roo.form.TriggerField.superclass.constructor.call(this, config);
22056 };
22057
22058 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
22059     /**
22060      * @cfg {String} triggerClass A CSS class to apply to the trigger
22061      */
22062     /**
22063      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22064      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
22065      */
22066     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
22067     /**
22068      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
22069      */
22070     hideTrigger:false,
22071
22072     /** @cfg {Boolean} grow @hide */
22073     /** @cfg {Number} growMin @hide */
22074     /** @cfg {Number} growMax @hide */
22075
22076     /**
22077      * @hide 
22078      * @method
22079      */
22080     autoSize: Roo.emptyFn,
22081     // private
22082     monitorTab : true,
22083     // private
22084     deferHeight : true,
22085
22086     
22087     actionMode : 'wrap',
22088     // private
22089     onResize : function(w, h){
22090         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
22091         if(typeof w == 'number'){
22092             var x = w - this.trigger.getWidth();
22093             this.el.setWidth(this.adjustWidth('input', x));
22094             this.trigger.setStyle('left', x+'px');
22095         }
22096     },
22097
22098     // private
22099     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22100
22101     // private
22102     getResizeEl : function(){
22103         return this.wrap;
22104     },
22105
22106     // private
22107     getPositionEl : function(){
22108         return this.wrap;
22109     },
22110
22111     // private
22112     alignErrorIcon : function(){
22113         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
22114     },
22115
22116     // private
22117     onRender : function(ct, position){
22118         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
22119         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
22120         this.trigger = this.wrap.createChild(this.triggerConfig ||
22121                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
22122         if(this.hideTrigger){
22123             this.trigger.setDisplayed(false);
22124         }
22125         this.initTrigger();
22126         if(!this.width){
22127             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
22128         }
22129     },
22130
22131     // private
22132     initTrigger : function(){
22133         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
22134         this.trigger.addClassOnOver('x-form-trigger-over');
22135         this.trigger.addClassOnClick('x-form-trigger-click');
22136     },
22137
22138     // private
22139     onDestroy : function(){
22140         if(this.trigger){
22141             this.trigger.removeAllListeners();
22142             this.trigger.remove();
22143         }
22144         if(this.wrap){
22145             this.wrap.remove();
22146         }
22147         Roo.form.TriggerField.superclass.onDestroy.call(this);
22148     },
22149
22150     // private
22151     onFocus : function(){
22152         Roo.form.TriggerField.superclass.onFocus.call(this);
22153         if(!this.mimicing){
22154             this.wrap.addClass('x-trigger-wrap-focus');
22155             this.mimicing = true;
22156             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
22157             if(this.monitorTab){
22158                 this.el.on("keydown", this.checkTab, this);
22159             }
22160         }
22161     },
22162
22163     // private
22164     checkTab : function(e){
22165         if(e.getKey() == e.TAB){
22166             this.triggerBlur();
22167         }
22168     },
22169
22170     // private
22171     onBlur : function(){
22172         // do nothing
22173     },
22174
22175     // private
22176     mimicBlur : function(e, t){
22177         if(!this.wrap.contains(t) && this.validateBlur()){
22178             this.triggerBlur();
22179         }
22180     },
22181
22182     // private
22183     triggerBlur : function(){
22184         this.mimicing = false;
22185         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
22186         if(this.monitorTab){
22187             this.el.un("keydown", this.checkTab, this);
22188         }
22189         this.wrap.removeClass('x-trigger-wrap-focus');
22190         Roo.form.TriggerField.superclass.onBlur.call(this);
22191     },
22192
22193     // private
22194     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
22195     validateBlur : function(e, t){
22196         return true;
22197     },
22198
22199     // private
22200     onDisable : function(){
22201         Roo.form.TriggerField.superclass.onDisable.call(this);
22202         if(this.wrap){
22203             this.wrap.addClass('x-item-disabled');
22204         }
22205     },
22206
22207     // private
22208     onEnable : function(){
22209         Roo.form.TriggerField.superclass.onEnable.call(this);
22210         if(this.wrap){
22211             this.wrap.removeClass('x-item-disabled');
22212         }
22213     },
22214
22215     // private
22216     onShow : function(){
22217         var ae = this.getActionEl();
22218         
22219         if(ae){
22220             ae.dom.style.display = '';
22221             ae.dom.style.visibility = 'visible';
22222         }
22223     },
22224
22225     // private
22226     
22227     onHide : function(){
22228         var ae = this.getActionEl();
22229         ae.dom.style.display = 'none';
22230     },
22231
22232     /**
22233      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
22234      * by an implementing function.
22235      * @method
22236      * @param {EventObject} e
22237      */
22238     onTriggerClick : Roo.emptyFn
22239 });
22240
22241 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
22242 // to be extended by an implementing class.  For an example of implementing this class, see the custom
22243 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
22244 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
22245     initComponent : function(){
22246         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
22247
22248         this.triggerConfig = {
22249             tag:'span', cls:'x-form-twin-triggers', cn:[
22250             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
22251             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
22252         ]};
22253     },
22254
22255     getTrigger : function(index){
22256         return this.triggers[index];
22257     },
22258
22259     initTrigger : function(){
22260         var ts = this.trigger.select('.x-form-trigger', true);
22261         this.wrap.setStyle('overflow', 'hidden');
22262         var triggerField = this;
22263         ts.each(function(t, all, index){
22264             t.hide = function(){
22265                 var w = triggerField.wrap.getWidth();
22266                 this.dom.style.display = 'none';
22267                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22268             };
22269             t.show = function(){
22270                 var w = triggerField.wrap.getWidth();
22271                 this.dom.style.display = '';
22272                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
22273             };
22274             var triggerIndex = 'Trigger'+(index+1);
22275
22276             if(this['hide'+triggerIndex]){
22277                 t.dom.style.display = 'none';
22278             }
22279             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
22280             t.addClassOnOver('x-form-trigger-over');
22281             t.addClassOnClick('x-form-trigger-click');
22282         }, this);
22283         this.triggers = ts.elements;
22284     },
22285
22286     onTrigger1Click : Roo.emptyFn,
22287     onTrigger2Click : Roo.emptyFn
22288 });/*
22289  * Based on:
22290  * Ext JS Library 1.1.1
22291  * Copyright(c) 2006-2007, Ext JS, LLC.
22292  *
22293  * Originally Released Under LGPL - original licence link has changed is not relivant.
22294  *
22295  * Fork - LGPL
22296  * <script type="text/javascript">
22297  */
22298  
22299 /**
22300  * @class Roo.form.TextArea
22301  * @extends Roo.form.TextField
22302  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
22303  * support for auto-sizing.
22304  * @constructor
22305  * Creates a new TextArea
22306  * @param {Object} config Configuration options
22307  */
22308 Roo.form.TextArea = function(config){
22309     Roo.form.TextArea.superclass.constructor.call(this, config);
22310     // these are provided exchanges for backwards compat
22311     // minHeight/maxHeight were replaced by growMin/growMax to be
22312     // compatible with TextField growing config values
22313     if(this.minHeight !== undefined){
22314         this.growMin = this.minHeight;
22315     }
22316     if(this.maxHeight !== undefined){
22317         this.growMax = this.maxHeight;
22318     }
22319 };
22320
22321 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
22322     /**
22323      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
22324      */
22325     growMin : 60,
22326     /**
22327      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
22328      */
22329     growMax: 1000,
22330     /**
22331      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
22332      * in the field (equivalent to setting overflow: hidden, defaults to false)
22333      */
22334     preventScrollbars: false,
22335     /**
22336      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
22337      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
22338      */
22339
22340     // private
22341     onRender : function(ct, position){
22342         if(!this.el){
22343             this.defaultAutoCreate = {
22344                 tag: "textarea",
22345                 style:"width:300px;height:60px;",
22346                 autocomplete: "off"
22347             };
22348         }
22349         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
22350         if(this.grow){
22351             this.textSizeEl = Roo.DomHelper.append(document.body, {
22352                 tag: "pre", cls: "x-form-grow-sizer"
22353             });
22354             if(this.preventScrollbars){
22355                 this.el.setStyle("overflow", "hidden");
22356             }
22357             this.el.setHeight(this.growMin);
22358         }
22359     },
22360
22361     onDestroy : function(){
22362         if(this.textSizeEl){
22363             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
22364         }
22365         Roo.form.TextArea.superclass.onDestroy.call(this);
22366     },
22367
22368     // private
22369     onKeyUp : function(e){
22370         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
22371             this.autoSize();
22372         }
22373     },
22374
22375     /**
22376      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
22377      * This only takes effect if grow = true, and fires the autosize event if the height changes.
22378      */
22379     autoSize : function(){
22380         if(!this.grow || !this.textSizeEl){
22381             return;
22382         }
22383         var el = this.el;
22384         var v = el.dom.value;
22385         var ts = this.textSizeEl;
22386
22387         ts.innerHTML = '';
22388         ts.appendChild(document.createTextNode(v));
22389         v = ts.innerHTML;
22390
22391         Roo.fly(ts).setWidth(this.el.getWidth());
22392         if(v.length < 1){
22393             v = "&#160;&#160;";
22394         }else{
22395             if(Roo.isIE){
22396                 v = v.replace(/\n/g, '<p>&#160;</p>');
22397             }
22398             v += "&#160;\n&#160;";
22399         }
22400         ts.innerHTML = v;
22401         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
22402         if(h != this.lastHeight){
22403             this.lastHeight = h;
22404             this.el.setHeight(h);
22405             this.fireEvent("autosize", this, h);
22406         }
22407     }
22408 });/*
22409  * Based on:
22410  * Ext JS Library 1.1.1
22411  * Copyright(c) 2006-2007, Ext JS, LLC.
22412  *
22413  * Originally Released Under LGPL - original licence link has changed is not relivant.
22414  *
22415  * Fork - LGPL
22416  * <script type="text/javascript">
22417  */
22418  
22419
22420 /**
22421  * @class Roo.form.NumberField
22422  * @extends Roo.form.TextField
22423  * Numeric text field that provides automatic keystroke filtering and numeric validation.
22424  * @constructor
22425  * Creates a new NumberField
22426  * @param {Object} config Configuration options
22427  */
22428 Roo.form.NumberField = function(config){
22429     Roo.form.NumberField.superclass.constructor.call(this, config);
22430 };
22431
22432 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
22433     /**
22434      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
22435      */
22436     fieldClass: "x-form-field x-form-num-field",
22437     /**
22438      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
22439      */
22440     allowDecimals : true,
22441     /**
22442      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
22443      */
22444     decimalSeparator : ".",
22445     /**
22446      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
22447      */
22448     decimalPrecision : 2,
22449     /**
22450      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
22451      */
22452     allowNegative : true,
22453     /**
22454      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
22455      */
22456     minValue : Number.NEGATIVE_INFINITY,
22457     /**
22458      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
22459      */
22460     maxValue : Number.MAX_VALUE,
22461     /**
22462      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
22463      */
22464     minText : "The minimum value for this field is {0}",
22465     /**
22466      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
22467      */
22468     maxText : "The maximum value for this field is {0}",
22469     /**
22470      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
22471      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
22472      */
22473     nanText : "{0} is not a valid number",
22474
22475     // private
22476     initEvents : function(){
22477         Roo.form.NumberField.superclass.initEvents.call(this);
22478         var allowed = "0123456789";
22479         if(this.allowDecimals){
22480             allowed += this.decimalSeparator;
22481         }
22482         if(this.allowNegative){
22483             allowed += "-";
22484         }
22485         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
22486         var keyPress = function(e){
22487             var k = e.getKey();
22488             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
22489                 return;
22490             }
22491             var c = e.getCharCode();
22492             if(allowed.indexOf(String.fromCharCode(c)) === -1){
22493                 e.stopEvent();
22494             }
22495         };
22496         this.el.on("keypress", keyPress, this);
22497     },
22498
22499     // private
22500     validateValue : function(value){
22501         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
22502             return false;
22503         }
22504         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22505              return true;
22506         }
22507         var num = this.parseValue(value);
22508         if(isNaN(num)){
22509             this.markInvalid(String.format(this.nanText, value));
22510             return false;
22511         }
22512         if(num < this.minValue){
22513             this.markInvalid(String.format(this.minText, this.minValue));
22514             return false;
22515         }
22516         if(num > this.maxValue){
22517             this.markInvalid(String.format(this.maxText, this.maxValue));
22518             return false;
22519         }
22520         return true;
22521     },
22522
22523     getValue : function(){
22524         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
22525     },
22526
22527     // private
22528     parseValue : function(value){
22529         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
22530         return isNaN(value) ? '' : value;
22531     },
22532
22533     // private
22534     fixPrecision : function(value){
22535         var nan = isNaN(value);
22536         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
22537             return nan ? '' : value;
22538         }
22539         return parseFloat(value).toFixed(this.decimalPrecision);
22540     },
22541
22542     setValue : function(v){
22543         v = this.fixPrecision(v);
22544         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
22545     },
22546
22547     // private
22548     decimalPrecisionFcn : function(v){
22549         return Math.floor(v);
22550     },
22551
22552     beforeBlur : function(){
22553         var v = this.parseValue(this.getRawValue());
22554         if(v){
22555             this.setValue(v);
22556         }
22557     }
22558 });/*
22559  * Based on:
22560  * Ext JS Library 1.1.1
22561  * Copyright(c) 2006-2007, Ext JS, LLC.
22562  *
22563  * Originally Released Under LGPL - original licence link has changed is not relivant.
22564  *
22565  * Fork - LGPL
22566  * <script type="text/javascript">
22567  */
22568  
22569 /**
22570  * @class Roo.form.DateField
22571  * @extends Roo.form.TriggerField
22572  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22573 * @constructor
22574 * Create a new DateField
22575 * @param {Object} config
22576  */
22577 Roo.form.DateField = function(config){
22578     Roo.form.DateField.superclass.constructor.call(this, config);
22579     
22580       this.addEvents({
22581          
22582         /**
22583          * @event select
22584          * Fires when a date is selected
22585              * @param {Roo.form.DateField} combo This combo box
22586              * @param {Date} date The date selected
22587              */
22588         'select' : true
22589          
22590     });
22591     
22592     
22593     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22594     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22595     this.ddMatch = null;
22596     if(this.disabledDates){
22597         var dd = this.disabledDates;
22598         var re = "(?:";
22599         for(var i = 0; i < dd.length; i++){
22600             re += dd[i];
22601             if(i != dd.length-1) re += "|";
22602         }
22603         this.ddMatch = new RegExp(re + ")");
22604     }
22605 };
22606
22607 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
22608     /**
22609      * @cfg {String} format
22610      * The default date format string which can be overriden for localization support.  The format must be
22611      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22612      */
22613     format : "m/d/y",
22614     /**
22615      * @cfg {String} altFormats
22616      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22617      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22618      */
22619     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22620     /**
22621      * @cfg {Array} disabledDays
22622      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22623      */
22624     disabledDays : null,
22625     /**
22626      * @cfg {String} disabledDaysText
22627      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22628      */
22629     disabledDaysText : "Disabled",
22630     /**
22631      * @cfg {Array} disabledDates
22632      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22633      * expression so they are very powerful. Some examples:
22634      * <ul>
22635      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22636      * <li>["03/08", "09/16"] would disable those days for every year</li>
22637      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22638      * <li>["03/../2006"] would disable every day in March 2006</li>
22639      * <li>["^03"] would disable every day in every March</li>
22640      * </ul>
22641      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22642      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22643      */
22644     disabledDates : null,
22645     /**
22646      * @cfg {String} disabledDatesText
22647      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22648      */
22649     disabledDatesText : "Disabled",
22650     /**
22651      * @cfg {Date/String} minValue
22652      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22653      * valid format (defaults to null).
22654      */
22655     minValue : null,
22656     /**
22657      * @cfg {Date/String} maxValue
22658      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22659      * valid format (defaults to null).
22660      */
22661     maxValue : null,
22662     /**
22663      * @cfg {String} minText
22664      * The error text to display when the date in the cell is before minValue (defaults to
22665      * 'The date in this field must be after {minValue}').
22666      */
22667     minText : "The date in this field must be equal to or after {0}",
22668     /**
22669      * @cfg {String} maxText
22670      * The error text to display when the date in the cell is after maxValue (defaults to
22671      * 'The date in this field must be before {maxValue}').
22672      */
22673     maxText : "The date in this field must be equal to or before {0}",
22674     /**
22675      * @cfg {String} invalidText
22676      * The error text to display when the date in the field is invalid (defaults to
22677      * '{value} is not a valid date - it must be in the format {format}').
22678      */
22679     invalidText : "{0} is not a valid date - it must be in the format {1}",
22680     /**
22681      * @cfg {String} triggerClass
22682      * An additional CSS class used to style the trigger button.  The trigger will always get the
22683      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
22684      * which displays a calendar icon).
22685      */
22686     triggerClass : 'x-form-date-trigger',
22687     
22688
22689     /**
22690      * @cfg {Boolean} useIso
22691      * if enabled, then the date field will use a hidden field to store the 
22692      * real value as iso formated date. default (false)
22693      */ 
22694     useIso : false,
22695     /**
22696      * @cfg {String/Object} autoCreate
22697      * A DomHelper element spec, or true for a default element spec (defaults to
22698      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
22699      */ 
22700     // private
22701     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
22702     
22703     // private
22704     hiddenField: false,
22705     
22706     onRender : function(ct, position)
22707     {
22708         Roo.form.DateField.superclass.onRender.call(this, ct, position);
22709         if (this.useIso) {
22710             this.el.dom.removeAttribute('name'); 
22711             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
22712                     'before', true);
22713             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
22714             // prevent input submission
22715             this.hiddenName = this.name;
22716         }
22717             
22718             
22719     },
22720     
22721     // private
22722     validateValue : function(value)
22723     {
22724         value = this.formatDate(value);
22725         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
22726             return false;
22727         }
22728         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
22729              return true;
22730         }
22731         var svalue = value;
22732         value = this.parseDate(value);
22733         if(!value){
22734             this.markInvalid(String.format(this.invalidText, svalue, this.format));
22735             return false;
22736         }
22737         var time = value.getTime();
22738         if(this.minValue && time < this.minValue.getTime()){
22739             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
22740             return false;
22741         }
22742         if(this.maxValue && time > this.maxValue.getTime()){
22743             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
22744             return false;
22745         }
22746         if(this.disabledDays){
22747             var day = value.getDay();
22748             for(var i = 0; i < this.disabledDays.length; i++) {
22749                 if(day === this.disabledDays[i]){
22750                     this.markInvalid(this.disabledDaysText);
22751                     return false;
22752                 }
22753             }
22754         }
22755         var fvalue = this.formatDate(value);
22756         if(this.ddMatch && this.ddMatch.test(fvalue)){
22757             this.markInvalid(String.format(this.disabledDatesText, fvalue));
22758             return false;
22759         }
22760         return true;
22761     },
22762
22763     // private
22764     // Provides logic to override the default TriggerField.validateBlur which just returns true
22765     validateBlur : function(){
22766         return !this.menu || !this.menu.isVisible();
22767     },
22768
22769     /**
22770      * Returns the current date value of the date field.
22771      * @return {Date} The date value
22772      */
22773     getValue : function(){
22774         
22775         return  this.hiddenField ?
22776                 this.hiddenField.value :
22777                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
22778     },
22779
22780     /**
22781      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
22782      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
22783      * (the default format used is "m/d/y").
22784      * <br />Usage:
22785      * <pre><code>
22786 //All of these calls set the same date value (May 4, 2006)
22787
22788 //Pass a date object:
22789 var dt = new Date('5/4/06');
22790 dateField.setValue(dt);
22791
22792 //Pass a date string (default format):
22793 dateField.setValue('5/4/06');
22794
22795 //Pass a date string (custom format):
22796 dateField.format = 'Y-m-d';
22797 dateField.setValue('2006-5-4');
22798 </code></pre>
22799      * @param {String/Date} date The date or valid date string
22800      */
22801     setValue : function(date){
22802         if (this.hiddenField) {
22803             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
22804         }
22805         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
22806         // make sure the value field is always stored as a date..
22807         this.value = this.parseDate(date);
22808         
22809         
22810     },
22811
22812     // private
22813     parseDate : function(value){
22814         if(!value || value instanceof Date){
22815             return value;
22816         }
22817         var v = Date.parseDate(value, this.format);
22818          if (this.useIso) {
22819             v = Date.parseDate(value, 'Y-m-d');
22820         }
22821         if(!v && this.altFormats){
22822             if(!this.altFormatsArray){
22823                 this.altFormatsArray = this.altFormats.split("|");
22824             }
22825             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22826                 v = Date.parseDate(value, this.altFormatsArray[i]);
22827             }
22828         }
22829         return v;
22830     },
22831
22832     // private
22833     formatDate : function(date, fmt){
22834         return (!date || !(date instanceof Date)) ?
22835                date : date.dateFormat(fmt || this.format);
22836     },
22837
22838     // private
22839     menuListeners : {
22840         select: function(m, d){
22841             this.setValue(d);
22842             this.fireEvent('select', this, d);
22843         },
22844         show : function(){ // retain focus styling
22845             this.onFocus();
22846         },
22847         hide : function(){
22848             this.focus.defer(10, this);
22849             var ml = this.menuListeners;
22850             this.menu.un("select", ml.select,  this);
22851             this.menu.un("show", ml.show,  this);
22852             this.menu.un("hide", ml.hide,  this);
22853         }
22854     },
22855
22856     // private
22857     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22858     onTriggerClick : function(){
22859         if(this.disabled){
22860             return;
22861         }
22862         if(this.menu == null){
22863             this.menu = new Roo.menu.DateMenu();
22864         }
22865         Roo.apply(this.menu.picker,  {
22866             showClear: this.allowBlank,
22867             minDate : this.minValue,
22868             maxDate : this.maxValue,
22869             disabledDatesRE : this.ddMatch,
22870             disabledDatesText : this.disabledDatesText,
22871             disabledDays : this.disabledDays,
22872             disabledDaysText : this.disabledDaysText,
22873             format : this.useIso ? 'Y-m-d' : this.format,
22874             minText : String.format(this.minText, this.formatDate(this.minValue)),
22875             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22876         });
22877         this.menu.on(Roo.apply({}, this.menuListeners, {
22878             scope:this
22879         }));
22880         this.menu.picker.setValue(this.getValue() || new Date());
22881         this.menu.show(this.el, "tl-bl?");
22882     },
22883
22884     beforeBlur : function(){
22885         var v = this.parseDate(this.getRawValue());
22886         if(v){
22887             this.setValue(v);
22888         }
22889     }
22890
22891     /** @cfg {Boolean} grow @hide */
22892     /** @cfg {Number} growMin @hide */
22893     /** @cfg {Number} growMax @hide */
22894     /**
22895      * @hide
22896      * @method autoSize
22897      */
22898 });/*
22899  * Based on:
22900  * Ext JS Library 1.1.1
22901  * Copyright(c) 2006-2007, Ext JS, LLC.
22902  *
22903  * Originally Released Under LGPL - original licence link has changed is not relivant.
22904  *
22905  * Fork - LGPL
22906  * <script type="text/javascript">
22907  */
22908  
22909 /**
22910  * @class Roo.form.MonthField
22911  * @extends Roo.form.TriggerField
22912  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22913 * @constructor
22914 * Create a new MonthField
22915 * @param {Object} config
22916  */
22917 Roo.form.MonthField = function(config){
22918     
22919     Roo.form.MonthField.superclass.constructor.call(this, config);
22920     
22921       this.addEvents({
22922          
22923         /**
22924          * @event select
22925          * Fires when a date is selected
22926              * @param {Roo.form.MonthFieeld} combo This combo box
22927              * @param {Date} date The date selected
22928              */
22929         'select' : true
22930          
22931     });
22932     
22933     
22934     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22935     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22936     this.ddMatch = null;
22937     if(this.disabledDates){
22938         var dd = this.disabledDates;
22939         var re = "(?:";
22940         for(var i = 0; i < dd.length; i++){
22941             re += dd[i];
22942             if(i != dd.length-1) re += "|";
22943         }
22944         this.ddMatch = new RegExp(re + ")");
22945     }
22946 };
22947
22948 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22949     /**
22950      * @cfg {String} format
22951      * The default date format string which can be overriden for localization support.  The format must be
22952      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22953      */
22954     format : "M Y",
22955     /**
22956      * @cfg {String} altFormats
22957      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22958      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22959      */
22960     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22961     /**
22962      * @cfg {Array} disabledDays
22963      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22964      */
22965     disabledDays : [0,1,2,3,4,5,6],
22966     /**
22967      * @cfg {String} disabledDaysText
22968      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22969      */
22970     disabledDaysText : "Disabled",
22971     /**
22972      * @cfg {Array} disabledDates
22973      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22974      * expression so they are very powerful. Some examples:
22975      * <ul>
22976      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22977      * <li>["03/08", "09/16"] would disable those days for every year</li>
22978      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22979      * <li>["03/../2006"] would disable every day in March 2006</li>
22980      * <li>["^03"] would disable every day in every March</li>
22981      * </ul>
22982      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22983      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22984      */
22985     disabledDates : null,
22986     /**
22987      * @cfg {String} disabledDatesText
22988      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22989      */
22990     disabledDatesText : "Disabled",
22991     /**
22992      * @cfg {Date/String} minValue
22993      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22994      * valid format (defaults to null).
22995      */
22996     minValue : null,
22997     /**
22998      * @cfg {Date/String} maxValue
22999      * The maximum allowed date. Can be either a Javascript date object or a string date in a
23000      * valid format (defaults to null).
23001      */
23002     maxValue : null,
23003     /**
23004      * @cfg {String} minText
23005      * The error text to display when the date in the cell is before minValue (defaults to
23006      * 'The date in this field must be after {minValue}').
23007      */
23008     minText : "The date in this field must be equal to or after {0}",
23009     /**
23010      * @cfg {String} maxTextf
23011      * The error text to display when the date in the cell is after maxValue (defaults to
23012      * 'The date in this field must be before {maxValue}').
23013      */
23014     maxText : "The date in this field must be equal to or before {0}",
23015     /**
23016      * @cfg {String} invalidText
23017      * The error text to display when the date in the field is invalid (defaults to
23018      * '{value} is not a valid date - it must be in the format {format}').
23019      */
23020     invalidText : "{0} is not a valid date - it must be in the format {1}",
23021     /**
23022      * @cfg {String} triggerClass
23023      * An additional CSS class used to style the trigger button.  The trigger will always get the
23024      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23025      * which displays a calendar icon).
23026      */
23027     triggerClass : 'x-form-date-trigger',
23028     
23029
23030     /**
23031      * @cfg {Boolean} useIso
23032      * if enabled, then the date field will use a hidden field to store the 
23033      * real value as iso formated date. default (true)
23034      */ 
23035     useIso : true,
23036     /**
23037      * @cfg {String/Object} autoCreate
23038      * A DomHelper element spec, or true for a default element spec (defaults to
23039      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23040      */ 
23041     // private
23042     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23043     
23044     // private
23045     hiddenField: false,
23046     
23047     hideMonthPicker : false,
23048     
23049     onRender : function(ct, position)
23050     {
23051         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23052         if (this.useIso) {
23053             this.el.dom.removeAttribute('name'); 
23054             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23055                     'before', true);
23056             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23057             // prevent input submission
23058             this.hiddenName = this.name;
23059         }
23060             
23061             
23062     },
23063     
23064     // private
23065     validateValue : function(value)
23066     {
23067         value = this.formatDate(value);
23068         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
23069             return false;
23070         }
23071         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23072              return true;
23073         }
23074         var svalue = value;
23075         value = this.parseDate(value);
23076         if(!value){
23077             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23078             return false;
23079         }
23080         var time = value.getTime();
23081         if(this.minValue && time < this.minValue.getTime()){
23082             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23083             return false;
23084         }
23085         if(this.maxValue && time > this.maxValue.getTime()){
23086             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23087             return false;
23088         }
23089         /*if(this.disabledDays){
23090             var day = value.getDay();
23091             for(var i = 0; i < this.disabledDays.length; i++) {
23092                 if(day === this.disabledDays[i]){
23093                     this.markInvalid(this.disabledDaysText);
23094                     return false;
23095                 }
23096             }
23097         }
23098         */
23099         var fvalue = this.formatDate(value);
23100         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23101             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23102             return false;
23103         }
23104         */
23105         return true;
23106     },
23107
23108     // private
23109     // Provides logic to override the default TriggerField.validateBlur which just returns true
23110     validateBlur : function(){
23111         return !this.menu || !this.menu.isVisible();
23112     },
23113
23114     /**
23115      * Returns the current date value of the date field.
23116      * @return {Date} The date value
23117      */
23118     getValue : function(){
23119         
23120         
23121         
23122         return  this.hiddenField ?
23123                 this.hiddenField.value :
23124                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
23125     },
23126
23127     /**
23128      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23129      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
23130      * (the default format used is "m/d/y").
23131      * <br />Usage:
23132      * <pre><code>
23133 //All of these calls set the same date value (May 4, 2006)
23134
23135 //Pass a date object:
23136 var dt = new Date('5/4/06');
23137 monthField.setValue(dt);
23138
23139 //Pass a date string (default format):
23140 monthField.setValue('5/4/06');
23141
23142 //Pass a date string (custom format):
23143 monthField.format = 'Y-m-d';
23144 monthField.setValue('2006-5-4');
23145 </code></pre>
23146      * @param {String/Date} date The date or valid date string
23147      */
23148     setValue : function(date){
23149         Roo.log('month setValue' + date);
23150         if (this.hiddenField) {
23151             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23152         }
23153         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23154         this.value = this.parseDate(date);
23155     },
23156
23157     // private
23158     parseDate : function(value){
23159         if(!value || value instanceof Date){
23160             return value;
23161         }
23162         var v = Date.parseDate(value, this.format);
23163         if (this.useIso) {
23164             v = Date.parseDate(value, 'Y-m-d');
23165         }
23166         
23167         
23168         if(!v && this.altFormats){
23169             if(!this.altFormatsArray){
23170                 this.altFormatsArray = this.altFormats.split("|");
23171             }
23172             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23173                 v = Date.parseDate(value, this.altFormatsArray[i]);
23174             }
23175         }
23176         return v;
23177     },
23178
23179     // private
23180     formatDate : function(date, fmt){
23181         return (!date || !(date instanceof Date)) ?
23182                date : date.dateFormat(fmt || this.format);
23183     },
23184
23185     // private
23186     menuListeners : {
23187         select: function(m, d){
23188             this.setValue(d);
23189             this.fireEvent('select', this, d);
23190         },
23191         show : function(){ // retain focus styling
23192             this.onFocus();
23193         },
23194         hide : function(){
23195             this.focus.defer(10, this);
23196             var ml = this.menuListeners;
23197             this.menu.un("select", ml.select,  this);
23198             this.menu.un("show", ml.show,  this);
23199             this.menu.un("hide", ml.hide,  this);
23200         }
23201     },
23202     // private
23203     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23204     onTriggerClick : function(){
23205         if(this.disabled){
23206             return;
23207         }
23208         if(this.menu == null){
23209             this.menu = new Roo.menu.DateMenu();
23210            
23211         }
23212         
23213         Roo.apply(this.menu.picker,  {
23214             
23215             showClear: this.allowBlank,
23216             minDate : this.minValue,
23217             maxDate : this.maxValue,
23218             disabledDatesRE : this.ddMatch,
23219             disabledDatesText : this.disabledDatesText,
23220             
23221             format : this.format,
23222             minText : String.format(this.minText, this.formatDate(this.minValue)),
23223             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23224             
23225         });
23226         
23227        
23228         
23229         var m = this.menu;
23230         var p = m.picker;
23231         p.format = this.useIso ? 'Y-m-d' : this.format;  // make sure they are the same..?
23232         Roo.log('picker set value');
23233         Roo.log(this.getValue());
23234         p.setValue(this.getValue() || new Date());
23235         m.show(this.el, 'tl-bl?');
23236         
23237         // hidden the day picker
23238         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23239         
23240         
23241         p.hideMonthPicker  = function(disableAnim){
23242             if(this.monthPicker){
23243                 if(disableAnim === true){
23244                     this.monthPicker.hide();
23245                 }else{
23246                     this.monthPicker.slideOut('t', {duration:.2});
23247                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth));
23248                     p.fireEvent("select", this, this.value);
23249                     m.hide();
23250                 }
23251             }
23252         }
23253         this.menu.on(  this.menuListeners, {
23254                 scope:this
23255             });
23256         
23257         p.showMonthPicker.defer(100, p);
23258     
23259         
23260        
23261     },
23262
23263     beforeBlur : function(){
23264         var v = this.parseDate(this.getRawValue());
23265         if(v){
23266             this.setValue(v);
23267         }
23268     }
23269
23270     /** @cfg {Boolean} grow @hide */
23271     /** @cfg {Number} growMin @hide */
23272     /** @cfg {Number} growMax @hide */
23273     /**
23274      * @hide
23275      * @method autoSize
23276      */
23277 });/*
23278  * Based on:
23279  * Ext JS Library 1.1.1
23280  * Copyright(c) 2006-2007, Ext JS, LLC.
23281  *
23282  * Originally Released Under LGPL - original licence link has changed is not relivant.
23283  *
23284  * Fork - LGPL
23285  * <script type="text/javascript">
23286  */
23287  
23288
23289 /**
23290  * @class Roo.form.ComboBox
23291  * @extends Roo.form.TriggerField
23292  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23293  * @constructor
23294  * Create a new ComboBox.
23295  * @param {Object} config Configuration options
23296  */
23297 Roo.form.ComboBox = function(config){
23298     Roo.form.ComboBox.superclass.constructor.call(this, config);
23299     this.addEvents({
23300         /**
23301          * @event expand
23302          * Fires when the dropdown list is expanded
23303              * @param {Roo.form.ComboBox} combo This combo box
23304              */
23305         'expand' : true,
23306         /**
23307          * @event collapse
23308          * Fires when the dropdown list is collapsed
23309              * @param {Roo.form.ComboBox} combo This combo box
23310              */
23311         'collapse' : true,
23312         /**
23313          * @event beforeselect
23314          * Fires before a list item is selected. Return false to cancel the selection.
23315              * @param {Roo.form.ComboBox} combo This combo box
23316              * @param {Roo.data.Record} record The data record returned from the underlying store
23317              * @param {Number} index The index of the selected item in the dropdown list
23318              */
23319         'beforeselect' : true,
23320         /**
23321          * @event select
23322          * Fires when a list item is selected
23323              * @param {Roo.form.ComboBox} combo This combo box
23324              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23325              * @param {Number} index The index of the selected item in the dropdown list
23326              */
23327         'select' : true,
23328         /**
23329          * @event beforequery
23330          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23331          * The event object passed has these properties:
23332              * @param {Roo.form.ComboBox} combo This combo box
23333              * @param {String} query The query
23334              * @param {Boolean} forceAll true to force "all" query
23335              * @param {Boolean} cancel true to cancel the query
23336              * @param {Object} e The query event object
23337              */
23338         'beforequery': true,
23339          /**
23340          * @event add
23341          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23342              * @param {Roo.form.ComboBox} combo This combo box
23343              */
23344         'add' : true,
23345         /**
23346          * @event edit
23347          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23348              * @param {Roo.form.ComboBox} combo This combo box
23349              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23350              */
23351         'edit' : true
23352         
23353         
23354     });
23355     if(this.transform){
23356         this.allowDomMove = false;
23357         var s = Roo.getDom(this.transform);
23358         if(!this.hiddenName){
23359             this.hiddenName = s.name;
23360         }
23361         if(!this.store){
23362             this.mode = 'local';
23363             var d = [], opts = s.options;
23364             for(var i = 0, len = opts.length;i < len; i++){
23365                 var o = opts[i];
23366                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23367                 if(o.selected) {
23368                     this.value = value;
23369                 }
23370                 d.push([value, o.text]);
23371             }
23372             this.store = new Roo.data.SimpleStore({
23373                 'id': 0,
23374                 fields: ['value', 'text'],
23375                 data : d
23376             });
23377             this.valueField = 'value';
23378             this.displayField = 'text';
23379         }
23380         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23381         if(!this.lazyRender){
23382             this.target = true;
23383             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23384             s.parentNode.removeChild(s); // remove it
23385             this.render(this.el.parentNode);
23386         }else{
23387             s.parentNode.removeChild(s); // remove it
23388         }
23389
23390     }
23391     if (this.store) {
23392         this.store = Roo.factory(this.store, Roo.data);
23393     }
23394     
23395     this.selectedIndex = -1;
23396     if(this.mode == 'local'){
23397         if(config.queryDelay === undefined){
23398             this.queryDelay = 10;
23399         }
23400         if(config.minChars === undefined){
23401             this.minChars = 0;
23402         }
23403     }
23404 };
23405
23406 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23407     /**
23408      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23409      */
23410     /**
23411      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23412      * rendering into an Roo.Editor, defaults to false)
23413      */
23414     /**
23415      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23416      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23417      */
23418     /**
23419      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23420      */
23421     /**
23422      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23423      * the dropdown list (defaults to undefined, with no header element)
23424      */
23425
23426      /**
23427      * @cfg {String/Roo.Template} tpl The template to use to render the output
23428      */
23429      
23430     // private
23431     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23432     /**
23433      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23434      */
23435     listWidth: undefined,
23436     /**
23437      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23438      * mode = 'remote' or 'text' if mode = 'local')
23439      */
23440     displayField: undefined,
23441     /**
23442      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23443      * mode = 'remote' or 'value' if mode = 'local'). 
23444      * Note: use of a valueField requires the user make a selection
23445      * in order for a value to be mapped.
23446      */
23447     valueField: undefined,
23448     
23449     
23450     /**
23451      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23452      * field's data value (defaults to the underlying DOM element's name)
23453      */
23454     hiddenName: undefined,
23455     /**
23456      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23457      */
23458     listClass: '',
23459     /**
23460      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23461      */
23462     selectedClass: 'x-combo-selected',
23463     /**
23464      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23465      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23466      * which displays a downward arrow icon).
23467      */
23468     triggerClass : 'x-form-arrow-trigger',
23469     /**
23470      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23471      */
23472     shadow:'sides',
23473     /**
23474      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23475      * anchor positions (defaults to 'tl-bl')
23476      */
23477     listAlign: 'tl-bl?',
23478     /**
23479      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23480      */
23481     maxHeight: 300,
23482     /**
23483      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23484      * query specified by the allQuery config option (defaults to 'query')
23485      */
23486     triggerAction: 'query',
23487     /**
23488      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23489      * (defaults to 4, does not apply if editable = false)
23490      */
23491     minChars : 4,
23492     /**
23493      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23494      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23495      */
23496     typeAhead: false,
23497     /**
23498      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23499      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23500      */
23501     queryDelay: 500,
23502     /**
23503      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23504      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23505      */
23506     pageSize: 0,
23507     /**
23508      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23509      * when editable = true (defaults to false)
23510      */
23511     selectOnFocus:false,
23512     /**
23513      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23514      */
23515     queryParam: 'query',
23516     /**
23517      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23518      * when mode = 'remote' (defaults to 'Loading...')
23519      */
23520     loadingText: 'Loading...',
23521     /**
23522      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23523      */
23524     resizable: false,
23525     /**
23526      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23527      */
23528     handleHeight : 8,
23529     /**
23530      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23531      * traditional select (defaults to true)
23532      */
23533     editable: true,
23534     /**
23535      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23536      */
23537     allQuery: '',
23538     /**
23539      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23540      */
23541     mode: 'remote',
23542     /**
23543      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23544      * listWidth has a higher value)
23545      */
23546     minListWidth : 70,
23547     /**
23548      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23549      * allow the user to set arbitrary text into the field (defaults to false)
23550      */
23551     forceSelection:false,
23552     /**
23553      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23554      * if typeAhead = true (defaults to 250)
23555      */
23556     typeAheadDelay : 250,
23557     /**
23558      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23559      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23560      */
23561     valueNotFoundText : undefined,
23562     /**
23563      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23564      */
23565     blockFocus : false,
23566     
23567     /**
23568      * @cfg {Boolean} disableClear Disable showing of clear button.
23569      */
23570     disableClear : false,
23571     /**
23572      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23573      */
23574     alwaysQuery : false,
23575     
23576     //private
23577     addicon : false,
23578     editicon: false,
23579     
23580     // element that contains real text value.. (when hidden is used..)
23581      
23582     // private
23583     onRender : function(ct, position){
23584         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23585         if(this.hiddenName){
23586             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23587                     'before', true);
23588             this.hiddenField.value =
23589                 this.hiddenValue !== undefined ? this.hiddenValue :
23590                 this.value !== undefined ? this.value : '';
23591
23592             // prevent input submission
23593             this.el.dom.removeAttribute('name');
23594              
23595              
23596         }
23597         if(Roo.isGecko){
23598             this.el.dom.setAttribute('autocomplete', 'off');
23599         }
23600
23601         var cls = 'x-combo-list';
23602
23603         this.list = new Roo.Layer({
23604             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23605         });
23606
23607         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23608         this.list.setWidth(lw);
23609         this.list.swallowEvent('mousewheel');
23610         this.assetHeight = 0;
23611
23612         if(this.title){
23613             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23614             this.assetHeight += this.header.getHeight();
23615         }
23616
23617         this.innerList = this.list.createChild({cls:cls+'-inner'});
23618         this.innerList.on('mouseover', this.onViewOver, this);
23619         this.innerList.on('mousemove', this.onViewMove, this);
23620         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23621         
23622         if(this.allowBlank && !this.pageSize && !this.disableClear){
23623             this.footer = this.list.createChild({cls:cls+'-ft'});
23624             this.pageTb = new Roo.Toolbar(this.footer);
23625            
23626         }
23627         if(this.pageSize){
23628             this.footer = this.list.createChild({cls:cls+'-ft'});
23629             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23630                     {pageSize: this.pageSize});
23631             
23632         }
23633         
23634         if (this.pageTb && this.allowBlank && !this.disableClear) {
23635             var _this = this;
23636             this.pageTb.add(new Roo.Toolbar.Fill(), {
23637                 cls: 'x-btn-icon x-btn-clear',
23638                 text: '&#160;',
23639                 handler: function()
23640                 {
23641                     _this.collapse();
23642                     _this.clearValue();
23643                     _this.onSelect(false, -1);
23644                 }
23645             });
23646         }
23647         if (this.footer) {
23648             this.assetHeight += this.footer.getHeight();
23649         }
23650         
23651
23652         if(!this.tpl){
23653             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23654         }
23655
23656         this.view = new Roo.View(this.innerList, this.tpl, {
23657             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23658         });
23659
23660         this.view.on('click', this.onViewClick, this);
23661
23662         this.store.on('beforeload', this.onBeforeLoad, this);
23663         this.store.on('load', this.onLoad, this);
23664         this.store.on('loadexception', this.onLoadException, this);
23665
23666         if(this.resizable){
23667             this.resizer = new Roo.Resizable(this.list,  {
23668                pinned:true, handles:'se'
23669             });
23670             this.resizer.on('resize', function(r, w, h){
23671                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23672                 this.listWidth = w;
23673                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23674                 this.restrictHeight();
23675             }, this);
23676             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23677         }
23678         if(!this.editable){
23679             this.editable = true;
23680             this.setEditable(false);
23681         }  
23682         
23683         
23684         if (typeof(this.events.add.listeners) != 'undefined') {
23685             
23686             this.addicon = this.wrap.createChild(
23687                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23688        
23689             this.addicon.on('click', function(e) {
23690                 this.fireEvent('add', this);
23691             }, this);
23692         }
23693         if (typeof(this.events.edit.listeners) != 'undefined') {
23694             
23695             this.editicon = this.wrap.createChild(
23696                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23697             if (this.addicon) {
23698                 this.editicon.setStyle('margin-left', '40px');
23699             }
23700             this.editicon.on('click', function(e) {
23701                 
23702                 // we fire even  if inothing is selected..
23703                 this.fireEvent('edit', this, this.lastData );
23704                 
23705             }, this);
23706         }
23707         
23708         
23709         
23710     },
23711
23712     // private
23713     initEvents : function(){
23714         Roo.form.ComboBox.superclass.initEvents.call(this);
23715
23716         this.keyNav = new Roo.KeyNav(this.el, {
23717             "up" : function(e){
23718                 this.inKeyMode = true;
23719                 this.selectPrev();
23720             },
23721
23722             "down" : function(e){
23723                 if(!this.isExpanded()){
23724                     this.onTriggerClick();
23725                 }else{
23726                     this.inKeyMode = true;
23727                     this.selectNext();
23728                 }
23729             },
23730
23731             "enter" : function(e){
23732                 this.onViewClick();
23733                 //return true;
23734             },
23735
23736             "esc" : function(e){
23737                 this.collapse();
23738             },
23739
23740             "tab" : function(e){
23741                 this.onViewClick(false);
23742                 this.fireEvent("specialkey", this, e);
23743                 return true;
23744             },
23745
23746             scope : this,
23747
23748             doRelay : function(foo, bar, hname){
23749                 if(hname == 'down' || this.scope.isExpanded()){
23750                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23751                 }
23752                 return true;
23753             },
23754
23755             forceKeyDown: true
23756         });
23757         this.queryDelay = Math.max(this.queryDelay || 10,
23758                 this.mode == 'local' ? 10 : 250);
23759         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23760         if(this.typeAhead){
23761             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23762         }
23763         if(this.editable !== false){
23764             this.el.on("keyup", this.onKeyUp, this);
23765         }
23766         if(this.forceSelection){
23767             this.on('blur', this.doForce, this);
23768         }
23769     },
23770
23771     onDestroy : function(){
23772         if(this.view){
23773             this.view.setStore(null);
23774             this.view.el.removeAllListeners();
23775             this.view.el.remove();
23776             this.view.purgeListeners();
23777         }
23778         if(this.list){
23779             this.list.destroy();
23780         }
23781         if(this.store){
23782             this.store.un('beforeload', this.onBeforeLoad, this);
23783             this.store.un('load', this.onLoad, this);
23784             this.store.un('loadexception', this.onLoadException, this);
23785         }
23786         Roo.form.ComboBox.superclass.onDestroy.call(this);
23787     },
23788
23789     // private
23790     fireKey : function(e){
23791         if(e.isNavKeyPress() && !this.list.isVisible()){
23792             this.fireEvent("specialkey", this, e);
23793         }
23794     },
23795
23796     // private
23797     onResize: function(w, h){
23798         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23799         
23800         if(typeof w != 'number'){
23801             // we do not handle it!?!?
23802             return;
23803         }
23804         var tw = this.trigger.getWidth();
23805         tw += this.addicon ? this.addicon.getWidth() : 0;
23806         tw += this.editicon ? this.editicon.getWidth() : 0;
23807         var x = w - tw;
23808         this.el.setWidth( this.adjustWidth('input', x));
23809             
23810         this.trigger.setStyle('left', x+'px');
23811         
23812         if(this.list && this.listWidth === undefined){
23813             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23814             this.list.setWidth(lw);
23815             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23816         }
23817         
23818     
23819         
23820     },
23821
23822     /**
23823      * Allow or prevent the user from directly editing the field text.  If false is passed,
23824      * the user will only be able to select from the items defined in the dropdown list.  This method
23825      * is the runtime equivalent of setting the 'editable' config option at config time.
23826      * @param {Boolean} value True to allow the user to directly edit the field text
23827      */
23828     setEditable : function(value){
23829         if(value == this.editable){
23830             return;
23831         }
23832         this.editable = value;
23833         if(!value){
23834             this.el.dom.setAttribute('readOnly', true);
23835             this.el.on('mousedown', this.onTriggerClick,  this);
23836             this.el.addClass('x-combo-noedit');
23837         }else{
23838             this.el.dom.setAttribute('readOnly', false);
23839             this.el.un('mousedown', this.onTriggerClick,  this);
23840             this.el.removeClass('x-combo-noedit');
23841         }
23842     },
23843
23844     // private
23845     onBeforeLoad : function(){
23846         if(!this.hasFocus){
23847             return;
23848         }
23849         this.innerList.update(this.loadingText ?
23850                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23851         this.restrictHeight();
23852         this.selectedIndex = -1;
23853     },
23854
23855     // private
23856     onLoad : function(){
23857         if(!this.hasFocus){
23858             return;
23859         }
23860         if(this.store.getCount() > 0){
23861             this.expand();
23862             this.restrictHeight();
23863             if(this.lastQuery == this.allQuery){
23864                 if(this.editable){
23865                     this.el.dom.select();
23866                 }
23867                 if(!this.selectByValue(this.value, true)){
23868                     this.select(0, true);
23869                 }
23870             }else{
23871                 this.selectNext();
23872                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23873                     this.taTask.delay(this.typeAheadDelay);
23874                 }
23875             }
23876         }else{
23877             this.onEmptyResults();
23878         }
23879         //this.el.focus();
23880     },
23881     // private
23882     onLoadException : function()
23883     {
23884         this.collapse();
23885         Roo.log(this.store.reader.jsonData);
23886         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23887             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23888         }
23889         
23890         
23891     },
23892     // private
23893     onTypeAhead : function(){
23894         if(this.store.getCount() > 0){
23895             var r = this.store.getAt(0);
23896             var newValue = r.data[this.displayField];
23897             var len = newValue.length;
23898             var selStart = this.getRawValue().length;
23899             if(selStart != len){
23900                 this.setRawValue(newValue);
23901                 this.selectText(selStart, newValue.length);
23902             }
23903         }
23904     },
23905
23906     // private
23907     onSelect : function(record, index){
23908         if(this.fireEvent('beforeselect', this, record, index) !== false){
23909             this.setFromData(index > -1 ? record.data : false);
23910             this.collapse();
23911             this.fireEvent('select', this, record, index);
23912         }
23913     },
23914
23915     /**
23916      * Returns the currently selected field value or empty string if no value is set.
23917      * @return {String} value The selected value
23918      */
23919     getValue : function(){
23920         if(this.valueField){
23921             return typeof this.value != 'undefined' ? this.value : '';
23922         }else{
23923             return Roo.form.ComboBox.superclass.getValue.call(this);
23924         }
23925     },
23926
23927     /**
23928      * Clears any text/value currently set in the field
23929      */
23930     clearValue : function(){
23931         if(this.hiddenField){
23932             this.hiddenField.value = '';
23933         }
23934         this.value = '';
23935         this.setRawValue('');
23936         this.lastSelectionText = '';
23937         this.applyEmptyText();
23938     },
23939
23940     /**
23941      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23942      * will be displayed in the field.  If the value does not match the data value of an existing item,
23943      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23944      * Otherwise the field will be blank (although the value will still be set).
23945      * @param {String} value The value to match
23946      */
23947     setValue : function(v){
23948         var text = v;
23949         if(this.valueField){
23950             var r = this.findRecord(this.valueField, v);
23951             if(r){
23952                 text = r.data[this.displayField];
23953             }else if(this.valueNotFoundText !== undefined){
23954                 text = this.valueNotFoundText;
23955             }
23956         }
23957         this.lastSelectionText = text;
23958         if(this.hiddenField){
23959             this.hiddenField.value = v;
23960         }
23961         Roo.form.ComboBox.superclass.setValue.call(this, text);
23962         this.value = v;
23963     },
23964     /**
23965      * @property {Object} the last set data for the element
23966      */
23967     
23968     lastData : false,
23969     /**
23970      * Sets the value of the field based on a object which is related to the record format for the store.
23971      * @param {Object} value the value to set as. or false on reset?
23972      */
23973     setFromData : function(o){
23974         var dv = ''; // display value
23975         var vv = ''; // value value..
23976         this.lastData = o;
23977         if (this.displayField) {
23978             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23979         } else {
23980             // this is an error condition!!!
23981             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23982         }
23983         
23984         if(this.valueField){
23985             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23986         }
23987         if(this.hiddenField){
23988             this.hiddenField.value = vv;
23989             
23990             this.lastSelectionText = dv;
23991             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23992             this.value = vv;
23993             return;
23994         }
23995         // no hidden field.. - we store the value in 'value', but still display
23996         // display field!!!!
23997         this.lastSelectionText = dv;
23998         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23999         this.value = vv;
24000         
24001         
24002     },
24003     // private
24004     reset : function(){
24005         // overridden so that last data is reset..
24006         this.setValue(this.originalValue);
24007         this.clearInvalid();
24008         this.lastData = false;
24009     },
24010     // private
24011     findRecord : function(prop, value){
24012         var record;
24013         if(this.store.getCount() > 0){
24014             this.store.each(function(r){
24015                 if(r.data[prop] == value){
24016                     record = r;
24017                     return false;
24018                 }
24019                 return true;
24020             });
24021         }
24022         return record;
24023     },
24024     
24025     getName: function()
24026     {
24027         // returns hidden if it's set..
24028         if (!this.rendered) {return ''};
24029         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24030         
24031     },
24032     // private
24033     onViewMove : function(e, t){
24034         this.inKeyMode = false;
24035     },
24036
24037     // private
24038     onViewOver : function(e, t){
24039         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24040             return;
24041         }
24042         var item = this.view.findItemFromChild(t);
24043         if(item){
24044             var index = this.view.indexOf(item);
24045             this.select(index, false);
24046         }
24047     },
24048
24049     // private
24050     onViewClick : function(doFocus)
24051     {
24052         var index = this.view.getSelectedIndexes()[0];
24053         var r = this.store.getAt(index);
24054         if(r){
24055             this.onSelect(r, index);
24056         }
24057         if(doFocus !== false && !this.blockFocus){
24058             this.el.focus();
24059         }
24060     },
24061
24062     // private
24063     restrictHeight : function(){
24064         this.innerList.dom.style.height = '';
24065         var inner = this.innerList.dom;
24066         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24067         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24068         this.list.beginUpdate();
24069         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24070         this.list.alignTo(this.el, this.listAlign);
24071         this.list.endUpdate();
24072     },
24073
24074     // private
24075     onEmptyResults : function(){
24076         this.collapse();
24077     },
24078
24079     /**
24080      * Returns true if the dropdown list is expanded, else false.
24081      */
24082     isExpanded : function(){
24083         return this.list.isVisible();
24084     },
24085
24086     /**
24087      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24088      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24089      * @param {String} value The data value of the item to select
24090      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24091      * selected item if it is not currently in view (defaults to true)
24092      * @return {Boolean} True if the value matched an item in the list, else false
24093      */
24094     selectByValue : function(v, scrollIntoView){
24095         if(v !== undefined && v !== null){
24096             var r = this.findRecord(this.valueField || this.displayField, v);
24097             if(r){
24098                 this.select(this.store.indexOf(r), scrollIntoView);
24099                 return true;
24100             }
24101         }
24102         return false;
24103     },
24104
24105     /**
24106      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24107      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24108      * @param {Number} index The zero-based index of the list item to select
24109      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24110      * selected item if it is not currently in view (defaults to true)
24111      */
24112     select : function(index, scrollIntoView){
24113         this.selectedIndex = index;
24114         this.view.select(index);
24115         if(scrollIntoView !== false){
24116             var el = this.view.getNode(index);
24117             if(el){
24118                 this.innerList.scrollChildIntoView(el, false);
24119             }
24120         }
24121     },
24122
24123     // private
24124     selectNext : function(){
24125         var ct = this.store.getCount();
24126         if(ct > 0){
24127             if(this.selectedIndex == -1){
24128                 this.select(0);
24129             }else if(this.selectedIndex < ct-1){
24130                 this.select(this.selectedIndex+1);
24131             }
24132         }
24133     },
24134
24135     // private
24136     selectPrev : function(){
24137         var ct = this.store.getCount();
24138         if(ct > 0){
24139             if(this.selectedIndex == -1){
24140                 this.select(0);
24141             }else if(this.selectedIndex != 0){
24142                 this.select(this.selectedIndex-1);
24143             }
24144         }
24145     },
24146
24147     // private
24148     onKeyUp : function(e){
24149         if(this.editable !== false && !e.isSpecialKey()){
24150             this.lastKey = e.getKey();
24151             this.dqTask.delay(this.queryDelay);
24152         }
24153     },
24154
24155     // private
24156     validateBlur : function(){
24157         return !this.list || !this.list.isVisible();   
24158     },
24159
24160     // private
24161     initQuery : function(){
24162         this.doQuery(this.getRawValue());
24163     },
24164
24165     // private
24166     doForce : function(){
24167         if(this.el.dom.value.length > 0){
24168             this.el.dom.value =
24169                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24170             this.applyEmptyText();
24171         }
24172     },
24173
24174     /**
24175      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24176      * query allowing the query action to be canceled if needed.
24177      * @param {String} query The SQL query to execute
24178      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24179      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24180      * saved in the current store (defaults to false)
24181      */
24182     doQuery : function(q, forceAll){
24183         if(q === undefined || q === null){
24184             q = '';
24185         }
24186         var qe = {
24187             query: q,
24188             forceAll: forceAll,
24189             combo: this,
24190             cancel:false
24191         };
24192         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24193             return false;
24194         }
24195         q = qe.query;
24196         forceAll = qe.forceAll;
24197         if(forceAll === true || (q.length >= this.minChars)){
24198             if(this.lastQuery != q || this.alwaysQuery){
24199                 this.lastQuery = q;
24200                 if(this.mode == 'local'){
24201                     this.selectedIndex = -1;
24202                     if(forceAll){
24203                         this.store.clearFilter();
24204                     }else{
24205                         this.store.filter(this.displayField, q);
24206                     }
24207                     this.onLoad();
24208                 }else{
24209                     this.store.baseParams[this.queryParam] = q;
24210                     this.store.load({
24211                         params: this.getParams(q)
24212                     });
24213                     this.expand();
24214                 }
24215             }else{
24216                 this.selectedIndex = -1;
24217                 this.onLoad();   
24218             }
24219         }
24220     },
24221
24222     // private
24223     getParams : function(q){
24224         var p = {};
24225         //p[this.queryParam] = q;
24226         if(this.pageSize){
24227             p.start = 0;
24228             p.limit = this.pageSize;
24229         }
24230         return p;
24231     },
24232
24233     /**
24234      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24235      */
24236     collapse : function(){
24237         if(!this.isExpanded()){
24238             return;
24239         }
24240         this.list.hide();
24241         Roo.get(document).un('mousedown', this.collapseIf, this);
24242         Roo.get(document).un('mousewheel', this.collapseIf, this);
24243         if (!this.editable) {
24244             Roo.get(document).un('keydown', this.listKeyPress, this);
24245         }
24246         this.fireEvent('collapse', this);
24247     },
24248
24249     // private
24250     collapseIf : function(e){
24251         if(!e.within(this.wrap) && !e.within(this.list)){
24252             this.collapse();
24253         }
24254     },
24255
24256     /**
24257      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24258      */
24259     expand : function(){
24260         if(this.isExpanded() || !this.hasFocus){
24261             return;
24262         }
24263         this.list.alignTo(this.el, this.listAlign);
24264         this.list.show();
24265         Roo.get(document).on('mousedown', this.collapseIf, this);
24266         Roo.get(document).on('mousewheel', this.collapseIf, this);
24267         if (!this.editable) {
24268             Roo.get(document).on('keydown', this.listKeyPress, this);
24269         }
24270         
24271         this.fireEvent('expand', this);
24272     },
24273
24274     // private
24275     // Implements the default empty TriggerField.onTriggerClick function
24276     onTriggerClick : function(){
24277         if(this.disabled){
24278             return;
24279         }
24280         if(this.isExpanded()){
24281             this.collapse();
24282             if (!this.blockFocus) {
24283                 this.el.focus();
24284             }
24285             
24286         }else {
24287             this.hasFocus = true;
24288             if(this.triggerAction == 'all') {
24289                 this.doQuery(this.allQuery, true);
24290             } else {
24291                 this.doQuery(this.getRawValue());
24292             }
24293             if (!this.blockFocus) {
24294                 this.el.focus();
24295             }
24296         }
24297     },
24298     listKeyPress : function(e)
24299     {
24300         //Roo.log('listkeypress');
24301         // scroll to first matching element based on key pres..
24302         if (e.isSpecialKey()) {
24303             return false;
24304         }
24305         var k = String.fromCharCode(e.getKey()).toUpperCase();
24306         //Roo.log(k);
24307         var match  = false;
24308         var csel = this.view.getSelectedNodes();
24309         var cselitem = false;
24310         if (csel.length) {
24311             var ix = this.view.indexOf(csel[0]);
24312             cselitem  = this.store.getAt(ix);
24313             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24314                 cselitem = false;
24315             }
24316             
24317         }
24318         
24319         this.store.each(function(v) { 
24320             if (cselitem) {
24321                 // start at existing selection.
24322                 if (cselitem.id == v.id) {
24323                     cselitem = false;
24324                 }
24325                 return;
24326             }
24327                 
24328             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24329                 match = this.store.indexOf(v);
24330                 return false;
24331             }
24332         }, this);
24333         
24334         if (match === false) {
24335             return true; // no more action?
24336         }
24337         // scroll to?
24338         this.view.select(match);
24339         var sn = Roo.get(this.view.getSelectedNodes()[0])
24340         sn.scrollIntoView(sn.dom.parentNode, false);
24341     }
24342
24343     /** 
24344     * @cfg {Boolean} grow 
24345     * @hide 
24346     */
24347     /** 
24348     * @cfg {Number} growMin 
24349     * @hide 
24350     */
24351     /** 
24352     * @cfg {Number} growMax 
24353     * @hide 
24354     */
24355     /**
24356      * @hide
24357      * @method autoSize
24358      */
24359 });/*
24360  * Copyright(c) 2010-2012, Roo J Solutions Limited
24361  *
24362  * Licence LGPL
24363  *
24364  */
24365
24366 /**
24367  * @class Roo.form.ComboBoxArray
24368  * @extends Roo.form.TextField
24369  * A facebook style adder... for lists of email / people / countries  etc...
24370  * pick multiple items from a combo box, and shows each one.
24371  *
24372  *  Fred [x]  Brian [x]  [Pick another |v]
24373  *
24374  *
24375  *  For this to work: it needs various extra information
24376  *    - normal combo problay has
24377  *      name, hiddenName
24378  *    + displayField, valueField
24379  *
24380  *    For our purpose...
24381  *
24382  *
24383  *   If we change from 'extends' to wrapping...
24384  *   
24385  *  
24386  *
24387  
24388  
24389  * @constructor
24390  * Create a new ComboBoxArray.
24391  * @param {Object} config Configuration options
24392  */
24393  
24394
24395 Roo.form.ComboBoxArray = function(config)
24396 {
24397     
24398     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24399     
24400     this.items = new Roo.util.MixedCollection(false);
24401     
24402     // construct the child combo...
24403     
24404     
24405     
24406     
24407    
24408     
24409 }
24410
24411  
24412 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24413
24414     /**
24415      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24416      */
24417     
24418     lastData : false,
24419     
24420     // behavies liek a hiddne field
24421     inputType:      'hidden',
24422     /**
24423      * @cfg {Number} width The width of the box that displays the selected element
24424      */ 
24425     width:          300,
24426
24427     
24428     
24429     /**
24430      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24431      */
24432     name : false,
24433     /**
24434      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24435      */
24436     hiddenName : false,
24437     
24438     
24439     // private the array of items that are displayed..
24440     items  : false,
24441     // private - the hidden field el.
24442     hiddenEl : false,
24443     // private - the filed el..
24444     el : false,
24445     
24446     //validateValue : function() { return true; }, // all values are ok!
24447     //onAddClick: function() { },
24448     
24449     onRender : function(ct, position) 
24450     {
24451         
24452         // create the standard hidden element
24453         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24454         
24455         
24456         // give fake names to child combo;
24457         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24458         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24459         
24460         this.combo = Roo.factory(this.combo, Roo.form);
24461         this.combo.onRender(ct, position);
24462         
24463         // assigned so form know we need to do this..
24464         this.store          = this.combo.store;
24465         this.valueField     = this.combo.valueField;
24466         this.displayField   = this.combo.displayField ;
24467         
24468         
24469         this.combo.wrap.addClass('x-cbarray-grp');
24470         
24471         var cbwrap = this.combo.wrap.createChild(
24472             {tag: 'div', cls: 'x-cbarray-cb'},
24473             this.combo.el.dom
24474         );
24475         
24476              
24477         this.hiddenEl = this.combo.wrap.createChild({
24478             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24479         });
24480         this.el = this.combo.wrap.createChild({
24481             tag: 'input',  type:'hidden' , name: this.name, value : ''
24482         });
24483          //   this.el.dom.removeAttribute("name");
24484         
24485         
24486         this.outerWrap = this.combo.wrap;
24487         this.wrap = cbwrap;
24488         
24489         this.outerWrap.setWidth(this.width);
24490         this.outerWrap.dom.removeChild(this.el.dom);
24491         
24492         this.wrap.dom.appendChild(this.el.dom);
24493         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24494         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24495         
24496         this.combo.trigger.setStyle('position','relative');
24497         this.combo.trigger.setStyle('left', '0px');
24498         this.combo.trigger.setStyle('top', '2px');
24499         
24500         this.combo.el.setStyle('vertical-align', 'text-bottom');
24501         
24502         //this.trigger.setStyle('vertical-align', 'top');
24503         
24504         // this should use the code from combo really... on('add' ....)
24505         if (this.adder) {
24506             
24507         
24508             this.adder = this.outerWrap.createChild(
24509                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24510             var _t = this;
24511             this.adder.on('click', function(e) {
24512                 _t.fireEvent('adderclick', this, e);
24513             }, _t);
24514         }
24515         //var _t = this;
24516         //this.adder.on('click', this.onAddClick, _t);
24517         
24518         
24519         this.combo.on('select', function(cb, rec, ix) {
24520             this.addItem(rec.data);
24521             
24522             cb.setValue('');
24523             cb.el.dom.value = '';
24524             //cb.lastData = rec.data;
24525             // add to list
24526             
24527         }, this);
24528         
24529         
24530     },
24531     
24532     
24533     getName: function()
24534     {
24535         // returns hidden if it's set..
24536         if (!this.rendered) {return ''};
24537         return  this.hiddenName ? this.hiddenName : this.name;
24538         
24539     },
24540     
24541     
24542     onResize: function(w, h){
24543         
24544         return;
24545         // not sure if this is needed..
24546         //this.combo.onResize(w,h);
24547         
24548         if(typeof w != 'number'){
24549             // we do not handle it!?!?
24550             return;
24551         }
24552         var tw = this.combo.trigger.getWidth();
24553         tw += this.addicon ? this.addicon.getWidth() : 0;
24554         tw += this.editicon ? this.editicon.getWidth() : 0;
24555         var x = w - tw;
24556         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24557             
24558         this.combo.trigger.setStyle('left', '0px');
24559         
24560         if(this.list && this.listWidth === undefined){
24561             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24562             this.list.setWidth(lw);
24563             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24564         }
24565         
24566     
24567         
24568     },
24569     
24570     addItem: function(rec)
24571     {
24572         var valueField = this.combo.valueField;
24573         var displayField = this.combo.displayField;
24574         if (this.items.indexOfKey(rec[valueField]) > -1) {
24575             //console.log("GOT " + rec.data.id);
24576             return;
24577         }
24578         
24579         var x = new Roo.form.ComboBoxArray.Item({
24580             //id : rec[this.idField],
24581             data : rec,
24582             displayField : displayField ,
24583             tipField : displayField ,
24584             cb : this
24585         });
24586         // use the 
24587         this.items.add(rec[valueField],x);
24588         // add it before the element..
24589         this.updateHiddenEl();
24590         x.render(this.outerWrap, this.wrap.dom);
24591         // add the image handler..
24592     },
24593     
24594     updateHiddenEl : function()
24595     {
24596         this.validate();
24597         if (!this.hiddenEl) {
24598             return;
24599         }
24600         var ar = [];
24601         var idField = this.combo.valueField;
24602         
24603         this.items.each(function(f) {
24604             ar.push(f.data[idField]);
24605            
24606         });
24607         this.hiddenEl.dom.value = ar.join(',');
24608         this.validate();
24609     },
24610     
24611     reset : function()
24612     {
24613         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24614         this.items.each(function(f) {
24615            f.remove(); 
24616         });
24617         this.el.dom.value = '';
24618         if (this.hiddenEl) {
24619             this.hiddenEl.dom.value = '';
24620         }
24621         
24622     },
24623     getValue: function()
24624     {
24625         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24626     },
24627     setValue: function(v) // not a valid action - must use addItems..
24628     {
24629          
24630         this.reset();
24631         
24632         
24633         
24634         if (this.store.isLocal && (typeof(v) == 'string')) {
24635             // then we can use the store to find the values..
24636             // comma seperated at present.. this needs to allow JSON based encoding..
24637             this.hiddenEl.value  = v;
24638             var v_ar = [];
24639             Roo.each(v.split(','), function(k) {
24640                 Roo.log("CHECK " + this.valueField + ',' + k);
24641                 var li = this.store.query(this.valueField, k);
24642                 if (!li.length) {
24643                     return;
24644                 }
24645                 add = {};
24646                 add[this.valueField] = k;
24647                 add[this.displayField] = li.item(0).data[this.displayField];
24648                 
24649                 this.addItem(add);
24650             }, this) 
24651              
24652         }
24653         if (typeof(v) == 'object') {
24654             // then let's assume it's an array of objects..
24655             Roo.each(v, function(l) {
24656                 this.addItem(l);
24657             }, this);
24658              
24659         }
24660         
24661         
24662     },
24663     setFromData: function(v)
24664     {
24665         // this recieves an object, if setValues is called.
24666         this.reset();
24667         this.el.dom.value = v[this.displayField];
24668         this.hiddenEl.dom.value = v[this.valueField];
24669         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24670             return;
24671         }
24672         var kv = v[this.valueField];
24673         var dv = v[this.displayField];
24674         kv = typeof(kv) != 'string' ? '' : kv;
24675         dv = typeof(dv) != 'string' ? '' : dv;
24676         
24677         
24678         var keys = kv.split(',');
24679         var display = dv.split(',');
24680         for (var i = 0 ; i < keys.length; i++) {
24681             
24682             add = {};
24683             add[this.valueField] = keys[i];
24684             add[this.displayField] = display[i];
24685             this.addItem(add);
24686         }
24687       
24688         
24689     },
24690     
24691     
24692     validateValue : function(value){
24693         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24694         
24695     }
24696     
24697 });
24698
24699
24700
24701 /**
24702  * @class Roo.form.ComboBoxArray.Item
24703  * @extends Roo.BoxComponent
24704  * A selected item in the list
24705  *  Fred [x]  Brian [x]  [Pick another |v]
24706  * 
24707  * @constructor
24708  * Create a new item.
24709  * @param {Object} config Configuration options
24710  */
24711  
24712 Roo.form.ComboBoxArray.Item = function(config) {
24713     config.id = Roo.id();
24714     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24715 }
24716
24717 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24718     data : {},
24719     cb: false,
24720     displayField : false,
24721     tipField : false,
24722     
24723     
24724     defaultAutoCreate : {
24725         tag: 'div',
24726         cls: 'x-cbarray-item',
24727         cn : [ 
24728             { tag: 'div' },
24729             {
24730                 tag: 'img',
24731                 width:16,
24732                 height : 16,
24733                 src : Roo.BLANK_IMAGE_URL ,
24734                 align: 'center'
24735             }
24736         ]
24737         
24738     },
24739     
24740  
24741     onRender : function(ct, position)
24742     {
24743         Roo.form.Field.superclass.onRender.call(this, ct, position);
24744         
24745         if(!this.el){
24746             var cfg = this.getAutoCreate();
24747             this.el = ct.createChild(cfg, position);
24748         }
24749         
24750         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24751         
24752         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24753             this.cb.renderer(this.data) :
24754             String.format('{0}',this.data[this.displayField]);
24755         
24756             
24757         this.el.child('div').dom.setAttribute('qtip',
24758                         String.format('{0}',this.data[this.tipField])
24759         );
24760         
24761         this.el.child('img').on('click', this.remove, this);
24762         
24763     },
24764    
24765     remove : function()
24766     {
24767         
24768         this.cb.items.remove(this);
24769         this.el.child('img').un('click', this.remove, this);
24770         this.el.remove();
24771         this.cb.updateHiddenEl();
24772     }
24773     
24774     
24775 });/*
24776  * Based on:
24777  * Ext JS Library 1.1.1
24778  * Copyright(c) 2006-2007, Ext JS, LLC.
24779  *
24780  * Originally Released Under LGPL - original licence link has changed is not relivant.
24781  *
24782  * Fork - LGPL
24783  * <script type="text/javascript">
24784  */
24785 /**
24786  * @class Roo.form.Checkbox
24787  * @extends Roo.form.Field
24788  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24789  * @constructor
24790  * Creates a new Checkbox
24791  * @param {Object} config Configuration options
24792  */
24793 Roo.form.Checkbox = function(config){
24794     Roo.form.Checkbox.superclass.constructor.call(this, config);
24795     this.addEvents({
24796         /**
24797          * @event check
24798          * Fires when the checkbox is checked or unchecked.
24799              * @param {Roo.form.Checkbox} this This checkbox
24800              * @param {Boolean} checked The new checked value
24801              */
24802         check : true
24803     });
24804 };
24805
24806 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24807     /**
24808      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24809      */
24810     focusClass : undefined,
24811     /**
24812      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24813      */
24814     fieldClass: "x-form-field",
24815     /**
24816      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24817      */
24818     checked: false,
24819     /**
24820      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24821      * {tag: "input", type: "checkbox", autocomplete: "off"})
24822      */
24823     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24824     /**
24825      * @cfg {String} boxLabel The text that appears beside the checkbox
24826      */
24827     boxLabel : "",
24828     /**
24829      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24830      */  
24831     inputValue : '1',
24832     /**
24833      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24834      */
24835      valueOff: '0', // value when not checked..
24836
24837     actionMode : 'viewEl', 
24838     //
24839     // private
24840     itemCls : 'x-menu-check-item x-form-item',
24841     groupClass : 'x-menu-group-item',
24842     inputType : 'hidden',
24843     
24844     
24845     inSetChecked: false, // check that we are not calling self...
24846     
24847     inputElement: false, // real input element?
24848     basedOn: false, // ????
24849     
24850     isFormField: true, // not sure where this is needed!!!!
24851
24852     onResize : function(){
24853         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24854         if(!this.boxLabel){
24855             this.el.alignTo(this.wrap, 'c-c');
24856         }
24857     },
24858
24859     initEvents : function(){
24860         Roo.form.Checkbox.superclass.initEvents.call(this);
24861         this.el.on("click", this.onClick,  this);
24862         this.el.on("change", this.onClick,  this);
24863     },
24864
24865
24866     getResizeEl : function(){
24867         return this.wrap;
24868     },
24869
24870     getPositionEl : function(){
24871         return this.wrap;
24872     },
24873
24874     // private
24875     onRender : function(ct, position){
24876         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24877         /*
24878         if(this.inputValue !== undefined){
24879             this.el.dom.value = this.inputValue;
24880         }
24881         */
24882         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24883         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24884         var viewEl = this.wrap.createChild({ 
24885             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24886         this.viewEl = viewEl;   
24887         this.wrap.on('click', this.onClick,  this); 
24888         
24889         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24890         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24891         
24892         
24893         
24894         if(this.boxLabel){
24895             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24896         //    viewEl.on('click', this.onClick,  this); 
24897         }
24898         //if(this.checked){
24899             this.setChecked(this.checked);
24900         //}else{
24901             //this.checked = this.el.dom;
24902         //}
24903
24904     },
24905
24906     // private
24907     initValue : Roo.emptyFn,
24908
24909     /**
24910      * Returns the checked state of the checkbox.
24911      * @return {Boolean} True if checked, else false
24912      */
24913     getValue : function(){
24914         if(this.el){
24915             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24916         }
24917         return this.valueOff;
24918         
24919     },
24920
24921         // private
24922     onClick : function(){ 
24923         this.setChecked(!this.checked);
24924
24925         //if(this.el.dom.checked != this.checked){
24926         //    this.setValue(this.el.dom.checked);
24927        // }
24928     },
24929
24930     /**
24931      * Sets the checked state of the checkbox.
24932      * On is always based on a string comparison between inputValue and the param.
24933      * @param {Boolean/String} value - the value to set 
24934      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24935      */
24936     setValue : function(v,suppressEvent){
24937         
24938         
24939         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24940         //if(this.el && this.el.dom){
24941         //    this.el.dom.checked = this.checked;
24942         //    this.el.dom.defaultChecked = this.checked;
24943         //}
24944         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24945         //this.fireEvent("check", this, this.checked);
24946     },
24947     // private..
24948     setChecked : function(state,suppressEvent)
24949     {
24950         if (this.inSetChecked) {
24951             this.checked = state;
24952             return;
24953         }
24954         
24955     
24956         if(this.wrap){
24957             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24958         }
24959         this.checked = state;
24960         if(suppressEvent !== true){
24961             this.fireEvent('check', this, state);
24962         }
24963         this.inSetChecked = true;
24964         this.el.dom.value = state ? this.inputValue : this.valueOff;
24965         this.inSetChecked = false;
24966         
24967     },
24968     // handle setting of hidden value by some other method!!?!?
24969     setFromHidden: function()
24970     {
24971         if(!this.el){
24972             return;
24973         }
24974         //console.log("SET FROM HIDDEN");
24975         //alert('setFrom hidden');
24976         this.setValue(this.el.dom.value);
24977     },
24978     
24979     onDestroy : function()
24980     {
24981         if(this.viewEl){
24982             Roo.get(this.viewEl).remove();
24983         }
24984          
24985         Roo.form.Checkbox.superclass.onDestroy.call(this);
24986     }
24987
24988 });/*
24989  * Based on:
24990  * Ext JS Library 1.1.1
24991  * Copyright(c) 2006-2007, Ext JS, LLC.
24992  *
24993  * Originally Released Under LGPL - original licence link has changed is not relivant.
24994  *
24995  * Fork - LGPL
24996  * <script type="text/javascript">
24997  */
24998  
24999 /**
25000  * @class Roo.form.Radio
25001  * @extends Roo.form.Checkbox
25002  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
25003  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
25004  * @constructor
25005  * Creates a new Radio
25006  * @param {Object} config Configuration options
25007  */
25008 Roo.form.Radio = function(){
25009     Roo.form.Radio.superclass.constructor.apply(this, arguments);
25010 };
25011 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
25012     inputType: 'radio',
25013
25014     /**
25015      * If this radio is part of a group, it will return the selected value
25016      * @return {String}
25017      */
25018     getGroupValue : function(){
25019         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
25020     }
25021 });//<script type="text/javascript">
25022
25023 /*
25024  * Ext JS Library 1.1.1
25025  * Copyright(c) 2006-2007, Ext JS, LLC.
25026  * licensing@extjs.com
25027  * 
25028  * http://www.extjs.com/license
25029  */
25030  
25031  /*
25032   * 
25033   * Known bugs:
25034   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25035   * - IE ? - no idea how much works there.
25036   * 
25037   * 
25038   * 
25039   */
25040  
25041
25042 /**
25043  * @class Ext.form.HtmlEditor
25044  * @extends Ext.form.Field
25045  * Provides a lightweight HTML Editor component.
25046  *
25047  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25048  * 
25049  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25050  * supported by this editor.</b><br/><br/>
25051  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25052  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25053  */
25054 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25055       /**
25056      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25057      */
25058     toolbars : false,
25059     /**
25060      * @cfg {String} createLinkText The default text for the create link prompt
25061      */
25062     createLinkText : 'Please enter the URL for the link:',
25063     /**
25064      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25065      */
25066     defaultLinkValue : 'http:/'+'/',
25067    
25068      /**
25069      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25070      *                        Roo.resizable.
25071      */
25072     resizable : false,
25073      /**
25074      * @cfg {Number} height (in pixels)
25075      */   
25076     height: 300,
25077    /**
25078      * @cfg {Number} width (in pixels)
25079      */   
25080     width: 500,
25081     
25082     /**
25083      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25084      * 
25085      */
25086     stylesheets: false,
25087     
25088     // id of frame..
25089     frameId: false,
25090     
25091     // private properties
25092     validationEvent : false,
25093     deferHeight: true,
25094     initialized : false,
25095     activated : false,
25096     sourceEditMode : false,
25097     onFocus : Roo.emptyFn,
25098     iframePad:3,
25099     hideMode:'offsets',
25100     
25101     defaultAutoCreate : { // modified by initCompnoent..
25102         tag: "textarea",
25103         style:"width:500px;height:300px;",
25104         autocomplete: "off"
25105     },
25106
25107     // private
25108     initComponent : function(){
25109         this.addEvents({
25110             /**
25111              * @event initialize
25112              * Fires when the editor is fully initialized (including the iframe)
25113              * @param {HtmlEditor} this
25114              */
25115             initialize: true,
25116             /**
25117              * @event activate
25118              * Fires when the editor is first receives the focus. Any insertion must wait
25119              * until after this event.
25120              * @param {HtmlEditor} this
25121              */
25122             activate: true,
25123              /**
25124              * @event beforesync
25125              * Fires before the textarea is updated with content from the editor iframe. Return false
25126              * to cancel the sync.
25127              * @param {HtmlEditor} this
25128              * @param {String} html
25129              */
25130             beforesync: true,
25131              /**
25132              * @event beforepush
25133              * Fires before the iframe editor is updated with content from the textarea. Return false
25134              * to cancel the push.
25135              * @param {HtmlEditor} this
25136              * @param {String} html
25137              */
25138             beforepush: true,
25139              /**
25140              * @event sync
25141              * Fires when the textarea is updated with content from the editor iframe.
25142              * @param {HtmlEditor} this
25143              * @param {String} html
25144              */
25145             sync: true,
25146              /**
25147              * @event push
25148              * Fires when the iframe editor is updated with content from the textarea.
25149              * @param {HtmlEditor} this
25150              * @param {String} html
25151              */
25152             push: true,
25153              /**
25154              * @event editmodechange
25155              * Fires when the editor switches edit modes
25156              * @param {HtmlEditor} this
25157              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25158              */
25159             editmodechange: true,
25160             /**
25161              * @event editorevent
25162              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25163              * @param {HtmlEditor} this
25164              */
25165             editorevent: true
25166         });
25167         this.defaultAutoCreate =  {
25168             tag: "textarea",
25169             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25170             autocomplete: "off"
25171         };
25172     },
25173
25174     /**
25175      * Protected method that will not generally be called directly. It
25176      * is called when the editor creates its toolbar. Override this method if you need to
25177      * add custom toolbar buttons.
25178      * @param {HtmlEditor} editor
25179      */
25180     createToolbar : function(editor){
25181         if (!editor.toolbars || !editor.toolbars.length) {
25182             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25183         }
25184         
25185         for (var i =0 ; i < editor.toolbars.length;i++) {
25186             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25187             editor.toolbars[i].init(editor);
25188         }
25189          
25190         
25191     },
25192
25193     /**
25194      * Protected method that will not generally be called directly. It
25195      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25196      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25197      */
25198     getDocMarkup : function(){
25199         // body styles..
25200         var st = '';
25201         if (this.stylesheets === false) {
25202             
25203             Roo.get(document.head).select('style').each(function(node) {
25204                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25205             });
25206             
25207             Roo.get(document.head).select('link').each(function(node) { 
25208                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25209             });
25210             
25211         } else if (!this.stylesheets.length) {
25212                 // simple..
25213                 st = '<style type="text/css">' +
25214                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25215                    '</style>';
25216         } else {
25217             Roo.each(this.stylesheets, function(s) {
25218                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25219             });
25220             
25221         }
25222         
25223         st +=  '<style type="text/css">' +
25224             'IMG { cursor: pointer } ' +
25225         '</style>';
25226
25227         
25228         return '<html><head>' + st  +
25229             //<style type="text/css">' +
25230             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25231             //'</style>' +
25232             ' </head><body class="roo-htmleditor-body"></body></html>';
25233     },
25234
25235     // private
25236     onRender : function(ct, position)
25237     {
25238         var _t = this;
25239         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25240         this.el.dom.style.border = '0 none';
25241         this.el.dom.setAttribute('tabIndex', -1);
25242         this.el.addClass('x-hidden');
25243         if(Roo.isIE){ // fix IE 1px bogus margin
25244             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25245         }
25246         this.wrap = this.el.wrap({
25247             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25248         });
25249         
25250         if (this.resizable) {
25251             this.resizeEl = new Roo.Resizable(this.wrap, {
25252                 pinned : true,
25253                 wrap: true,
25254                 dynamic : true,
25255                 minHeight : this.height,
25256                 height: this.height,
25257                 handles : this.resizable,
25258                 width: this.width,
25259                 listeners : {
25260                     resize : function(r, w, h) {
25261                         _t.onResize(w,h); // -something
25262                     }
25263                 }
25264             });
25265             
25266         }
25267
25268         this.frameId = Roo.id();
25269         
25270         this.createToolbar(this);
25271         
25272       
25273         
25274         var iframe = this.wrap.createChild({
25275             tag: 'iframe',
25276             id: this.frameId,
25277             name: this.frameId,
25278             frameBorder : 'no',
25279             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25280         }, this.el
25281         );
25282         
25283        // console.log(iframe);
25284         //this.wrap.dom.appendChild(iframe);
25285
25286         this.iframe = iframe.dom;
25287
25288          this.assignDocWin();
25289         
25290         this.doc.designMode = 'on';
25291        
25292         this.doc.open();
25293         this.doc.write(this.getDocMarkup());
25294         this.doc.close();
25295
25296         
25297         var task = { // must defer to wait for browser to be ready
25298             run : function(){
25299                 //console.log("run task?" + this.doc.readyState);
25300                 this.assignDocWin();
25301                 if(this.doc.body || this.doc.readyState == 'complete'){
25302                     try {
25303                         this.doc.designMode="on";
25304                     } catch (e) {
25305                         return;
25306                     }
25307                     Roo.TaskMgr.stop(task);
25308                     this.initEditor.defer(10, this);
25309                 }
25310             },
25311             interval : 10,
25312             duration:10000,
25313             scope: this
25314         };
25315         Roo.TaskMgr.start(task);
25316
25317         if(!this.width){
25318             this.setSize(this.wrap.getSize());
25319         }
25320         if (this.resizeEl) {
25321             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25322             // should trigger onReize..
25323         }
25324     },
25325
25326     // private
25327     onResize : function(w, h)
25328     {
25329         //Roo.log('resize: ' +w + ',' + h );
25330         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25331         if(this.el && this.iframe){
25332             if(typeof w == 'number'){
25333                 var aw = w - this.wrap.getFrameWidth('lr');
25334                 this.el.setWidth(this.adjustWidth('textarea', aw));
25335                 this.iframe.style.width = aw + 'px';
25336             }
25337             if(typeof h == 'number'){
25338                 var tbh = 0;
25339                 for (var i =0; i < this.toolbars.length;i++) {
25340                     // fixme - ask toolbars for heights?
25341                     tbh += this.toolbars[i].tb.el.getHeight();
25342                     if (this.toolbars[i].footer) {
25343                         tbh += this.toolbars[i].footer.el.getHeight();
25344                     }
25345                 }
25346                 
25347                 
25348                 
25349                 
25350                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25351                 ah -= 5; // knock a few pixes off for look..
25352                 this.el.setHeight(this.adjustWidth('textarea', ah));
25353                 this.iframe.style.height = ah + 'px';
25354                 if(this.doc){
25355                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25356                 }
25357             }
25358         }
25359     },
25360
25361     /**
25362      * Toggles the editor between standard and source edit mode.
25363      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25364      */
25365     toggleSourceEdit : function(sourceEditMode){
25366         
25367         this.sourceEditMode = sourceEditMode === true;
25368         
25369         if(this.sourceEditMode){
25370           
25371             this.syncValue();
25372             this.iframe.className = 'x-hidden';
25373             this.el.removeClass('x-hidden');
25374             this.el.dom.removeAttribute('tabIndex');
25375             this.el.focus();
25376         }else{
25377              
25378             this.pushValue();
25379             this.iframe.className = '';
25380             this.el.addClass('x-hidden');
25381             this.el.dom.setAttribute('tabIndex', -1);
25382             this.deferFocus();
25383         }
25384         this.setSize(this.wrap.getSize());
25385         this.fireEvent('editmodechange', this, this.sourceEditMode);
25386     },
25387
25388     // private used internally
25389     createLink : function(){
25390         var url = prompt(this.createLinkText, this.defaultLinkValue);
25391         if(url && url != 'http:/'+'/'){
25392             this.relayCmd('createlink', url);
25393         }
25394     },
25395
25396     // private (for BoxComponent)
25397     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25398
25399     // private (for BoxComponent)
25400     getResizeEl : function(){
25401         return this.wrap;
25402     },
25403
25404     // private (for BoxComponent)
25405     getPositionEl : function(){
25406         return this.wrap;
25407     },
25408
25409     // private
25410     initEvents : function(){
25411         this.originalValue = this.getValue();
25412     },
25413
25414     /**
25415      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25416      * @method
25417      */
25418     markInvalid : Roo.emptyFn,
25419     /**
25420      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25421      * @method
25422      */
25423     clearInvalid : Roo.emptyFn,
25424
25425     setValue : function(v){
25426         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25427         this.pushValue();
25428     },
25429
25430     /**
25431      * Protected method that will not generally be called directly. If you need/want
25432      * custom HTML cleanup, this is the method you should override.
25433      * @param {String} html The HTML to be cleaned
25434      * return {String} The cleaned HTML
25435      */
25436     cleanHtml : function(html){
25437         html = String(html);
25438         if(html.length > 5){
25439             if(Roo.isSafari){ // strip safari nonsense
25440                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25441             }
25442         }
25443         if(html == '&nbsp;'){
25444             html = '';
25445         }
25446         return html;
25447     },
25448
25449     /**
25450      * Protected method that will not generally be called directly. Syncs the contents
25451      * of the editor iframe with the textarea.
25452      */
25453     syncValue : function(){
25454         if(this.initialized){
25455             var bd = (this.doc.body || this.doc.documentElement);
25456             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25457             var html = bd.innerHTML;
25458             if(Roo.isSafari){
25459                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25460                 var m = bs.match(/text-align:(.*?);/i);
25461                 if(m && m[1]){
25462                     html = '<div style="'+m[0]+'">' + html + '</div>';
25463                 }
25464             }
25465             html = this.cleanHtml(html);
25466             // fix up the special chars..
25467             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25468                 return "&#"+b.charCodeAt()+";" 
25469             });
25470             if(this.fireEvent('beforesync', this, html) !== false){
25471                 this.el.dom.value = html;
25472                 this.fireEvent('sync', this, html);
25473             }
25474         }
25475     },
25476
25477     /**
25478      * Protected method that will not generally be called directly. Pushes the value of the textarea
25479      * into the iframe editor.
25480      */
25481     pushValue : function(){
25482         if(this.initialized){
25483             var v = this.el.dom.value;
25484             if(v.length < 1){
25485                 v = '&#160;';
25486             }
25487             
25488             if(this.fireEvent('beforepush', this, v) !== false){
25489                 var d = (this.doc.body || this.doc.documentElement);
25490                 d.innerHTML = v;
25491                 this.cleanUpPaste();
25492                 this.el.dom.value = d.innerHTML;
25493                 this.fireEvent('push', this, v);
25494             }
25495         }
25496     },
25497
25498     // private
25499     deferFocus : function(){
25500         this.focus.defer(10, this);
25501     },
25502
25503     // doc'ed in Field
25504     focus : function(){
25505         if(this.win && !this.sourceEditMode){
25506             this.win.focus();
25507         }else{
25508             this.el.focus();
25509         }
25510     },
25511     
25512     assignDocWin: function()
25513     {
25514         var iframe = this.iframe;
25515         
25516          if(Roo.isIE){
25517             this.doc = iframe.contentWindow.document;
25518             this.win = iframe.contentWindow;
25519         } else {
25520             if (!Roo.get(this.frameId)) {
25521                 return;
25522             }
25523             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25524             this.win = Roo.get(this.frameId).dom.contentWindow;
25525         }
25526     },
25527     
25528     // private
25529     initEditor : function(){
25530         //console.log("INIT EDITOR");
25531         this.assignDocWin();
25532         
25533         
25534         
25535         this.doc.designMode="on";
25536         this.doc.open();
25537         this.doc.write(this.getDocMarkup());
25538         this.doc.close();
25539         
25540         var dbody = (this.doc.body || this.doc.documentElement);
25541         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25542         // this copies styles from the containing element into thsi one..
25543         // not sure why we need all of this..
25544         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25545         ss['background-attachment'] = 'fixed'; // w3c
25546         dbody.bgProperties = 'fixed'; // ie
25547         Roo.DomHelper.applyStyles(dbody, ss);
25548         Roo.EventManager.on(this.doc, {
25549             //'mousedown': this.onEditorEvent,
25550             'mouseup': this.onEditorEvent,
25551             'dblclick': this.onEditorEvent,
25552             'click': this.onEditorEvent,
25553             'keyup': this.onEditorEvent,
25554             buffer:100,
25555             scope: this
25556         });
25557         if(Roo.isGecko){
25558             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25559         }
25560         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25561             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25562         }
25563         this.initialized = true;
25564
25565         this.fireEvent('initialize', this);
25566         this.pushValue();
25567     },
25568
25569     // private
25570     onDestroy : function(){
25571         
25572         
25573         
25574         if(this.rendered){
25575             
25576             for (var i =0; i < this.toolbars.length;i++) {
25577                 // fixme - ask toolbars for heights?
25578                 this.toolbars[i].onDestroy();
25579             }
25580             
25581             this.wrap.dom.innerHTML = '';
25582             this.wrap.remove();
25583         }
25584     },
25585
25586     // private
25587     onFirstFocus : function(){
25588         
25589         this.assignDocWin();
25590         
25591         
25592         this.activated = true;
25593         for (var i =0; i < this.toolbars.length;i++) {
25594             this.toolbars[i].onFirstFocus();
25595         }
25596        
25597         if(Roo.isGecko){ // prevent silly gecko errors
25598             this.win.focus();
25599             var s = this.win.getSelection();
25600             if(!s.focusNode || s.focusNode.nodeType != 3){
25601                 var r = s.getRangeAt(0);
25602                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25603                 r.collapse(true);
25604                 this.deferFocus();
25605             }
25606             try{
25607                 this.execCmd('useCSS', true);
25608                 this.execCmd('styleWithCSS', false);
25609             }catch(e){}
25610         }
25611         this.fireEvent('activate', this);
25612     },
25613
25614     // private
25615     adjustFont: function(btn){
25616         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25617         //if(Roo.isSafari){ // safari
25618         //    adjust *= 2;
25619        // }
25620         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25621         if(Roo.isSafari){ // safari
25622             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25623             v =  (v < 10) ? 10 : v;
25624             v =  (v > 48) ? 48 : v;
25625             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25626             
25627         }
25628         
25629         
25630         v = Math.max(1, v+adjust);
25631         
25632         this.execCmd('FontSize', v  );
25633     },
25634
25635     onEditorEvent : function(e){
25636         this.fireEvent('editorevent', this, e);
25637       //  this.updateToolbar();
25638         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25639     },
25640
25641     insertTag : function(tg)
25642     {
25643         // could be a bit smarter... -> wrap the current selected tRoo..
25644         
25645         this.execCmd("formatblock",   tg);
25646         
25647     },
25648     
25649     insertText : function(txt)
25650     {
25651         
25652         
25653         range = this.createRange();
25654         range.deleteContents();
25655                //alert(Sender.getAttribute('label'));
25656                
25657         range.insertNode(this.doc.createTextNode(txt));
25658     } ,
25659     
25660     // private
25661     relayBtnCmd : function(btn){
25662         this.relayCmd(btn.cmd);
25663     },
25664
25665     /**
25666      * Executes a Midas editor command on the editor document and performs necessary focus and
25667      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25668      * @param {String} cmd The Midas command
25669      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25670      */
25671     relayCmd : function(cmd, value){
25672         this.win.focus();
25673         this.execCmd(cmd, value);
25674         this.fireEvent('editorevent', this);
25675         //this.updateToolbar();
25676         this.deferFocus();
25677     },
25678
25679     /**
25680      * Executes a Midas editor command directly on the editor document.
25681      * For visual commands, you should use {@link #relayCmd} instead.
25682      * <b>This should only be called after the editor is initialized.</b>
25683      * @param {String} cmd The Midas command
25684      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25685      */
25686     execCmd : function(cmd, value){
25687         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25688         this.syncValue();
25689     },
25690  
25691  
25692    
25693     /**
25694      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25695      * to insert tRoo.
25696      * @param {String} text | dom node.. 
25697      */
25698     insertAtCursor : function(text)
25699     {
25700         
25701         
25702         
25703         if(!this.activated){
25704             return;
25705         }
25706         /*
25707         if(Roo.isIE){
25708             this.win.focus();
25709             var r = this.doc.selection.createRange();
25710             if(r){
25711                 r.collapse(true);
25712                 r.pasteHTML(text);
25713                 this.syncValue();
25714                 this.deferFocus();
25715             
25716             }
25717             return;
25718         }
25719         */
25720         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25721             this.win.focus();
25722             
25723             
25724             // from jquery ui (MIT licenced)
25725             var range, node;
25726             var win = this.win;
25727             
25728             if (win.getSelection && win.getSelection().getRangeAt) {
25729                 range = win.getSelection().getRangeAt(0);
25730                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25731                 range.insertNode(node);
25732             } else if (win.document.selection && win.document.selection.createRange) {
25733                 // no firefox support
25734                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25735                 win.document.selection.createRange().pasteHTML(txt);
25736             } else {
25737                 // no firefox support
25738                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25739                 this.execCmd('InsertHTML', txt);
25740             } 
25741             
25742             this.syncValue();
25743             
25744             this.deferFocus();
25745         }
25746     },
25747  // private
25748     mozKeyPress : function(e){
25749         if(e.ctrlKey){
25750             var c = e.getCharCode(), cmd;
25751           
25752             if(c > 0){
25753                 c = String.fromCharCode(c).toLowerCase();
25754                 switch(c){
25755                     case 'b':
25756                         cmd = 'bold';
25757                         break;
25758                     case 'i':
25759                         cmd = 'italic';
25760                         break;
25761                     
25762                     case 'u':
25763                         cmd = 'underline';
25764                         break;
25765                     
25766                     case 'v':
25767                         this.cleanUpPaste.defer(100, this);
25768                         return;
25769                         
25770                 }
25771                 if(cmd){
25772                     this.win.focus();
25773                     this.execCmd(cmd);
25774                     this.deferFocus();
25775                     e.preventDefault();
25776                 }
25777                 
25778             }
25779         }
25780     },
25781
25782     // private
25783     fixKeys : function(){ // load time branching for fastest keydown performance
25784         if(Roo.isIE){
25785             return function(e){
25786                 var k = e.getKey(), r;
25787                 if(k == e.TAB){
25788                     e.stopEvent();
25789                     r = this.doc.selection.createRange();
25790                     if(r){
25791                         r.collapse(true);
25792                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25793                         this.deferFocus();
25794                     }
25795                     return;
25796                 }
25797                 
25798                 if(k == e.ENTER){
25799                     r = this.doc.selection.createRange();
25800                     if(r){
25801                         var target = r.parentElement();
25802                         if(!target || target.tagName.toLowerCase() != 'li'){
25803                             e.stopEvent();
25804                             r.pasteHTML('<br />');
25805                             r.collapse(false);
25806                             r.select();
25807                         }
25808                     }
25809                 }
25810                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25811                     this.cleanUpPaste.defer(100, this);
25812                     return;
25813                 }
25814                 
25815                 
25816             };
25817         }else if(Roo.isOpera){
25818             return function(e){
25819                 var k = e.getKey();
25820                 if(k == e.TAB){
25821                     e.stopEvent();
25822                     this.win.focus();
25823                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25824                     this.deferFocus();
25825                 }
25826                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25827                     this.cleanUpPaste.defer(100, this);
25828                     return;
25829                 }
25830                 
25831             };
25832         }else if(Roo.isSafari){
25833             return function(e){
25834                 var k = e.getKey();
25835                 
25836                 if(k == e.TAB){
25837                     e.stopEvent();
25838                     this.execCmd('InsertText','\t');
25839                     this.deferFocus();
25840                     return;
25841                 }
25842                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25843                     this.cleanUpPaste.defer(100, this);
25844                     return;
25845                 }
25846                 
25847              };
25848         }
25849     }(),
25850     
25851     getAllAncestors: function()
25852     {
25853         var p = this.getSelectedNode();
25854         var a = [];
25855         if (!p) {
25856             a.push(p); // push blank onto stack..
25857             p = this.getParentElement();
25858         }
25859         
25860         
25861         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25862             a.push(p);
25863             p = p.parentNode;
25864         }
25865         a.push(this.doc.body);
25866         return a;
25867     },
25868     lastSel : false,
25869     lastSelNode : false,
25870     
25871     
25872     getSelection : function() 
25873     {
25874         this.assignDocWin();
25875         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25876     },
25877     
25878     getSelectedNode: function() 
25879     {
25880         // this may only work on Gecko!!!
25881         
25882         // should we cache this!!!!
25883         
25884         
25885         
25886          
25887         var range = this.createRange(this.getSelection()).cloneRange();
25888         
25889         if (Roo.isIE) {
25890             var parent = range.parentElement();
25891             while (true) {
25892                 var testRange = range.duplicate();
25893                 testRange.moveToElementText(parent);
25894                 if (testRange.inRange(range)) {
25895                     break;
25896                 }
25897                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25898                     break;
25899                 }
25900                 parent = parent.parentElement;
25901             }
25902             return parent;
25903         }
25904         
25905         // is ancestor a text element.
25906         var ac =  range.commonAncestorContainer;
25907         if (ac.nodeType == 3) {
25908             ac = ac.parentNode;
25909         }
25910         
25911         var ar = ac.childNodes;
25912          
25913         var nodes = [];
25914         var other_nodes = [];
25915         var has_other_nodes = false;
25916         for (var i=0;i<ar.length;i++) {
25917             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25918                 continue;
25919             }
25920             // fullly contained node.
25921             
25922             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25923                 nodes.push(ar[i]);
25924                 continue;
25925             }
25926             
25927             // probably selected..
25928             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25929                 other_nodes.push(ar[i]);
25930                 continue;
25931             }
25932             // outer..
25933             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25934                 continue;
25935             }
25936             
25937             
25938             has_other_nodes = true;
25939         }
25940         if (!nodes.length && other_nodes.length) {
25941             nodes= other_nodes;
25942         }
25943         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25944             return false;
25945         }
25946         
25947         return nodes[0];
25948     },
25949     createRange: function(sel)
25950     {
25951         // this has strange effects when using with 
25952         // top toolbar - not sure if it's a great idea.
25953         //this.editor.contentWindow.focus();
25954         if (typeof sel != "undefined") {
25955             try {
25956                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25957             } catch(e) {
25958                 return this.doc.createRange();
25959             }
25960         } else {
25961             return this.doc.createRange();
25962         }
25963     },
25964     getParentElement: function()
25965     {
25966         
25967         this.assignDocWin();
25968         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25969         
25970         var range = this.createRange(sel);
25971          
25972         try {
25973             var p = range.commonAncestorContainer;
25974             while (p.nodeType == 3) { // text node
25975                 p = p.parentNode;
25976             }
25977             return p;
25978         } catch (e) {
25979             return null;
25980         }
25981     
25982     },
25983     /***
25984      *
25985      * Range intersection.. the hard stuff...
25986      *  '-1' = before
25987      *  '0' = hits..
25988      *  '1' = after.
25989      *         [ -- selected range --- ]
25990      *   [fail]                        [fail]
25991      *
25992      *    basically..
25993      *      if end is before start or  hits it. fail.
25994      *      if start is after end or hits it fail.
25995      *
25996      *   if either hits (but other is outside. - then it's not 
25997      *   
25998      *    
25999      **/
26000     
26001     
26002     // @see http://www.thismuchiknow.co.uk/?p=64.
26003     rangeIntersectsNode : function(range, node)
26004     {
26005         var nodeRange = node.ownerDocument.createRange();
26006         try {
26007             nodeRange.selectNode(node);
26008         } catch (e) {
26009             nodeRange.selectNodeContents(node);
26010         }
26011     
26012         var rangeStartRange = range.cloneRange();
26013         rangeStartRange.collapse(true);
26014     
26015         var rangeEndRange = range.cloneRange();
26016         rangeEndRange.collapse(false);
26017     
26018         var nodeStartRange = nodeRange.cloneRange();
26019         nodeStartRange.collapse(true);
26020     
26021         var nodeEndRange = nodeRange.cloneRange();
26022         nodeEndRange.collapse(false);
26023     
26024         return rangeStartRange.compareBoundaryPoints(
26025                  Range.START_TO_START, nodeEndRange) == -1 &&
26026                rangeEndRange.compareBoundaryPoints(
26027                  Range.START_TO_START, nodeStartRange) == 1;
26028         
26029          
26030     },
26031     rangeCompareNode : function(range, node)
26032     {
26033         var nodeRange = node.ownerDocument.createRange();
26034         try {
26035             nodeRange.selectNode(node);
26036         } catch (e) {
26037             nodeRange.selectNodeContents(node);
26038         }
26039         
26040         
26041         range.collapse(true);
26042     
26043         nodeRange.collapse(true);
26044      
26045         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26046         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26047          
26048         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26049         
26050         var nodeIsBefore   =  ss == 1;
26051         var nodeIsAfter    = ee == -1;
26052         
26053         if (nodeIsBefore && nodeIsAfter)
26054             return 0; // outer
26055         if (!nodeIsBefore && nodeIsAfter)
26056             return 1; //right trailed.
26057         
26058         if (nodeIsBefore && !nodeIsAfter)
26059             return 2;  // left trailed.
26060         // fully contined.
26061         return 3;
26062     },
26063
26064     // private? - in a new class?
26065     cleanUpPaste :  function()
26066     {
26067         // cleans up the whole document..
26068          Roo.log('cleanuppaste');
26069         this.cleanUpChildren(this.doc.body);
26070         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26071         if (clean != this.doc.body.innerHTML) {
26072             this.doc.body.innerHTML = clean;
26073         }
26074         
26075     },
26076     
26077     cleanWordChars : function(input) {
26078         var he = Roo.form.HtmlEditor;
26079     
26080         var output = input;
26081         Roo.each(he.swapCodes, function(sw) { 
26082         
26083             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26084             output = output.replace(swapper, sw[1]);
26085         });
26086         return output;
26087     },
26088     
26089     
26090     cleanUpChildren : function (n)
26091     {
26092         if (!n.childNodes.length) {
26093             return;
26094         }
26095         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26096            this.cleanUpChild(n.childNodes[i]);
26097         }
26098     },
26099     
26100     
26101         
26102     
26103     cleanUpChild : function (node)
26104     {
26105         //console.log(node);
26106         if (node.nodeName == "#text") {
26107             // clean up silly Windows -- stuff?
26108             return; 
26109         }
26110         if (node.nodeName == "#comment") {
26111             node.parentNode.removeChild(node);
26112             // clean up silly Windows -- stuff?
26113             return; 
26114         }
26115         
26116         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26117             // remove node.
26118             node.parentNode.removeChild(node);
26119             return;
26120             
26121         }
26122         
26123         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26124         
26125         // remove <a name=....> as rendering on yahoo mailer is bored with this.
26126         
26127         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26128             remove_keep_children = true;
26129         }
26130         
26131         if (remove_keep_children) {
26132             this.cleanUpChildren(node);
26133             // inserts everything just before this node...
26134             while (node.childNodes.length) {
26135                 var cn = node.childNodes[0];
26136                 node.removeChild(cn);
26137                 node.parentNode.insertBefore(cn, node);
26138             }
26139             node.parentNode.removeChild(node);
26140             return;
26141         }
26142         
26143         if (!node.attributes || !node.attributes.length) {
26144             this.cleanUpChildren(node);
26145             return;
26146         }
26147         
26148         function cleanAttr(n,v)
26149         {
26150             
26151             if (v.match(/^\./) || v.match(/^\//)) {
26152                 return;
26153             }
26154             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26155                 return;
26156             }
26157             if (v.match(/^#/)) {
26158                 return;
26159             }
26160             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26161             node.removeAttribute(n);
26162             
26163         }
26164         
26165         function cleanStyle(n,v)
26166         {
26167             if (v.match(/expression/)) { //XSS?? should we even bother..
26168                 node.removeAttribute(n);
26169                 return;
26170             }
26171             
26172             
26173             var parts = v.split(/;/);
26174             Roo.each(parts, function(p) {
26175                 p = p.replace(/\s+/g,'');
26176                 if (!p.length) {
26177                     return true;
26178                 }
26179                 var l = p.split(':').shift().replace(/\s+/g,'');
26180                 
26181                 // only allow 'c whitelisted system attributes'
26182                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26183                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26184                     node.removeAttribute(n);
26185                     return false;
26186                 }
26187                 return true;
26188             });
26189             
26190             
26191         }
26192         
26193         
26194         for (var i = node.attributes.length-1; i > -1 ; i--) {
26195             var a = node.attributes[i];
26196             //console.log(a);
26197             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26198                 node.removeAttribute(a.name);
26199                 continue;
26200             }
26201             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26202                 cleanAttr(a.name,a.value); // fixme..
26203                 continue;
26204             }
26205             if (a.name == 'style') {
26206                 cleanStyle(a.name,a.value);
26207                 continue;
26208             }
26209             /// clean up MS crap..
26210             // tecnically this should be a list of valid class'es..
26211             
26212             
26213             if (a.name == 'class') {
26214                 if (a.value.match(/^Mso/)) {
26215                     node.className = '';
26216                 }
26217                 
26218                 if (a.value.match(/body/)) {
26219                     node.className = '';
26220                 }
26221                 continue;
26222             }
26223             
26224             // style cleanup!?
26225             // class cleanup?
26226             
26227         }
26228         
26229         
26230         this.cleanUpChildren(node);
26231         
26232         
26233     }
26234     
26235     
26236     // hide stuff that is not compatible
26237     /**
26238      * @event blur
26239      * @hide
26240      */
26241     /**
26242      * @event change
26243      * @hide
26244      */
26245     /**
26246      * @event focus
26247      * @hide
26248      */
26249     /**
26250      * @event specialkey
26251      * @hide
26252      */
26253     /**
26254      * @cfg {String} fieldClass @hide
26255      */
26256     /**
26257      * @cfg {String} focusClass @hide
26258      */
26259     /**
26260      * @cfg {String} autoCreate @hide
26261      */
26262     /**
26263      * @cfg {String} inputType @hide
26264      */
26265     /**
26266      * @cfg {String} invalidClass @hide
26267      */
26268     /**
26269      * @cfg {String} invalidText @hide
26270      */
26271     /**
26272      * @cfg {String} msgFx @hide
26273      */
26274     /**
26275      * @cfg {String} validateOnBlur @hide
26276      */
26277 });
26278
26279 Roo.form.HtmlEditor.white = [
26280         'area', 'br', 'img', 'input', 'hr', 'wbr',
26281         
26282        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26283        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26284        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26285        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26286        'table',   'ul',         'xmp', 
26287        
26288        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26289       'thead',   'tr', 
26290      
26291       'dir', 'menu', 'ol', 'ul', 'dl',
26292        
26293       'embed',  'object'
26294 ];
26295
26296
26297 Roo.form.HtmlEditor.black = [
26298     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26299         'applet', // 
26300         'base',   'basefont', 'bgsound', 'blink',  'body', 
26301         'frame',  'frameset', 'head',    'html',   'ilayer', 
26302         'iframe', 'layer',  'link',     'meta',    'object',   
26303         'script', 'style' ,'title',  'xml' // clean later..
26304 ];
26305 Roo.form.HtmlEditor.clean = [
26306     'script', 'style', 'title', 'xml'
26307 ];
26308 Roo.form.HtmlEditor.remove = [
26309     'font'
26310 ];
26311 // attributes..
26312
26313 Roo.form.HtmlEditor.ablack = [
26314     'on'
26315 ];
26316     
26317 Roo.form.HtmlEditor.aclean = [ 
26318     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26319 ];
26320
26321 // protocols..
26322 Roo.form.HtmlEditor.pwhite= [
26323         'http',  'https',  'mailto'
26324 ];
26325
26326 // white listed style attributes.
26327 Roo.form.HtmlEditor.cwhite= [
26328         'text-align',
26329         'font-size'
26330 ];
26331
26332
26333 Roo.form.HtmlEditor.swapCodes   =[ 
26334     [    8211, "--" ], 
26335     [    8212, "--" ], 
26336     [    8216,  "'" ],  
26337     [    8217, "'" ],  
26338     [    8220, '"' ],  
26339     [    8221, '"' ],  
26340     [    8226, "*" ],  
26341     [    8230, "..." ]
26342 ]; 
26343
26344     // <script type="text/javascript">
26345 /*
26346  * Based on
26347  * Ext JS Library 1.1.1
26348  * Copyright(c) 2006-2007, Ext JS, LLC.
26349  *  
26350  
26351  */
26352
26353 /**
26354  * @class Roo.form.HtmlEditorToolbar1
26355  * Basic Toolbar
26356  * 
26357  * Usage:
26358  *
26359  new Roo.form.HtmlEditor({
26360     ....
26361     toolbars : [
26362         new Roo.form.HtmlEditorToolbar1({
26363             disable : { fonts: 1 , format: 1, ..., ... , ...],
26364             btns : [ .... ]
26365         })
26366     }
26367      
26368  * 
26369  * @cfg {Object} disable List of elements to disable..
26370  * @cfg {Array} btns List of additional buttons.
26371  * 
26372  * 
26373  * NEEDS Extra CSS? 
26374  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26375  */
26376  
26377 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26378 {
26379     
26380     Roo.apply(this, config);
26381     
26382     // default disabled, based on 'good practice'..
26383     this.disable = this.disable || {};
26384     Roo.applyIf(this.disable, {
26385         fontSize : true,
26386         colors : true,
26387         specialElements : true
26388     });
26389     
26390     
26391     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26392     // dont call parent... till later.
26393 }
26394
26395 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26396     
26397     tb: false,
26398     
26399     rendered: false,
26400     
26401     editor : false,
26402     /**
26403      * @cfg {Object} disable  List of toolbar elements to disable
26404          
26405      */
26406     disable : false,
26407       /**
26408      * @cfg {Array} fontFamilies An array of available font families
26409      */
26410     fontFamilies : [
26411         'Arial',
26412         'Courier New',
26413         'Tahoma',
26414         'Times New Roman',
26415         'Verdana'
26416     ],
26417     
26418     specialChars : [
26419            "&#169;",
26420           "&#174;",     
26421           "&#8482;",    
26422           "&#163;" ,    
26423          // "&#8212;",    
26424           "&#8230;",    
26425           "&#247;" ,    
26426         //  "&#225;" ,     ?? a acute?
26427            "&#8364;"    , //Euro
26428        //   "&#8220;"    ,
26429         //  "&#8221;"    ,
26430         //  "&#8226;"    ,
26431           "&#176;"  //   , // degrees
26432
26433          // "&#233;"     , // e ecute
26434          // "&#250;"     , // u ecute?
26435     ],
26436     
26437     specialElements : [
26438         {
26439             text: "Insert Table",
26440             xtype: 'MenuItem',
26441             xns : Roo.Menu,
26442             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26443                 
26444         },
26445         {    
26446             text: "Insert Image",
26447             xtype: 'MenuItem',
26448             xns : Roo.Menu,
26449             ihtml : '<img src="about:blank"/>'
26450             
26451         }
26452         
26453          
26454     ],
26455     
26456     
26457     inputElements : [ 
26458             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26459             "input:submit", "input:button", "select", "textarea", "label" ],
26460     formats : [
26461         ["p"] ,  
26462         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26463         ["pre"],[ "code"], 
26464         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26465     ],
26466      /**
26467      * @cfg {String} defaultFont default font to use.
26468      */
26469     defaultFont: 'tahoma',
26470    
26471     fontSelect : false,
26472     
26473     
26474     formatCombo : false,
26475     
26476     init : function(editor)
26477     {
26478         this.editor = editor;
26479         
26480         
26481         var fid = editor.frameId;
26482         var etb = this;
26483         function btn(id, toggle, handler){
26484             var xid = fid + '-'+ id ;
26485             return {
26486                 id : xid,
26487                 cmd : id,
26488                 cls : 'x-btn-icon x-edit-'+id,
26489                 enableToggle:toggle !== false,
26490                 scope: editor, // was editor...
26491                 handler:handler||editor.relayBtnCmd,
26492                 clickEvent:'mousedown',
26493                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26494                 tabIndex:-1
26495             };
26496         }
26497         
26498         
26499         
26500         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26501         this.tb = tb;
26502          // stop form submits
26503         tb.el.on('click', function(e){
26504             e.preventDefault(); // what does this do?
26505         });
26506
26507         if(!this.disable.font && !Roo.isSafari){
26508             /* why no safari for fonts
26509             editor.fontSelect = tb.el.createChild({
26510                 tag:'select',
26511                 tabIndex: -1,
26512                 cls:'x-font-select',
26513                 html: editor.createFontOptions()
26514             });
26515             editor.fontSelect.on('change', function(){
26516                 var font = editor.fontSelect.dom.value;
26517                 editor.relayCmd('fontname', font);
26518                 editor.deferFocus();
26519             }, editor);
26520             tb.add(
26521                 editor.fontSelect.dom,
26522                 '-'
26523             );
26524             */
26525         };
26526         if(!this.disable.formats){
26527             this.formatCombo = new Roo.form.ComboBox({
26528                 store: new Roo.data.SimpleStore({
26529                     id : 'tag',
26530                     fields: ['tag'],
26531                     data : this.formats // from states.js
26532                 }),
26533                 blockFocus : true,
26534                 //autoCreate : {tag: "div",  size: "20"},
26535                 displayField:'tag',
26536                 typeAhead: false,
26537                 mode: 'local',
26538                 editable : false,
26539                 triggerAction: 'all',
26540                 emptyText:'Add tag',
26541                 selectOnFocus:true,
26542                 width:135,
26543                 listeners : {
26544                     'select': function(c, r, i) {
26545                         editor.insertTag(r.get('tag'));
26546                         editor.focus();
26547                     }
26548                 }
26549
26550             });
26551             tb.addField(this.formatCombo);
26552             
26553         }
26554         
26555         if(!this.disable.format){
26556             tb.add(
26557                 btn('bold'),
26558                 btn('italic'),
26559                 btn('underline')
26560             );
26561         };
26562         if(!this.disable.fontSize){
26563             tb.add(
26564                 '-',
26565                 
26566                 
26567                 btn('increasefontsize', false, editor.adjustFont),
26568                 btn('decreasefontsize', false, editor.adjustFont)
26569             );
26570         };
26571         
26572         
26573         if(!this.disable.colors){
26574             tb.add(
26575                 '-', {
26576                     id:editor.frameId +'-forecolor',
26577                     cls:'x-btn-icon x-edit-forecolor',
26578                     clickEvent:'mousedown',
26579                     tooltip: this.buttonTips['forecolor'] || undefined,
26580                     tabIndex:-1,
26581                     menu : new Roo.menu.ColorMenu({
26582                         allowReselect: true,
26583                         focus: Roo.emptyFn,
26584                         value:'000000',
26585                         plain:true,
26586                         selectHandler: function(cp, color){
26587                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26588                             editor.deferFocus();
26589                         },
26590                         scope: editor,
26591                         clickEvent:'mousedown'
26592                     })
26593                 }, {
26594                     id:editor.frameId +'backcolor',
26595                     cls:'x-btn-icon x-edit-backcolor',
26596                     clickEvent:'mousedown',
26597                     tooltip: this.buttonTips['backcolor'] || undefined,
26598                     tabIndex:-1,
26599                     menu : new Roo.menu.ColorMenu({
26600                         focus: Roo.emptyFn,
26601                         value:'FFFFFF',
26602                         plain:true,
26603                         allowReselect: true,
26604                         selectHandler: function(cp, color){
26605                             if(Roo.isGecko){
26606                                 editor.execCmd('useCSS', false);
26607                                 editor.execCmd('hilitecolor', color);
26608                                 editor.execCmd('useCSS', true);
26609                                 editor.deferFocus();
26610                             }else{
26611                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26612                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26613                                 editor.deferFocus();
26614                             }
26615                         },
26616                         scope:editor,
26617                         clickEvent:'mousedown'
26618                     })
26619                 }
26620             );
26621         };
26622         // now add all the items...
26623         
26624
26625         if(!this.disable.alignments){
26626             tb.add(
26627                 '-',
26628                 btn('justifyleft'),
26629                 btn('justifycenter'),
26630                 btn('justifyright')
26631             );
26632         };
26633
26634         //if(!Roo.isSafari){
26635             if(!this.disable.links){
26636                 tb.add(
26637                     '-',
26638                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26639                 );
26640             };
26641
26642             if(!this.disable.lists){
26643                 tb.add(
26644                     '-',
26645                     btn('insertorderedlist'),
26646                     btn('insertunorderedlist')
26647                 );
26648             }
26649             if(!this.disable.sourceEdit){
26650                 tb.add(
26651                     '-',
26652                     btn('sourceedit', true, function(btn){
26653                         this.toggleSourceEdit(btn.pressed);
26654                     })
26655                 );
26656             }
26657         //}
26658         
26659         var smenu = { };
26660         // special menu.. - needs to be tidied up..
26661         if (!this.disable.special) {
26662             smenu = {
26663                 text: "&#169;",
26664                 cls: 'x-edit-none',
26665                 
26666                 menu : {
26667                     items : []
26668                 }
26669             };
26670             for (var i =0; i < this.specialChars.length; i++) {
26671                 smenu.menu.items.push({
26672                     
26673                     html: this.specialChars[i],
26674                     handler: function(a,b) {
26675                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26676                         //editor.insertAtCursor(a.html);
26677                         
26678                     },
26679                     tabIndex:-1
26680                 });
26681             }
26682             
26683             
26684             tb.add(smenu);
26685             
26686             
26687         }
26688          
26689         if (!this.disable.specialElements) {
26690             var semenu = {
26691                 text: "Other;",
26692                 cls: 'x-edit-none',
26693                 menu : {
26694                     items : []
26695                 }
26696             };
26697             for (var i =0; i < this.specialElements.length; i++) {
26698                 semenu.menu.items.push(
26699                     Roo.apply({ 
26700                         handler: function(a,b) {
26701                             editor.insertAtCursor(this.ihtml);
26702                         }
26703                     }, this.specialElements[i])
26704                 );
26705                     
26706             }
26707             
26708             tb.add(semenu);
26709             
26710             
26711         }
26712          
26713         
26714         if (this.btns) {
26715             for(var i =0; i< this.btns.length;i++) {
26716                 var b = Roo.factory(this.btns[i],Roo.form);
26717                 b.cls =  'x-edit-none';
26718                 b.scope = editor;
26719                 tb.add(b);
26720             }
26721         
26722         }
26723         
26724         
26725         
26726         // disable everything...
26727         
26728         this.tb.items.each(function(item){
26729            if(item.id != editor.frameId+ '-sourceedit'){
26730                 item.disable();
26731             }
26732         });
26733         this.rendered = true;
26734         
26735         // the all the btns;
26736         editor.on('editorevent', this.updateToolbar, this);
26737         // other toolbars need to implement this..
26738         //editor.on('editmodechange', this.updateToolbar, this);
26739     },
26740     
26741     
26742     
26743     /**
26744      * Protected method that will not generally be called directly. It triggers
26745      * a toolbar update by reading the markup state of the current selection in the editor.
26746      */
26747     updateToolbar: function(){
26748
26749         if(!this.editor.activated){
26750             this.editor.onFirstFocus();
26751             return;
26752         }
26753
26754         var btns = this.tb.items.map, 
26755             doc = this.editor.doc,
26756             frameId = this.editor.frameId;
26757
26758         if(!this.disable.font && !Roo.isSafari){
26759             /*
26760             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26761             if(name != this.fontSelect.dom.value){
26762                 this.fontSelect.dom.value = name;
26763             }
26764             */
26765         }
26766         if(!this.disable.format){
26767             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26768             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26769             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26770         }
26771         if(!this.disable.alignments){
26772             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26773             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26774             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26775         }
26776         if(!Roo.isSafari && !this.disable.lists){
26777             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26778             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26779         }
26780         
26781         var ans = this.editor.getAllAncestors();
26782         if (this.formatCombo) {
26783             
26784             
26785             var store = this.formatCombo.store;
26786             this.formatCombo.setValue("");
26787             for (var i =0; i < ans.length;i++) {
26788                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26789                     // select it..
26790                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26791                     break;
26792                 }
26793             }
26794         }
26795         
26796         
26797         
26798         // hides menus... - so this cant be on a menu...
26799         Roo.menu.MenuMgr.hideAll();
26800
26801         //this.editorsyncValue();
26802     },
26803    
26804     
26805     createFontOptions : function(){
26806         var buf = [], fs = this.fontFamilies, ff, lc;
26807         for(var i = 0, len = fs.length; i< len; i++){
26808             ff = fs[i];
26809             lc = ff.toLowerCase();
26810             buf.push(
26811                 '<option value="',lc,'" style="font-family:',ff,';"',
26812                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26813                     ff,
26814                 '</option>'
26815             );
26816         }
26817         return buf.join('');
26818     },
26819     
26820     toggleSourceEdit : function(sourceEditMode){
26821         if(sourceEditMode === undefined){
26822             sourceEditMode = !this.sourceEditMode;
26823         }
26824         this.sourceEditMode = sourceEditMode === true;
26825         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26826         // just toggle the button?
26827         if(btn.pressed !== this.editor.sourceEditMode){
26828             btn.toggle(this.editor.sourceEditMode);
26829             return;
26830         }
26831         
26832         if(this.sourceEditMode){
26833             this.tb.items.each(function(item){
26834                 if(item.cmd != 'sourceedit'){
26835                     item.disable();
26836                 }
26837             });
26838           
26839         }else{
26840             if(this.initialized){
26841                 this.tb.items.each(function(item){
26842                     item.enable();
26843                 });
26844             }
26845             
26846         }
26847         // tell the editor that it's been pressed..
26848         this.editor.toggleSourceEdit(sourceEditMode);
26849        
26850     },
26851      /**
26852      * Object collection of toolbar tooltips for the buttons in the editor. The key
26853      * is the command id associated with that button and the value is a valid QuickTips object.
26854      * For example:
26855 <pre><code>
26856 {
26857     bold : {
26858         title: 'Bold (Ctrl+B)',
26859         text: 'Make the selected text bold.',
26860         cls: 'x-html-editor-tip'
26861     },
26862     italic : {
26863         title: 'Italic (Ctrl+I)',
26864         text: 'Make the selected text italic.',
26865         cls: 'x-html-editor-tip'
26866     },
26867     ...
26868 </code></pre>
26869     * @type Object
26870      */
26871     buttonTips : {
26872         bold : {
26873             title: 'Bold (Ctrl+B)',
26874             text: 'Make the selected text bold.',
26875             cls: 'x-html-editor-tip'
26876         },
26877         italic : {
26878             title: 'Italic (Ctrl+I)',
26879             text: 'Make the selected text italic.',
26880             cls: 'x-html-editor-tip'
26881         },
26882         underline : {
26883             title: 'Underline (Ctrl+U)',
26884             text: 'Underline the selected text.',
26885             cls: 'x-html-editor-tip'
26886         },
26887         increasefontsize : {
26888             title: 'Grow Text',
26889             text: 'Increase the font size.',
26890             cls: 'x-html-editor-tip'
26891         },
26892         decreasefontsize : {
26893             title: 'Shrink Text',
26894             text: 'Decrease the font size.',
26895             cls: 'x-html-editor-tip'
26896         },
26897         backcolor : {
26898             title: 'Text Highlight Color',
26899             text: 'Change the background color of the selected text.',
26900             cls: 'x-html-editor-tip'
26901         },
26902         forecolor : {
26903             title: 'Font Color',
26904             text: 'Change the color of the selected text.',
26905             cls: 'x-html-editor-tip'
26906         },
26907         justifyleft : {
26908             title: 'Align Text Left',
26909             text: 'Align text to the left.',
26910             cls: 'x-html-editor-tip'
26911         },
26912         justifycenter : {
26913             title: 'Center Text',
26914             text: 'Center text in the editor.',
26915             cls: 'x-html-editor-tip'
26916         },
26917         justifyright : {
26918             title: 'Align Text Right',
26919             text: 'Align text to the right.',
26920             cls: 'x-html-editor-tip'
26921         },
26922         insertunorderedlist : {
26923             title: 'Bullet List',
26924             text: 'Start a bulleted list.',
26925             cls: 'x-html-editor-tip'
26926         },
26927         insertorderedlist : {
26928             title: 'Numbered List',
26929             text: 'Start a numbered list.',
26930             cls: 'x-html-editor-tip'
26931         },
26932         createlink : {
26933             title: 'Hyperlink',
26934             text: 'Make the selected text a hyperlink.',
26935             cls: 'x-html-editor-tip'
26936         },
26937         sourceedit : {
26938             title: 'Source Edit',
26939             text: 'Switch to source editing mode.',
26940             cls: 'x-html-editor-tip'
26941         }
26942     },
26943     // private
26944     onDestroy : function(){
26945         if(this.rendered){
26946             
26947             this.tb.items.each(function(item){
26948                 if(item.menu){
26949                     item.menu.removeAll();
26950                     if(item.menu.el){
26951                         item.menu.el.destroy();
26952                     }
26953                 }
26954                 item.destroy();
26955             });
26956              
26957         }
26958     },
26959     onFirstFocus: function() {
26960         this.tb.items.each(function(item){
26961            item.enable();
26962         });
26963     }
26964 });
26965
26966
26967
26968
26969 // <script type="text/javascript">
26970 /*
26971  * Based on
26972  * Ext JS Library 1.1.1
26973  * Copyright(c) 2006-2007, Ext JS, LLC.
26974  *  
26975  
26976  */
26977
26978  
26979 /**
26980  * @class Roo.form.HtmlEditor.ToolbarContext
26981  * Context Toolbar
26982  * 
26983  * Usage:
26984  *
26985  new Roo.form.HtmlEditor({
26986     ....
26987     toolbars : [
26988         { xtype: 'ToolbarStandard', styles : {} }
26989         { xtype: 'ToolbarContext', disable : {} }
26990     ]
26991 })
26992
26993      
26994  * 
26995  * @config : {Object} disable List of elements to disable.. (not done yet.)
26996  * @config : {Object} styles  Map of styles available.
26997  * 
26998  */
26999
27000 Roo.form.HtmlEditor.ToolbarContext = function(config)
27001 {
27002     
27003     Roo.apply(this, config);
27004     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27005     // dont call parent... till later.
27006     this.styles = this.styles || {};
27007 }
27008 Roo.form.HtmlEditor.ToolbarContext.types = {
27009     'IMG' : {
27010         width : {
27011             title: "Width",
27012             width: 40
27013         },
27014         height:  {
27015             title: "Height",
27016             width: 40
27017         },
27018         align: {
27019             title: "Align",
27020             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
27021             width : 80
27022             
27023         },
27024         border: {
27025             title: "Border",
27026             width: 40
27027         },
27028         alt: {
27029             title: "Alt",
27030             width: 120
27031         },
27032         src : {
27033             title: "Src",
27034             width: 220
27035         }
27036         
27037     },
27038     'A' : {
27039         name : {
27040             title: "Name",
27041             width: 50
27042         },
27043         href:  {
27044             title: "Href",
27045             width: 220
27046         } // border?
27047         
27048     },
27049     'TABLE' : {
27050         rows : {
27051             title: "Rows",
27052             width: 20
27053         },
27054         cols : {
27055             title: "Cols",
27056             width: 20
27057         },
27058         width : {
27059             title: "Width",
27060             width: 40
27061         },
27062         height : {
27063             title: "Height",
27064             width: 40
27065         },
27066         border : {
27067             title: "Border",
27068             width: 20
27069         }
27070     },
27071     'TD' : {
27072         width : {
27073             title: "Width",
27074             width: 40
27075         },
27076         height : {
27077             title: "Height",
27078             width: 40
27079         },   
27080         align: {
27081             title: "Align",
27082             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27083             width: 80
27084         },
27085         valign: {
27086             title: "Valign",
27087             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27088             width: 80
27089         },
27090         colspan: {
27091             title: "Colspan",
27092             width: 20
27093             
27094         }
27095     },
27096     'INPUT' : {
27097         name : {
27098             title: "name",
27099             width: 120
27100         },
27101         value : {
27102             title: "Value",
27103             width: 120
27104         },
27105         width : {
27106             title: "Width",
27107             width: 40
27108         }
27109     },
27110     'LABEL' : {
27111         'for' : {
27112             title: "For",
27113             width: 120
27114         }
27115     },
27116     'TEXTAREA' : {
27117           name : {
27118             title: "name",
27119             width: 120
27120         },
27121         rows : {
27122             title: "Rows",
27123             width: 20
27124         },
27125         cols : {
27126             title: "Cols",
27127             width: 20
27128         }
27129     },
27130     'SELECT' : {
27131         name : {
27132             title: "name",
27133             width: 120
27134         },
27135         selectoptions : {
27136             title: "Options",
27137             width: 200
27138         }
27139     },
27140     
27141     // should we really allow this??
27142     // should this just be 
27143     'BODY' : {
27144         title : {
27145             title: "title",
27146             width: 200,
27147             disabled : true
27148         }
27149     },
27150     '*' : {
27151         // empty..
27152     }
27153 };
27154
27155
27156
27157 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27158     
27159     tb: false,
27160     
27161     rendered: false,
27162     
27163     editor : false,
27164     /**
27165      * @cfg {Object} disable  List of toolbar elements to disable
27166          
27167      */
27168     disable : false,
27169     /**
27170      * @cfg {Object} styles List of styles 
27171      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27172      *
27173      * These must be defined in the page, so they get rendered correctly..
27174      * .headline { }
27175      * TD.underline { }
27176      * 
27177      */
27178     styles : false,
27179     
27180     
27181     
27182     toolbars : false,
27183     
27184     init : function(editor)
27185     {
27186         this.editor = editor;
27187         
27188         
27189         var fid = editor.frameId;
27190         var etb = this;
27191         function btn(id, toggle, handler){
27192             var xid = fid + '-'+ id ;
27193             return {
27194                 id : xid,
27195                 cmd : id,
27196                 cls : 'x-btn-icon x-edit-'+id,
27197                 enableToggle:toggle !== false,
27198                 scope: editor, // was editor...
27199                 handler:handler||editor.relayBtnCmd,
27200                 clickEvent:'mousedown',
27201                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27202                 tabIndex:-1
27203             };
27204         }
27205         // create a new element.
27206         var wdiv = editor.wrap.createChild({
27207                 tag: 'div'
27208             }, editor.wrap.dom.firstChild.nextSibling, true);
27209         
27210         // can we do this more than once??
27211         
27212          // stop form submits
27213       
27214  
27215         // disable everything...
27216         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27217         this.toolbars = {};
27218            
27219         for (var i in  ty) {
27220           
27221             this.toolbars[i] = this.buildToolbar(ty[i],i);
27222         }
27223         this.tb = this.toolbars.BODY;
27224         this.tb.el.show();
27225         this.buildFooter();
27226         this.footer.show();
27227         editor.on('hide', function( ) { this.footer.hide() }, this);
27228         editor.on('show', function( ) { this.footer.show() }, this);
27229         
27230          
27231         this.rendered = true;
27232         
27233         // the all the btns;
27234         editor.on('editorevent', this.updateToolbar, this);
27235         // other toolbars need to implement this..
27236         //editor.on('editmodechange', this.updateToolbar, this);
27237     },
27238     
27239     
27240     
27241     /**
27242      * Protected method that will not generally be called directly. It triggers
27243      * a toolbar update by reading the markup state of the current selection in the editor.
27244      */
27245     updateToolbar: function(editor,ev,sel){
27246
27247         //Roo.log(ev);
27248         // capture mouse up - this is handy for selecting images..
27249         // perhaps should go somewhere else...
27250         if(!this.editor.activated){
27251              this.editor.onFirstFocus();
27252             return;
27253         }
27254         
27255         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27256         // selectNode - might want to handle IE?
27257         if (ev &&
27258             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27259             ev.target && ev.target.tagName == 'IMG') {
27260             // they have click on an image...
27261             // let's see if we can change the selection...
27262             sel = ev.target;
27263          
27264               var nodeRange = sel.ownerDocument.createRange();
27265             try {
27266                 nodeRange.selectNode(sel);
27267             } catch (e) {
27268                 nodeRange.selectNodeContents(sel);
27269             }
27270             //nodeRange.collapse(true);
27271             var s = editor.win.getSelection();
27272             s.removeAllRanges();
27273             s.addRange(nodeRange);
27274         }  
27275         
27276       
27277         var updateFooter = sel ? false : true;
27278         
27279         
27280         var ans = this.editor.getAllAncestors();
27281         
27282         // pick
27283         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27284         
27285         if (!sel) { 
27286             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27287             sel = sel ? sel : this.editor.doc.body;
27288             sel = sel.tagName.length ? sel : this.editor.doc.body;
27289             
27290         }
27291         // pick a menu that exists..
27292         var tn = sel.tagName.toUpperCase();
27293         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27294         
27295         tn = sel.tagName.toUpperCase();
27296         
27297         var lastSel = this.tb.selectedNode
27298         
27299         this.tb.selectedNode = sel;
27300         
27301         // if current menu does not match..
27302         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27303                 
27304             this.tb.el.hide();
27305             ///console.log("show: " + tn);
27306             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27307             this.tb.el.show();
27308             // update name
27309             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27310             
27311             
27312             // update attributes
27313             if (this.tb.fields) {
27314                 this.tb.fields.each(function(e) {
27315                    e.setValue(sel.getAttribute(e.attrname));
27316                 });
27317             }
27318             
27319             var hasStyles = false;
27320             for(var i in this.styles) {
27321                 hasStyles = true;
27322                 break;
27323             }
27324             
27325             // update styles
27326             if (hasStyles) { 
27327                 var st = this.tb.fields.item(0);
27328                 
27329                 st.store.removeAll();
27330                
27331                 
27332                 var cn = sel.className.split(/\s+/);
27333                 
27334                 var avs = [];
27335                 if (this.styles['*']) {
27336                     
27337                     Roo.each(this.styles['*'], function(v) {
27338                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27339                     });
27340                 }
27341                 if (this.styles[tn]) { 
27342                     Roo.each(this.styles[tn], function(v) {
27343                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27344                     });
27345                 }
27346                 
27347                 st.store.loadData(avs);
27348                 st.collapse();
27349                 st.setValue(cn);
27350             }
27351             // flag our selected Node.
27352             this.tb.selectedNode = sel;
27353            
27354            
27355             Roo.menu.MenuMgr.hideAll();
27356
27357         }
27358         
27359         if (!updateFooter) {
27360             return;
27361         }
27362         // update the footer
27363         //
27364         var html = '';
27365         
27366         this.footerEls = ans.reverse();
27367         Roo.each(this.footerEls, function(a,i) {
27368             if (!a) { return; }
27369             html += html.length ? ' &gt; '  :  '';
27370             
27371             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27372             
27373         });
27374        
27375         // 
27376         var sz = this.footDisp.up('td').getSize();
27377         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27378         this.footDisp.dom.style.marginLeft = '5px';
27379         
27380         this.footDisp.dom.style.overflow = 'hidden';
27381         
27382         this.footDisp.dom.innerHTML = html;
27383             
27384         //this.editorsyncValue();
27385     },
27386    
27387        
27388     // private
27389     onDestroy : function(){
27390         if(this.rendered){
27391             
27392             this.tb.items.each(function(item){
27393                 if(item.menu){
27394                     item.menu.removeAll();
27395                     if(item.menu.el){
27396                         item.menu.el.destroy();
27397                     }
27398                 }
27399                 item.destroy();
27400             });
27401              
27402         }
27403     },
27404     onFirstFocus: function() {
27405         // need to do this for all the toolbars..
27406         this.tb.items.each(function(item){
27407            item.enable();
27408         });
27409     },
27410     buildToolbar: function(tlist, nm)
27411     {
27412         var editor = this.editor;
27413          // create a new element.
27414         var wdiv = editor.wrap.createChild({
27415                 tag: 'div'
27416             }, editor.wrap.dom.firstChild.nextSibling, true);
27417         
27418        
27419         var tb = new Roo.Toolbar(wdiv);
27420         // add the name..
27421         
27422         tb.add(nm+ ":&nbsp;");
27423         
27424         var styles = [];
27425         for(var i in this.styles) {
27426             styles.push(i);
27427         }
27428         
27429         // styles...
27430         if (styles && styles.length) {
27431             
27432             // this needs a multi-select checkbox...
27433             tb.addField( new Roo.form.ComboBox({
27434                 store: new Roo.data.SimpleStore({
27435                     id : 'val',
27436                     fields: ['val', 'selected'],
27437                     data : [] 
27438                 }),
27439                 name : '-roo-edit-className',
27440                 attrname : 'className',
27441                 displayField:'val',
27442                 typeAhead: false,
27443                 mode: 'local',
27444                 editable : false,
27445                 triggerAction: 'all',
27446                 emptyText:'Select Style',
27447                 selectOnFocus:true,
27448                 width: 130,
27449                 listeners : {
27450                     'select': function(c, r, i) {
27451                         // initial support only for on class per el..
27452                         tb.selectedNode.className =  r ? r.get('val') : '';
27453                         editor.syncValue();
27454                     }
27455                 }
27456     
27457             }));
27458         }
27459             
27460         
27461         
27462         for (var i in tlist) {
27463             
27464             var item = tlist[i];
27465             tb.add(item.title + ":&nbsp;");
27466             
27467             
27468             
27469             
27470             if (item.opts) {
27471                 // opts == pulldown..
27472                 tb.addField( new Roo.form.ComboBox({
27473                     store: new Roo.data.SimpleStore({
27474                         id : 'val',
27475                         fields: ['val'],
27476                         data : item.opts  
27477                     }),
27478                     name : '-roo-edit-' + i,
27479                     attrname : i,
27480                     displayField:'val',
27481                     typeAhead: false,
27482                     mode: 'local',
27483                     editable : false,
27484                     triggerAction: 'all',
27485                     emptyText:'Select',
27486                     selectOnFocus:true,
27487                     width: item.width ? item.width  : 130,
27488                     listeners : {
27489                         'select': function(c, r, i) {
27490                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27491                         }
27492                     }
27493
27494                 }));
27495                 continue;
27496                     
27497                  
27498                 
27499                 tb.addField( new Roo.form.TextField({
27500                     name: i,
27501                     width: 100,
27502                     //allowBlank:false,
27503                     value: ''
27504                 }));
27505                 continue;
27506             }
27507             tb.addField( new Roo.form.TextField({
27508                 name: '-roo-edit-' + i,
27509                 attrname : i,
27510                 
27511                 width: item.width,
27512                 //allowBlank:true,
27513                 value: '',
27514                 listeners: {
27515                     'change' : function(f, nv, ov) {
27516                         tb.selectedNode.setAttribute(f.attrname, nv);
27517                     }
27518                 }
27519             }));
27520              
27521         }
27522         tb.el.on('click', function(e){
27523             e.preventDefault(); // what does this do?
27524         });
27525         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27526         tb.el.hide();
27527         tb.name = nm;
27528         // dont need to disable them... as they will get hidden
27529         return tb;
27530          
27531         
27532     },
27533     buildFooter : function()
27534     {
27535         
27536         var fel = this.editor.wrap.createChild();
27537         this.footer = new Roo.Toolbar(fel);
27538         // toolbar has scrolly on left / right?
27539         var footDisp= new Roo.Toolbar.Fill();
27540         var _t = this;
27541         this.footer.add(
27542             {
27543                 text : '&lt;',
27544                 xtype: 'Button',
27545                 handler : function() {
27546                     _t.footDisp.scrollTo('left',0,true)
27547                 }
27548             }
27549         );
27550         this.footer.add( footDisp );
27551         this.footer.add( 
27552             {
27553                 text : '&gt;',
27554                 xtype: 'Button',
27555                 handler : function() {
27556                     // no animation..
27557                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27558                 }
27559             }
27560         );
27561         var fel = Roo.get(footDisp.el);
27562         fel.addClass('x-editor-context');
27563         this.footDispWrap = fel; 
27564         this.footDispWrap.overflow  = 'hidden';
27565         
27566         this.footDisp = fel.createChild();
27567         this.footDispWrap.on('click', this.onContextClick, this)
27568         
27569         
27570     },
27571     onContextClick : function (ev,dom)
27572     {
27573         ev.preventDefault();
27574         var  cn = dom.className;
27575         Roo.log(cn);
27576         if (!cn.match(/x-ed-loc-/)) {
27577             return;
27578         }
27579         var n = cn.split('-').pop();
27580         var ans = this.footerEls;
27581         var sel = ans[n];
27582         
27583          // pick
27584         var range = this.editor.createRange();
27585         
27586         range.selectNodeContents(sel);
27587         //range.selectNode(sel);
27588         
27589         
27590         var selection = this.editor.getSelection();
27591         selection.removeAllRanges();
27592         selection.addRange(range);
27593         
27594         
27595         
27596         this.updateToolbar(null, null, sel);
27597         
27598         
27599     }
27600     
27601     
27602     
27603     
27604     
27605 });
27606
27607
27608
27609
27610
27611 /*
27612  * Based on:
27613  * Ext JS Library 1.1.1
27614  * Copyright(c) 2006-2007, Ext JS, LLC.
27615  *
27616  * Originally Released Under LGPL - original licence link has changed is not relivant.
27617  *
27618  * Fork - LGPL
27619  * <script type="text/javascript">
27620  */
27621  
27622 /**
27623  * @class Roo.form.BasicForm
27624  * @extends Roo.util.Observable
27625  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27626  * @constructor
27627  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27628  * @param {Object} config Configuration options
27629  */
27630 Roo.form.BasicForm = function(el, config){
27631     this.allItems = [];
27632     this.childForms = [];
27633     Roo.apply(this, config);
27634     /*
27635      * The Roo.form.Field items in this form.
27636      * @type MixedCollection
27637      */
27638      
27639      
27640     this.items = new Roo.util.MixedCollection(false, function(o){
27641         return o.id || (o.id = Roo.id());
27642     });
27643     this.addEvents({
27644         /**
27645          * @event beforeaction
27646          * Fires before any action is performed. Return false to cancel the action.
27647          * @param {Form} this
27648          * @param {Action} action The action to be performed
27649          */
27650         beforeaction: true,
27651         /**
27652          * @event actionfailed
27653          * Fires when an action fails.
27654          * @param {Form} this
27655          * @param {Action} action The action that failed
27656          */
27657         actionfailed : true,
27658         /**
27659          * @event actioncomplete
27660          * Fires when an action is completed.
27661          * @param {Form} this
27662          * @param {Action} action The action that completed
27663          */
27664         actioncomplete : true
27665     });
27666     if(el){
27667         this.initEl(el);
27668     }
27669     Roo.form.BasicForm.superclass.constructor.call(this);
27670 };
27671
27672 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27673     /**
27674      * @cfg {String} method
27675      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27676      */
27677     /**
27678      * @cfg {DataReader} reader
27679      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27680      * This is optional as there is built-in support for processing JSON.
27681      */
27682     /**
27683      * @cfg {DataReader} errorReader
27684      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27685      * This is completely optional as there is built-in support for processing JSON.
27686      */
27687     /**
27688      * @cfg {String} url
27689      * The URL to use for form actions if one isn't supplied in the action options.
27690      */
27691     /**
27692      * @cfg {Boolean} fileUpload
27693      * Set to true if this form is a file upload.
27694      */
27695      
27696     /**
27697      * @cfg {Object} baseParams
27698      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27699      */
27700      /**
27701      
27702     /**
27703      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27704      */
27705     timeout: 30,
27706
27707     // private
27708     activeAction : null,
27709
27710     /**
27711      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27712      * or setValues() data instead of when the form was first created.
27713      */
27714     trackResetOnLoad : false,
27715     
27716     
27717     /**
27718      * childForms - used for multi-tab forms
27719      * @type {Array}
27720      */
27721     childForms : false,
27722     
27723     /**
27724      * allItems - full list of fields.
27725      * @type {Array}
27726      */
27727     allItems : false,
27728     
27729     /**
27730      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27731      * element by passing it or its id or mask the form itself by passing in true.
27732      * @type Mixed
27733      */
27734     waitMsgTarget : false,
27735
27736     // private
27737     initEl : function(el){
27738         this.el = Roo.get(el);
27739         this.id = this.el.id || Roo.id();
27740         this.el.on('submit', this.onSubmit, this);
27741         this.el.addClass('x-form');
27742     },
27743
27744     // private
27745     onSubmit : function(e){
27746         e.stopEvent();
27747     },
27748
27749     /**
27750      * Returns true if client-side validation on the form is successful.
27751      * @return Boolean
27752      */
27753     isValid : function(){
27754         var valid = true;
27755         this.items.each(function(f){
27756            if(!f.validate()){
27757                valid = false;
27758            }
27759         });
27760         return valid;
27761     },
27762
27763     /**
27764      * Returns true if any fields in this form have changed since their original load.
27765      * @return Boolean
27766      */
27767     isDirty : function(){
27768         var dirty = false;
27769         this.items.each(function(f){
27770            if(f.isDirty()){
27771                dirty = true;
27772                return false;
27773            }
27774         });
27775         return dirty;
27776     },
27777
27778     /**
27779      * Performs a predefined action (submit or load) or custom actions you define on this form.
27780      * @param {String} actionName The name of the action type
27781      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27782      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27783      * accept other config options):
27784      * <pre>
27785 Property          Type             Description
27786 ----------------  ---------------  ----------------------------------------------------------------------------------
27787 url               String           The url for the action (defaults to the form's url)
27788 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27789 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27790 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27791                                    validate the form on the client (defaults to false)
27792      * </pre>
27793      * @return {BasicForm} this
27794      */
27795     doAction : function(action, options){
27796         if(typeof action == 'string'){
27797             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27798         }
27799         if(this.fireEvent('beforeaction', this, action) !== false){
27800             this.beforeAction(action);
27801             action.run.defer(100, action);
27802         }
27803         return this;
27804     },
27805
27806     /**
27807      * Shortcut to do a submit action.
27808      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27809      * @return {BasicForm} this
27810      */
27811     submit : function(options){
27812         this.doAction('submit', options);
27813         return this;
27814     },
27815
27816     /**
27817      * Shortcut to do a load action.
27818      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27819      * @return {BasicForm} this
27820      */
27821     load : function(options){
27822         this.doAction('load', options);
27823         return this;
27824     },
27825
27826     /**
27827      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27828      * @param {Record} record The record to edit
27829      * @return {BasicForm} this
27830      */
27831     updateRecord : function(record){
27832         record.beginEdit();
27833         var fs = record.fields;
27834         fs.each(function(f){
27835             var field = this.findField(f.name);
27836             if(field){
27837                 record.set(f.name, field.getValue());
27838             }
27839         }, this);
27840         record.endEdit();
27841         return this;
27842     },
27843
27844     /**
27845      * Loads an Roo.data.Record into this form.
27846      * @param {Record} record The record to load
27847      * @return {BasicForm} this
27848      */
27849     loadRecord : function(record){
27850         this.setValues(record.data);
27851         return this;
27852     },
27853
27854     // private
27855     beforeAction : function(action){
27856         var o = action.options;
27857         
27858        
27859         if(this.waitMsgTarget === true){
27860             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27861         }else if(this.waitMsgTarget){
27862             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27863             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27864         }else {
27865             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27866         }
27867          
27868     },
27869
27870     // private
27871     afterAction : function(action, success){
27872         this.activeAction = null;
27873         var o = action.options;
27874         
27875         if(this.waitMsgTarget === true){
27876             this.el.unmask();
27877         }else if(this.waitMsgTarget){
27878             this.waitMsgTarget.unmask();
27879         }else{
27880             Roo.MessageBox.updateProgress(1);
27881             Roo.MessageBox.hide();
27882         }
27883          
27884         if(success){
27885             if(o.reset){
27886                 this.reset();
27887             }
27888             Roo.callback(o.success, o.scope, [this, action]);
27889             this.fireEvent('actioncomplete', this, action);
27890             
27891         }else{
27892             
27893             // failure condition..
27894             // we have a scenario where updates need confirming.
27895             // eg. if a locking scenario exists..
27896             // we look for { errors : { needs_confirm : true }} in the response.
27897             if (
27898                 (typeof(action.result) != 'undefined')  &&
27899                 (typeof(action.result.errors) != 'undefined')  &&
27900                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27901            ){
27902                 var _t = this;
27903                 Roo.MessageBox.confirm(
27904                     "Change requires confirmation",
27905                     action.result.errorMsg,
27906                     function(r) {
27907                         if (r != 'yes') {
27908                             return;
27909                         }
27910                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27911                     }
27912                     
27913                 );
27914                 
27915                 
27916                 
27917                 return;
27918             }
27919             
27920             Roo.callback(o.failure, o.scope, [this, action]);
27921             // show an error message if no failed handler is set..
27922             if (!this.hasListener('actionfailed')) {
27923                 Roo.MessageBox.alert("Error",
27924                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27925                         action.result.errorMsg :
27926                         "Saving Failed, please check your entries or try again"
27927                 );
27928             }
27929             
27930             this.fireEvent('actionfailed', this, action);
27931         }
27932         
27933     },
27934
27935     /**
27936      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27937      * @param {String} id The value to search for
27938      * @return Field
27939      */
27940     findField : function(id){
27941         var field = this.items.get(id);
27942         if(!field){
27943             this.items.each(function(f){
27944                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27945                     field = f;
27946                     return false;
27947                 }
27948             });
27949         }
27950         return field || null;
27951     },
27952
27953     /**
27954      * Add a secondary form to this one, 
27955      * Used to provide tabbed forms. One form is primary, with hidden values 
27956      * which mirror the elements from the other forms.
27957      * 
27958      * @param {Roo.form.Form} form to add.
27959      * 
27960      */
27961     addForm : function(form)
27962     {
27963        
27964         if (this.childForms.indexOf(form) > -1) {
27965             // already added..
27966             return;
27967         }
27968         this.childForms.push(form);
27969         var n = '';
27970         Roo.each(form.allItems, function (fe) {
27971             
27972             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27973             if (this.findField(n)) { // already added..
27974                 return;
27975             }
27976             var add = new Roo.form.Hidden({
27977                 name : n
27978             });
27979             add.render(this.el);
27980             
27981             this.add( add );
27982         }, this);
27983         
27984     },
27985     /**
27986      * Mark fields in this form invalid in bulk.
27987      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27988      * @return {BasicForm} this
27989      */
27990     markInvalid : function(errors){
27991         if(errors instanceof Array){
27992             for(var i = 0, len = errors.length; i < len; i++){
27993                 var fieldError = errors[i];
27994                 var f = this.findField(fieldError.id);
27995                 if(f){
27996                     f.markInvalid(fieldError.msg);
27997                 }
27998             }
27999         }else{
28000             var field, id;
28001             for(id in errors){
28002                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
28003                     field.markInvalid(errors[id]);
28004                 }
28005             }
28006         }
28007         Roo.each(this.childForms || [], function (f) {
28008             f.markInvalid(errors);
28009         });
28010         
28011         return this;
28012     },
28013
28014     /**
28015      * Set values for fields in this form in bulk.
28016      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
28017      * @return {BasicForm} this
28018      */
28019     setValues : function(values){
28020         if(values instanceof Array){ // array of objects
28021             for(var i = 0, len = values.length; i < len; i++){
28022                 var v = values[i];
28023                 var f = this.findField(v.id);
28024                 if(f){
28025                     f.setValue(v.value);
28026                     if(this.trackResetOnLoad){
28027                         f.originalValue = f.getValue();
28028                     }
28029                 }
28030             }
28031         }else{ // object hash
28032             var field, id;
28033             for(id in values){
28034                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28035                     
28036                     if (field.setFromData && 
28037                         field.valueField && 
28038                         field.displayField &&
28039                         // combos' with local stores can 
28040                         // be queried via setValue()
28041                         // to set their value..
28042                         (field.store && !field.store.isLocal)
28043                         ) {
28044                         // it's a combo
28045                         var sd = { };
28046                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28047                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28048                         field.setFromData(sd);
28049                         
28050                     } else {
28051                         field.setValue(values[id]);
28052                     }
28053                     
28054                     
28055                     if(this.trackResetOnLoad){
28056                         field.originalValue = field.getValue();
28057                     }
28058                 }
28059             }
28060         }
28061          
28062         Roo.each(this.childForms || [], function (f) {
28063             f.setValues(values);
28064         });
28065                 
28066         return this;
28067     },
28068
28069     /**
28070      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28071      * they are returned as an array.
28072      * @param {Boolean} asString
28073      * @return {Object}
28074      */
28075     getValues : function(asString){
28076         if (this.childForms) {
28077             // copy values from the child forms
28078             Roo.each(this.childForms, function (f) {
28079                 this.setValues(f.getValues());
28080             }, this);
28081         }
28082         
28083         
28084         
28085         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28086         if(asString === true){
28087             return fs;
28088         }
28089         return Roo.urlDecode(fs);
28090     },
28091     
28092     /**
28093      * Returns the fields in this form as an object with key/value pairs. 
28094      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28095      * @return {Object}
28096      */
28097     getFieldValues : function(with_hidden)
28098     {
28099         if (this.childForms) {
28100             // copy values from the child forms
28101             // should this call getFieldValues - probably not as we do not currently copy
28102             // hidden fields when we generate..
28103             Roo.each(this.childForms, function (f) {
28104                 this.setValues(f.getValues());
28105             }, this);
28106         }
28107         
28108         var ret = {};
28109         this.items.each(function(f){
28110             if (!f.getName()) {
28111                 return;
28112             }
28113             var v = f.getValue();
28114             // not sure if this supported any more..
28115             if ((typeof(v) == 'object') && f.getRawValue) {
28116                 v = f.getRawValue() ; // dates..
28117             }
28118             // combo boxes where name != hiddenName...
28119             if (f.name != f.getName()) {
28120                 ret[f.name] = f.getRawValue();
28121             }
28122             ret[f.getName()] = v;
28123         });
28124         
28125         return ret;
28126     },
28127
28128     /**
28129      * Clears all invalid messages in this form.
28130      * @return {BasicForm} this
28131      */
28132     clearInvalid : function(){
28133         this.items.each(function(f){
28134            f.clearInvalid();
28135         });
28136         
28137         Roo.each(this.childForms || [], function (f) {
28138             f.clearInvalid();
28139         });
28140         
28141         
28142         return this;
28143     },
28144
28145     /**
28146      * Resets this form.
28147      * @return {BasicForm} this
28148      */
28149     reset : function(){
28150         this.items.each(function(f){
28151             f.reset();
28152         });
28153         
28154         Roo.each(this.childForms || [], function (f) {
28155             f.reset();
28156         });
28157        
28158         
28159         return this;
28160     },
28161
28162     /**
28163      * Add Roo.form components to this form.
28164      * @param {Field} field1
28165      * @param {Field} field2 (optional)
28166      * @param {Field} etc (optional)
28167      * @return {BasicForm} this
28168      */
28169     add : function(){
28170         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28171         return this;
28172     },
28173
28174
28175     /**
28176      * Removes a field from the items collection (does NOT remove its markup).
28177      * @param {Field} field
28178      * @return {BasicForm} this
28179      */
28180     remove : function(field){
28181         this.items.remove(field);
28182         return this;
28183     },
28184
28185     /**
28186      * Looks at the fields in this form, checks them for an id attribute,
28187      * and calls applyTo on the existing dom element with that id.
28188      * @return {BasicForm} this
28189      */
28190     render : function(){
28191         this.items.each(function(f){
28192             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28193                 f.applyTo(f.id);
28194             }
28195         });
28196         return this;
28197     },
28198
28199     /**
28200      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28201      * @param {Object} values
28202      * @return {BasicForm} this
28203      */
28204     applyToFields : function(o){
28205         this.items.each(function(f){
28206            Roo.apply(f, o);
28207         });
28208         return this;
28209     },
28210
28211     /**
28212      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28213      * @param {Object} values
28214      * @return {BasicForm} this
28215      */
28216     applyIfToFields : function(o){
28217         this.items.each(function(f){
28218            Roo.applyIf(f, o);
28219         });
28220         return this;
28221     }
28222 });
28223
28224 // back compat
28225 Roo.BasicForm = Roo.form.BasicForm;/*
28226  * Based on:
28227  * Ext JS Library 1.1.1
28228  * Copyright(c) 2006-2007, Ext JS, LLC.
28229  *
28230  * Originally Released Under LGPL - original licence link has changed is not relivant.
28231  *
28232  * Fork - LGPL
28233  * <script type="text/javascript">
28234  */
28235
28236 /**
28237  * @class Roo.form.Form
28238  * @extends Roo.form.BasicForm
28239  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28240  * @constructor
28241  * @param {Object} config Configuration options
28242  */
28243 Roo.form.Form = function(config){
28244     var xitems =  [];
28245     if (config.items) {
28246         xitems = config.items;
28247         delete config.items;
28248     }
28249    
28250     
28251     Roo.form.Form.superclass.constructor.call(this, null, config);
28252     this.url = this.url || this.action;
28253     if(!this.root){
28254         this.root = new Roo.form.Layout(Roo.applyIf({
28255             id: Roo.id()
28256         }, config));
28257     }
28258     this.active = this.root;
28259     /**
28260      * Array of all the buttons that have been added to this form via {@link addButton}
28261      * @type Array
28262      */
28263     this.buttons = [];
28264     this.allItems = [];
28265     this.addEvents({
28266         /**
28267          * @event clientvalidation
28268          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28269          * @param {Form} this
28270          * @param {Boolean} valid true if the form has passed client-side validation
28271          */
28272         clientvalidation: true,
28273         /**
28274          * @event rendered
28275          * Fires when the form is rendered
28276          * @param {Roo.form.Form} form
28277          */
28278         rendered : true
28279     });
28280     
28281     if (this.progressUrl) {
28282             // push a hidden field onto the list of fields..
28283             this.addxtype( {
28284                     xns: Roo.form, 
28285                     xtype : 'Hidden', 
28286                     name : 'UPLOAD_IDENTIFIER' 
28287             });
28288         }
28289         
28290     
28291     Roo.each(xitems, this.addxtype, this);
28292     
28293     
28294     
28295 };
28296
28297 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28298     /**
28299      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28300      */
28301     /**
28302      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28303      */
28304     /**
28305      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28306      */
28307     buttonAlign:'center',
28308
28309     /**
28310      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28311      */
28312     minButtonWidth:75,
28313
28314     /**
28315      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28316      * This property cascades to child containers if not set.
28317      */
28318     labelAlign:'left',
28319
28320     /**
28321      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28322      * fires a looping event with that state. This is required to bind buttons to the valid
28323      * state using the config value formBind:true on the button.
28324      */
28325     monitorValid : false,
28326
28327     /**
28328      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28329      */
28330     monitorPoll : 200,
28331     
28332     /**
28333      * @cfg {String} progressUrl - Url to return progress data 
28334      */
28335     
28336     progressUrl : false,
28337   
28338     /**
28339      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28340      * fields are added and the column is closed. If no fields are passed the column remains open
28341      * until end() is called.
28342      * @param {Object} config The config to pass to the column
28343      * @param {Field} field1 (optional)
28344      * @param {Field} field2 (optional)
28345      * @param {Field} etc (optional)
28346      * @return Column The column container object
28347      */
28348     column : function(c){
28349         var col = new Roo.form.Column(c);
28350         this.start(col);
28351         if(arguments.length > 1){ // duplicate code required because of Opera
28352             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28353             this.end();
28354         }
28355         return col;
28356     },
28357
28358     /**
28359      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28360      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28361      * until end() is called.
28362      * @param {Object} config The config to pass to the fieldset
28363      * @param {Field} field1 (optional)
28364      * @param {Field} field2 (optional)
28365      * @param {Field} etc (optional)
28366      * @return FieldSet The fieldset container object
28367      */
28368     fieldset : function(c){
28369         var fs = new Roo.form.FieldSet(c);
28370         this.start(fs);
28371         if(arguments.length > 1){ // duplicate code required because of Opera
28372             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28373             this.end();
28374         }
28375         return fs;
28376     },
28377
28378     /**
28379      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28380      * fields are added and the container is closed. If no fields are passed the container remains open
28381      * until end() is called.
28382      * @param {Object} config The config to pass to the Layout
28383      * @param {Field} field1 (optional)
28384      * @param {Field} field2 (optional)
28385      * @param {Field} etc (optional)
28386      * @return Layout The container object
28387      */
28388     container : function(c){
28389         var l = new Roo.form.Layout(c);
28390         this.start(l);
28391         if(arguments.length > 1){ // duplicate code required because of Opera
28392             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28393             this.end();
28394         }
28395         return l;
28396     },
28397
28398     /**
28399      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28400      * @param {Object} container A Roo.form.Layout or subclass of Layout
28401      * @return {Form} this
28402      */
28403     start : function(c){
28404         // cascade label info
28405         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28406         this.active.stack.push(c);
28407         c.ownerCt = this.active;
28408         this.active = c;
28409         return this;
28410     },
28411
28412     /**
28413      * Closes the current open container
28414      * @return {Form} this
28415      */
28416     end : function(){
28417         if(this.active == this.root){
28418             return this;
28419         }
28420         this.active = this.active.ownerCt;
28421         return this;
28422     },
28423
28424     /**
28425      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28426      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28427      * as the label of the field.
28428      * @param {Field} field1
28429      * @param {Field} field2 (optional)
28430      * @param {Field} etc. (optional)
28431      * @return {Form} this
28432      */
28433     add : function(){
28434         this.active.stack.push.apply(this.active.stack, arguments);
28435         this.allItems.push.apply(this.allItems,arguments);
28436         var r = [];
28437         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28438             if(a[i].isFormField){
28439                 r.push(a[i]);
28440             }
28441         }
28442         if(r.length > 0){
28443             Roo.form.Form.superclass.add.apply(this, r);
28444         }
28445         return this;
28446     },
28447     
28448
28449     
28450     
28451     
28452      /**
28453      * Find any element that has been added to a form, using it's ID or name
28454      * This can include framesets, columns etc. along with regular fields..
28455      * @param {String} id - id or name to find.
28456      
28457      * @return {Element} e - or false if nothing found.
28458      */
28459     findbyId : function(id)
28460     {
28461         var ret = false;
28462         if (!id) {
28463             return ret;
28464         }
28465         Roo.each(this.allItems, function(f){
28466             if (f.id == id || f.name == id ){
28467                 ret = f;
28468                 return false;
28469             }
28470         });
28471         return ret;
28472     },
28473
28474     
28475     
28476     /**
28477      * Render this form into the passed container. This should only be called once!
28478      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28479      * @return {Form} this
28480      */
28481     render : function(ct)
28482     {
28483         
28484         
28485         
28486         ct = Roo.get(ct);
28487         var o = this.autoCreate || {
28488             tag: 'form',
28489             method : this.method || 'POST',
28490             id : this.id || Roo.id()
28491         };
28492         this.initEl(ct.createChild(o));
28493
28494         this.root.render(this.el);
28495         
28496        
28497              
28498         this.items.each(function(f){
28499             f.render('x-form-el-'+f.id);
28500         });
28501
28502         if(this.buttons.length > 0){
28503             // tables are required to maintain order and for correct IE layout
28504             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28505                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28506                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28507             }}, null, true);
28508             var tr = tb.getElementsByTagName('tr')[0];
28509             for(var i = 0, len = this.buttons.length; i < len; i++) {
28510                 var b = this.buttons[i];
28511                 var td = document.createElement('td');
28512                 td.className = 'x-form-btn-td';
28513                 b.render(tr.appendChild(td));
28514             }
28515         }
28516         if(this.monitorValid){ // initialize after render
28517             this.startMonitoring();
28518         }
28519         this.fireEvent('rendered', this);
28520         return this;
28521     },
28522
28523     /**
28524      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28525      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28526      * object or a valid Roo.DomHelper element config
28527      * @param {Function} handler The function called when the button is clicked
28528      * @param {Object} scope (optional) The scope of the handler function
28529      * @return {Roo.Button}
28530      */
28531     addButton : function(config, handler, scope){
28532         var bc = {
28533             handler: handler,
28534             scope: scope,
28535             minWidth: this.minButtonWidth,
28536             hideParent:true
28537         };
28538         if(typeof config == "string"){
28539             bc.text = config;
28540         }else{
28541             Roo.apply(bc, config);
28542         }
28543         var btn = new Roo.Button(null, bc);
28544         this.buttons.push(btn);
28545         return btn;
28546     },
28547
28548      /**
28549      * Adds a series of form elements (using the xtype property as the factory method.
28550      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28551      * @param {Object} config 
28552      */
28553     
28554     addxtype : function()
28555     {
28556         var ar = Array.prototype.slice.call(arguments, 0);
28557         var ret = false;
28558         for(var i = 0; i < ar.length; i++) {
28559             if (!ar[i]) {
28560                 continue; // skip -- if this happends something invalid got sent, we 
28561                 // should ignore it, as basically that interface element will not show up
28562                 // and that should be pretty obvious!!
28563             }
28564             
28565             if (Roo.form[ar[i].xtype]) {
28566                 ar[i].form = this;
28567                 var fe = Roo.factory(ar[i], Roo.form);
28568                 if (!ret) {
28569                     ret = fe;
28570                 }
28571                 fe.form = this;
28572                 if (fe.store) {
28573                     fe.store.form = this;
28574                 }
28575                 if (fe.isLayout) {  
28576                          
28577                     this.start(fe);
28578                     this.allItems.push(fe);
28579                     if (fe.items && fe.addxtype) {
28580                         fe.addxtype.apply(fe, fe.items);
28581                         delete fe.items;
28582                     }
28583                      this.end();
28584                     continue;
28585                 }
28586                 
28587                 
28588                  
28589                 this.add(fe);
28590               //  console.log('adding ' + ar[i].xtype);
28591             }
28592             if (ar[i].xtype == 'Button') {  
28593                 //console.log('adding button');
28594                 //console.log(ar[i]);
28595                 this.addButton(ar[i]);
28596                 this.allItems.push(fe);
28597                 continue;
28598             }
28599             
28600             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28601                 alert('end is not supported on xtype any more, use items');
28602             //    this.end();
28603             //    //console.log('adding end');
28604             }
28605             
28606         }
28607         return ret;
28608     },
28609     
28610     /**
28611      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28612      * option "monitorValid"
28613      */
28614     startMonitoring : function(){
28615         if(!this.bound){
28616             this.bound = true;
28617             Roo.TaskMgr.start({
28618                 run : this.bindHandler,
28619                 interval : this.monitorPoll || 200,
28620                 scope: this
28621             });
28622         }
28623     },
28624
28625     /**
28626      * Stops monitoring of the valid state of this form
28627      */
28628     stopMonitoring : function(){
28629         this.bound = false;
28630     },
28631
28632     // private
28633     bindHandler : function(){
28634         if(!this.bound){
28635             return false; // stops binding
28636         }
28637         var valid = true;
28638         this.items.each(function(f){
28639             if(!f.isValid(true)){
28640                 valid = false;
28641                 return false;
28642             }
28643         });
28644         for(var i = 0, len = this.buttons.length; i < len; i++){
28645             var btn = this.buttons[i];
28646             if(btn.formBind === true && btn.disabled === valid){
28647                 btn.setDisabled(!valid);
28648             }
28649         }
28650         this.fireEvent('clientvalidation', this, valid);
28651     }
28652     
28653     
28654     
28655     
28656     
28657     
28658     
28659     
28660 });
28661
28662
28663 // back compat
28664 Roo.Form = Roo.form.Form;
28665 /*
28666  * Based on:
28667  * Ext JS Library 1.1.1
28668  * Copyright(c) 2006-2007, Ext JS, LLC.
28669  *
28670  * Originally Released Under LGPL - original licence link has changed is not relivant.
28671  *
28672  * Fork - LGPL
28673  * <script type="text/javascript">
28674  */
28675  
28676  /**
28677  * @class Roo.form.Action
28678  * Internal Class used to handle form actions
28679  * @constructor
28680  * @param {Roo.form.BasicForm} el The form element or its id
28681  * @param {Object} config Configuration options
28682  */
28683  
28684  
28685 // define the action interface
28686 Roo.form.Action = function(form, options){
28687     this.form = form;
28688     this.options = options || {};
28689 };
28690 /**
28691  * Client Validation Failed
28692  * @const 
28693  */
28694 Roo.form.Action.CLIENT_INVALID = 'client';
28695 /**
28696  * Server Validation Failed
28697  * @const 
28698  */
28699  Roo.form.Action.SERVER_INVALID = 'server';
28700  /**
28701  * Connect to Server Failed
28702  * @const 
28703  */
28704 Roo.form.Action.CONNECT_FAILURE = 'connect';
28705 /**
28706  * Reading Data from Server Failed
28707  * @const 
28708  */
28709 Roo.form.Action.LOAD_FAILURE = 'load';
28710
28711 Roo.form.Action.prototype = {
28712     type : 'default',
28713     failureType : undefined,
28714     response : undefined,
28715     result : undefined,
28716
28717     // interface method
28718     run : function(options){
28719
28720     },
28721
28722     // interface method
28723     success : function(response){
28724
28725     },
28726
28727     // interface method
28728     handleResponse : function(response){
28729
28730     },
28731
28732     // default connection failure
28733     failure : function(response){
28734         
28735         this.response = response;
28736         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28737         this.form.afterAction(this, false);
28738     },
28739
28740     processResponse : function(response){
28741         this.response = response;
28742         if(!response.responseText){
28743             return true;
28744         }
28745         this.result = this.handleResponse(response);
28746         return this.result;
28747     },
28748
28749     // utility functions used internally
28750     getUrl : function(appendParams){
28751         var url = this.options.url || this.form.url || this.form.el.dom.action;
28752         if(appendParams){
28753             var p = this.getParams();
28754             if(p){
28755                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28756             }
28757         }
28758         return url;
28759     },
28760
28761     getMethod : function(){
28762         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28763     },
28764
28765     getParams : function(){
28766         var bp = this.form.baseParams;
28767         var p = this.options.params;
28768         if(p){
28769             if(typeof p == "object"){
28770                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28771             }else if(typeof p == 'string' && bp){
28772                 p += '&' + Roo.urlEncode(bp);
28773             }
28774         }else if(bp){
28775             p = Roo.urlEncode(bp);
28776         }
28777         return p;
28778     },
28779
28780     createCallback : function(){
28781         return {
28782             success: this.success,
28783             failure: this.failure,
28784             scope: this,
28785             timeout: (this.form.timeout*1000),
28786             upload: this.form.fileUpload ? this.success : undefined
28787         };
28788     }
28789 };
28790
28791 Roo.form.Action.Submit = function(form, options){
28792     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28793 };
28794
28795 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28796     type : 'submit',
28797
28798     haveProgress : false,
28799     uploadComplete : false,
28800     
28801     // uploadProgress indicator.
28802     uploadProgress : function()
28803     {
28804         if (!this.form.progressUrl) {
28805             return;
28806         }
28807         
28808         if (!this.haveProgress) {
28809             Roo.MessageBox.progress("Uploading", "Uploading");
28810         }
28811         if (this.uploadComplete) {
28812            Roo.MessageBox.hide();
28813            return;
28814         }
28815         
28816         this.haveProgress = true;
28817    
28818         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28819         
28820         var c = new Roo.data.Connection();
28821         c.request({
28822             url : this.form.progressUrl,
28823             params: {
28824                 id : uid
28825             },
28826             method: 'GET',
28827             success : function(req){
28828                //console.log(data);
28829                 var rdata = false;
28830                 var edata;
28831                 try  {
28832                    rdata = Roo.decode(req.responseText)
28833                 } catch (e) {
28834                     Roo.log("Invalid data from server..");
28835                     Roo.log(edata);
28836                     return;
28837                 }
28838                 if (!rdata || !rdata.success) {
28839                     Roo.log(rdata);
28840                     Roo.MessageBox.alert(Roo.encode(rdata));
28841                     return;
28842                 }
28843                 var data = rdata.data;
28844                 
28845                 if (this.uploadComplete) {
28846                    Roo.MessageBox.hide();
28847                    return;
28848                 }
28849                    
28850                 if (data){
28851                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28852                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28853                     );
28854                 }
28855                 this.uploadProgress.defer(2000,this);
28856             },
28857        
28858             failure: function(data) {
28859                 Roo.log('progress url failed ');
28860                 Roo.log(data);
28861             },
28862             scope : this
28863         });
28864            
28865     },
28866     
28867     
28868     run : function()
28869     {
28870         // run get Values on the form, so it syncs any secondary forms.
28871         this.form.getValues();
28872         
28873         var o = this.options;
28874         var method = this.getMethod();
28875         var isPost = method == 'POST';
28876         if(o.clientValidation === false || this.form.isValid()){
28877             
28878             if (this.form.progressUrl) {
28879                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28880                     (new Date() * 1) + '' + Math.random());
28881                     
28882             } 
28883             
28884             
28885             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28886                 form:this.form.el.dom,
28887                 url:this.getUrl(!isPost),
28888                 method: method,
28889                 params:isPost ? this.getParams() : null,
28890                 isUpload: this.form.fileUpload
28891             }));
28892             
28893             this.uploadProgress();
28894
28895         }else if (o.clientValidation !== false){ // client validation failed
28896             this.failureType = Roo.form.Action.CLIENT_INVALID;
28897             this.form.afterAction(this, false);
28898         }
28899     },
28900
28901     success : function(response)
28902     {
28903         this.uploadComplete= true;
28904         if (this.haveProgress) {
28905             Roo.MessageBox.hide();
28906         }
28907         
28908         
28909         var result = this.processResponse(response);
28910         if(result === true || result.success){
28911             this.form.afterAction(this, true);
28912             return;
28913         }
28914         if(result.errors){
28915             this.form.markInvalid(result.errors);
28916             this.failureType = Roo.form.Action.SERVER_INVALID;
28917         }
28918         this.form.afterAction(this, false);
28919     },
28920     failure : function(response)
28921     {
28922         this.uploadComplete= true;
28923         if (this.haveProgress) {
28924             Roo.MessageBox.hide();
28925         }
28926         
28927         this.response = response;
28928         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28929         this.form.afterAction(this, false);
28930     },
28931     
28932     handleResponse : function(response){
28933         if(this.form.errorReader){
28934             var rs = this.form.errorReader.read(response);
28935             var errors = [];
28936             if(rs.records){
28937                 for(var i = 0, len = rs.records.length; i < len; i++) {
28938                     var r = rs.records[i];
28939                     errors[i] = r.data;
28940                 }
28941             }
28942             if(errors.length < 1){
28943                 errors = null;
28944             }
28945             return {
28946                 success : rs.success,
28947                 errors : errors
28948             };
28949         }
28950         var ret = false;
28951         try {
28952             ret = Roo.decode(response.responseText);
28953         } catch (e) {
28954             ret = {
28955                 success: false,
28956                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28957                 errors : []
28958             };
28959         }
28960         return ret;
28961         
28962     }
28963 });
28964
28965
28966 Roo.form.Action.Load = function(form, options){
28967     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28968     this.reader = this.form.reader;
28969 };
28970
28971 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28972     type : 'load',
28973
28974     run : function(){
28975         
28976         Roo.Ajax.request(Roo.apply(
28977                 this.createCallback(), {
28978                     method:this.getMethod(),
28979                     url:this.getUrl(false),
28980                     params:this.getParams()
28981         }));
28982     },
28983
28984     success : function(response){
28985         
28986         var result = this.processResponse(response);
28987         if(result === true || !result.success || !result.data){
28988             this.failureType = Roo.form.Action.LOAD_FAILURE;
28989             this.form.afterAction(this, false);
28990             return;
28991         }
28992         this.form.clearInvalid();
28993         this.form.setValues(result.data);
28994         this.form.afterAction(this, true);
28995     },
28996
28997     handleResponse : function(response){
28998         if(this.form.reader){
28999             var rs = this.form.reader.read(response);
29000             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
29001             return {
29002                 success : rs.success,
29003                 data : data
29004             };
29005         }
29006         return Roo.decode(response.responseText);
29007     }
29008 });
29009
29010 Roo.form.Action.ACTION_TYPES = {
29011     'load' : Roo.form.Action.Load,
29012     'submit' : Roo.form.Action.Submit
29013 };/*
29014  * Based on:
29015  * Ext JS Library 1.1.1
29016  * Copyright(c) 2006-2007, Ext JS, LLC.
29017  *
29018  * Originally Released Under LGPL - original licence link has changed is not relivant.
29019  *
29020  * Fork - LGPL
29021  * <script type="text/javascript">
29022  */
29023  
29024 /**
29025  * @class Roo.form.Layout
29026  * @extends Roo.Component
29027  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29028  * @constructor
29029  * @param {Object} config Configuration options
29030  */
29031 Roo.form.Layout = function(config){
29032     var xitems = [];
29033     if (config.items) {
29034         xitems = config.items;
29035         delete config.items;
29036     }
29037     Roo.form.Layout.superclass.constructor.call(this, config);
29038     this.stack = [];
29039     Roo.each(xitems, this.addxtype, this);
29040      
29041 };
29042
29043 Roo.extend(Roo.form.Layout, Roo.Component, {
29044     /**
29045      * @cfg {String/Object} autoCreate
29046      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29047      */
29048     /**
29049      * @cfg {String/Object/Function} style
29050      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29051      * a function which returns such a specification.
29052      */
29053     /**
29054      * @cfg {String} labelAlign
29055      * Valid values are "left," "top" and "right" (defaults to "left")
29056      */
29057     /**
29058      * @cfg {Number} labelWidth
29059      * Fixed width in pixels of all field labels (defaults to undefined)
29060      */
29061     /**
29062      * @cfg {Boolean} clear
29063      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29064      */
29065     clear : true,
29066     /**
29067      * @cfg {String} labelSeparator
29068      * The separator to use after field labels (defaults to ':')
29069      */
29070     labelSeparator : ':',
29071     /**
29072      * @cfg {Boolean} hideLabels
29073      * True to suppress the display of field labels in this layout (defaults to false)
29074      */
29075     hideLabels : false,
29076
29077     // private
29078     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29079     
29080     isLayout : true,
29081     
29082     // private
29083     onRender : function(ct, position){
29084         if(this.el){ // from markup
29085             this.el = Roo.get(this.el);
29086         }else {  // generate
29087             var cfg = this.getAutoCreate();
29088             this.el = ct.createChild(cfg, position);
29089         }
29090         if(this.style){
29091             this.el.applyStyles(this.style);
29092         }
29093         if(this.labelAlign){
29094             this.el.addClass('x-form-label-'+this.labelAlign);
29095         }
29096         if(this.hideLabels){
29097             this.labelStyle = "display:none";
29098             this.elementStyle = "padding-left:0;";
29099         }else{
29100             if(typeof this.labelWidth == 'number'){
29101                 this.labelStyle = "width:"+this.labelWidth+"px;";
29102                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29103             }
29104             if(this.labelAlign == 'top'){
29105                 this.labelStyle = "width:auto;";
29106                 this.elementStyle = "padding-left:0;";
29107             }
29108         }
29109         var stack = this.stack;
29110         var slen = stack.length;
29111         if(slen > 0){
29112             if(!this.fieldTpl){
29113                 var t = new Roo.Template(
29114                     '<div class="x-form-item {5}">',
29115                         '<label for="{0}" style="{2}">{1}{4}</label>',
29116                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29117                         '</div>',
29118                     '</div><div class="x-form-clear-left"></div>'
29119                 );
29120                 t.disableFormats = true;
29121                 t.compile();
29122                 Roo.form.Layout.prototype.fieldTpl = t;
29123             }
29124             for(var i = 0; i < slen; i++) {
29125                 if(stack[i].isFormField){
29126                     this.renderField(stack[i]);
29127                 }else{
29128                     this.renderComponent(stack[i]);
29129                 }
29130             }
29131         }
29132         if(this.clear){
29133             this.el.createChild({cls:'x-form-clear'});
29134         }
29135     },
29136
29137     // private
29138     renderField : function(f){
29139         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29140                f.id, //0
29141                f.fieldLabel, //1
29142                f.labelStyle||this.labelStyle||'', //2
29143                this.elementStyle||'', //3
29144                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29145                f.itemCls||this.itemCls||''  //5
29146        ], true).getPrevSibling());
29147     },
29148
29149     // private
29150     renderComponent : function(c){
29151         c.render(c.isLayout ? this.el : this.el.createChild());    
29152     },
29153     /**
29154      * Adds a object form elements (using the xtype property as the factory method.)
29155      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29156      * @param {Object} config 
29157      */
29158     addxtype : function(o)
29159     {
29160         // create the lement.
29161         o.form = this.form;
29162         var fe = Roo.factory(o, Roo.form);
29163         this.form.allItems.push(fe);
29164         this.stack.push(fe);
29165         
29166         if (fe.isFormField) {
29167             this.form.items.add(fe);
29168         }
29169          
29170         return fe;
29171     }
29172 });
29173
29174 /**
29175  * @class Roo.form.Column
29176  * @extends Roo.form.Layout
29177  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29178  * @constructor
29179  * @param {Object} config Configuration options
29180  */
29181 Roo.form.Column = function(config){
29182     Roo.form.Column.superclass.constructor.call(this, config);
29183 };
29184
29185 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29186     /**
29187      * @cfg {Number/String} width
29188      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29189      */
29190     /**
29191      * @cfg {String/Object} autoCreate
29192      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29193      */
29194
29195     // private
29196     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29197
29198     // private
29199     onRender : function(ct, position){
29200         Roo.form.Column.superclass.onRender.call(this, ct, position);
29201         if(this.width){
29202             this.el.setWidth(this.width);
29203         }
29204     }
29205 });
29206
29207
29208 /**
29209  * @class Roo.form.Row
29210  * @extends Roo.form.Layout
29211  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29212  * @constructor
29213  * @param {Object} config Configuration options
29214  */
29215
29216  
29217 Roo.form.Row = function(config){
29218     Roo.form.Row.superclass.constructor.call(this, config);
29219 };
29220  
29221 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29222       /**
29223      * @cfg {Number/String} width
29224      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29225      */
29226     /**
29227      * @cfg {Number/String} height
29228      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29229      */
29230     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29231     
29232     padWidth : 20,
29233     // private
29234     onRender : function(ct, position){
29235         //console.log('row render');
29236         if(!this.rowTpl){
29237             var t = new Roo.Template(
29238                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29239                     '<label for="{0}" style="{2}">{1}{4}</label>',
29240                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29241                     '</div>',
29242                 '</div>'
29243             );
29244             t.disableFormats = true;
29245             t.compile();
29246             Roo.form.Layout.prototype.rowTpl = t;
29247         }
29248         this.fieldTpl = this.rowTpl;
29249         
29250         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29251         var labelWidth = 100;
29252         
29253         if ((this.labelAlign != 'top')) {
29254             if (typeof this.labelWidth == 'number') {
29255                 labelWidth = this.labelWidth
29256             }
29257             this.padWidth =  20 + labelWidth;
29258             
29259         }
29260         
29261         Roo.form.Column.superclass.onRender.call(this, ct, position);
29262         if(this.width){
29263             this.el.setWidth(this.width);
29264         }
29265         if(this.height){
29266             this.el.setHeight(this.height);
29267         }
29268     },
29269     
29270     // private
29271     renderField : function(f){
29272         f.fieldEl = this.fieldTpl.append(this.el, [
29273                f.id, f.fieldLabel,
29274                f.labelStyle||this.labelStyle||'',
29275                this.elementStyle||'',
29276                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29277                f.itemCls||this.itemCls||'',
29278                f.width ? f.width + this.padWidth : 160 + this.padWidth
29279        ],true);
29280     }
29281 });
29282  
29283
29284 /**
29285  * @class Roo.form.FieldSet
29286  * @extends Roo.form.Layout
29287  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29288  * @constructor
29289  * @param {Object} config Configuration options
29290  */
29291 Roo.form.FieldSet = function(config){
29292     Roo.form.FieldSet.superclass.constructor.call(this, config);
29293 };
29294
29295 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29296     /**
29297      * @cfg {String} legend
29298      * The text to display as the legend for the FieldSet (defaults to '')
29299      */
29300     /**
29301      * @cfg {String/Object} autoCreate
29302      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29303      */
29304
29305     // private
29306     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29307
29308     // private
29309     onRender : function(ct, position){
29310         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29311         if(this.legend){
29312             this.setLegend(this.legend);
29313         }
29314     },
29315
29316     // private
29317     setLegend : function(text){
29318         if(this.rendered){
29319             this.el.child('legend').update(text);
29320         }
29321     }
29322 });/*
29323  * Based on:
29324  * Ext JS Library 1.1.1
29325  * Copyright(c) 2006-2007, Ext JS, LLC.
29326  *
29327  * Originally Released Under LGPL - original licence link has changed is not relivant.
29328  *
29329  * Fork - LGPL
29330  * <script type="text/javascript">
29331  */
29332 /**
29333  * @class Roo.form.VTypes
29334  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29335  * @singleton
29336  */
29337 Roo.form.VTypes = function(){
29338     // closure these in so they are only created once.
29339     var alpha = /^[a-zA-Z_]+$/;
29340     var alphanum = /^[a-zA-Z0-9_]+$/;
29341     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29342     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29343
29344     // All these messages and functions are configurable
29345     return {
29346         /**
29347          * The function used to validate email addresses
29348          * @param {String} value The email address
29349          */
29350         'email' : function(v){
29351             return email.test(v);
29352         },
29353         /**
29354          * The error text to display when the email validation function returns false
29355          * @type String
29356          */
29357         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29358         /**
29359          * The keystroke filter mask to be applied on email input
29360          * @type RegExp
29361          */
29362         'emailMask' : /[a-z0-9_\.\-@]/i,
29363
29364         /**
29365          * The function used to validate URLs
29366          * @param {String} value The URL
29367          */
29368         'url' : function(v){
29369             return url.test(v);
29370         },
29371         /**
29372          * The error text to display when the url validation function returns false
29373          * @type String
29374          */
29375         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29376         
29377         /**
29378          * The function used to validate alpha values
29379          * @param {String} value The value
29380          */
29381         'alpha' : function(v){
29382             return alpha.test(v);
29383         },
29384         /**
29385          * The error text to display when the alpha validation function returns false
29386          * @type String
29387          */
29388         'alphaText' : 'This field should only contain letters and _',
29389         /**
29390          * The keystroke filter mask to be applied on alpha input
29391          * @type RegExp
29392          */
29393         'alphaMask' : /[a-z_]/i,
29394
29395         /**
29396          * The function used to validate alphanumeric values
29397          * @param {String} value The value
29398          */
29399         'alphanum' : function(v){
29400             return alphanum.test(v);
29401         },
29402         /**
29403          * The error text to display when the alphanumeric validation function returns false
29404          * @type String
29405          */
29406         'alphanumText' : 'This field should only contain letters, numbers and _',
29407         /**
29408          * The keystroke filter mask to be applied on alphanumeric input
29409          * @type RegExp
29410          */
29411         'alphanumMask' : /[a-z0-9_]/i
29412     };
29413 }();//<script type="text/javascript">
29414
29415 /**
29416  * @class Roo.form.FCKeditor
29417  * @extends Roo.form.TextArea
29418  * Wrapper around the FCKEditor http://www.fckeditor.net
29419  * @constructor
29420  * Creates a new FCKeditor
29421  * @param {Object} config Configuration options
29422  */
29423 Roo.form.FCKeditor = function(config){
29424     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29425     this.addEvents({
29426          /**
29427          * @event editorinit
29428          * Fired when the editor is initialized - you can add extra handlers here..
29429          * @param {FCKeditor} this
29430          * @param {Object} the FCK object.
29431          */
29432         editorinit : true
29433     });
29434     
29435     
29436 };
29437 Roo.form.FCKeditor.editors = { };
29438 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29439 {
29440     //defaultAutoCreate : {
29441     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29442     //},
29443     // private
29444     /**
29445      * @cfg {Object} fck options - see fck manual for details.
29446      */
29447     fckconfig : false,
29448     
29449     /**
29450      * @cfg {Object} fck toolbar set (Basic or Default)
29451      */
29452     toolbarSet : 'Basic',
29453     /**
29454      * @cfg {Object} fck BasePath
29455      */ 
29456     basePath : '/fckeditor/',
29457     
29458     
29459     frame : false,
29460     
29461     value : '',
29462     
29463    
29464     onRender : function(ct, position)
29465     {
29466         if(!this.el){
29467             this.defaultAutoCreate = {
29468                 tag: "textarea",
29469                 style:"width:300px;height:60px;",
29470                 autocomplete: "off"
29471             };
29472         }
29473         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29474         /*
29475         if(this.grow){
29476             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29477             if(this.preventScrollbars){
29478                 this.el.setStyle("overflow", "hidden");
29479             }
29480             this.el.setHeight(this.growMin);
29481         }
29482         */
29483         //console.log('onrender' + this.getId() );
29484         Roo.form.FCKeditor.editors[this.getId()] = this;
29485          
29486
29487         this.replaceTextarea() ;
29488         
29489     },
29490     
29491     getEditor : function() {
29492         return this.fckEditor;
29493     },
29494     /**
29495      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29496      * @param {Mixed} value The value to set
29497      */
29498     
29499     
29500     setValue : function(value)
29501     {
29502         //console.log('setValue: ' + value);
29503         
29504         if(typeof(value) == 'undefined') { // not sure why this is happending...
29505             return;
29506         }
29507         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29508         
29509         //if(!this.el || !this.getEditor()) {
29510         //    this.value = value;
29511             //this.setValue.defer(100,this,[value]);    
29512         //    return;
29513         //} 
29514         
29515         if(!this.getEditor()) {
29516             return;
29517         }
29518         
29519         this.getEditor().SetData(value);
29520         
29521         //
29522
29523     },
29524
29525     /**
29526      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29527      * @return {Mixed} value The field value
29528      */
29529     getValue : function()
29530     {
29531         
29532         if (this.frame && this.frame.dom.style.display == 'none') {
29533             return Roo.form.FCKeditor.superclass.getValue.call(this);
29534         }
29535         
29536         if(!this.el || !this.getEditor()) {
29537            
29538            // this.getValue.defer(100,this); 
29539             return this.value;
29540         }
29541        
29542         
29543         var value=this.getEditor().GetData();
29544         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29545         return Roo.form.FCKeditor.superclass.getValue.call(this);
29546         
29547
29548     },
29549
29550     /**
29551      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29552      * @return {Mixed} value The field value
29553      */
29554     getRawValue : function()
29555     {
29556         if (this.frame && this.frame.dom.style.display == 'none') {
29557             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29558         }
29559         
29560         if(!this.el || !this.getEditor()) {
29561             //this.getRawValue.defer(100,this); 
29562             return this.value;
29563             return;
29564         }
29565         
29566         
29567         
29568         var value=this.getEditor().GetData();
29569         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29570         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29571          
29572     },
29573     
29574     setSize : function(w,h) {
29575         
29576         
29577         
29578         //if (this.frame && this.frame.dom.style.display == 'none') {
29579         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29580         //    return;
29581         //}
29582         //if(!this.el || !this.getEditor()) {
29583         //    this.setSize.defer(100,this, [w,h]); 
29584         //    return;
29585         //}
29586         
29587         
29588         
29589         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29590         
29591         this.frame.dom.setAttribute('width', w);
29592         this.frame.dom.setAttribute('height', h);
29593         this.frame.setSize(w,h);
29594         
29595     },
29596     
29597     toggleSourceEdit : function(value) {
29598         
29599       
29600          
29601         this.el.dom.style.display = value ? '' : 'none';
29602         this.frame.dom.style.display = value ?  'none' : '';
29603         
29604     },
29605     
29606     
29607     focus: function(tag)
29608     {
29609         if (this.frame.dom.style.display == 'none') {
29610             return Roo.form.FCKeditor.superclass.focus.call(this);
29611         }
29612         if(!this.el || !this.getEditor()) {
29613             this.focus.defer(100,this, [tag]); 
29614             return;
29615         }
29616         
29617         
29618         
29619         
29620         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29621         this.getEditor().Focus();
29622         if (tgs.length) {
29623             if (!this.getEditor().Selection.GetSelection()) {
29624                 this.focus.defer(100,this, [tag]); 
29625                 return;
29626             }
29627             
29628             
29629             var r = this.getEditor().EditorDocument.createRange();
29630             r.setStart(tgs[0],0);
29631             r.setEnd(tgs[0],0);
29632             this.getEditor().Selection.GetSelection().removeAllRanges();
29633             this.getEditor().Selection.GetSelection().addRange(r);
29634             this.getEditor().Focus();
29635         }
29636         
29637     },
29638     
29639     
29640     
29641     replaceTextarea : function()
29642     {
29643         if ( document.getElementById( this.getId() + '___Frame' ) )
29644             return ;
29645         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29646         //{
29647             // We must check the elements firstly using the Id and then the name.
29648         var oTextarea = document.getElementById( this.getId() );
29649         
29650         var colElementsByName = document.getElementsByName( this.getId() ) ;
29651          
29652         oTextarea.style.display = 'none' ;
29653
29654         if ( oTextarea.tabIndex ) {            
29655             this.TabIndex = oTextarea.tabIndex ;
29656         }
29657         
29658         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29659         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29660         this.frame = Roo.get(this.getId() + '___Frame')
29661     },
29662     
29663     _getConfigHtml : function()
29664     {
29665         var sConfig = '' ;
29666
29667         for ( var o in this.fckconfig ) {
29668             sConfig += sConfig.length > 0  ? '&amp;' : '';
29669             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29670         }
29671
29672         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29673     },
29674     
29675     
29676     _getIFrameHtml : function()
29677     {
29678         var sFile = 'fckeditor.html' ;
29679         /* no idea what this is about..
29680         try
29681         {
29682             if ( (/fcksource=true/i).test( window.top.location.search ) )
29683                 sFile = 'fckeditor.original.html' ;
29684         }
29685         catch (e) { 
29686         */
29687
29688         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29689         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29690         
29691         
29692         var html = '<iframe id="' + this.getId() +
29693             '___Frame" src="' + sLink +
29694             '" width="' + this.width +
29695             '" height="' + this.height + '"' +
29696             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29697             ' frameborder="0" scrolling="no"></iframe>' ;
29698
29699         return html ;
29700     },
29701     
29702     _insertHtmlBefore : function( html, element )
29703     {
29704         if ( element.insertAdjacentHTML )       {
29705             // IE
29706             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29707         } else { // Gecko
29708             var oRange = document.createRange() ;
29709             oRange.setStartBefore( element ) ;
29710             var oFragment = oRange.createContextualFragment( html );
29711             element.parentNode.insertBefore( oFragment, element ) ;
29712         }
29713     }
29714     
29715     
29716   
29717     
29718     
29719     
29720     
29721
29722 });
29723
29724 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29725
29726 function FCKeditor_OnComplete(editorInstance){
29727     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29728     f.fckEditor = editorInstance;
29729     //console.log("loaded");
29730     f.fireEvent('editorinit', f, editorInstance);
29731
29732   
29733
29734  
29735
29736
29737
29738
29739
29740
29741
29742
29743
29744
29745
29746
29747
29748
29749
29750 //<script type="text/javascript">
29751 /**
29752  * @class Roo.form.GridField
29753  * @extends Roo.form.Field
29754  * Embed a grid (or editable grid into a form)
29755  * STATUS ALPHA
29756  * 
29757  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29758  * it needs 
29759  * xgrid.store = Roo.data.Store
29760  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29761  * xgrid.store.reader = Roo.data.JsonReader 
29762  * 
29763  * 
29764  * @constructor
29765  * Creates a new GridField
29766  * @param {Object} config Configuration options
29767  */
29768 Roo.form.GridField = function(config){
29769     Roo.form.GridField.superclass.constructor.call(this, config);
29770      
29771 };
29772
29773 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29774     /**
29775      * @cfg {Number} width  - used to restrict width of grid..
29776      */
29777     width : 100,
29778     /**
29779      * @cfg {Number} height - used to restrict height of grid..
29780      */
29781     height : 50,
29782      /**
29783      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29784          * 
29785          *}
29786      */
29787     xgrid : false, 
29788     /**
29789      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29790      * {tag: "input", type: "checkbox", autocomplete: "off"})
29791      */
29792    // defaultAutoCreate : { tag: 'div' },
29793     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29794     /**
29795      * @cfg {String} addTitle Text to include for adding a title.
29796      */
29797     addTitle : false,
29798     //
29799     onResize : function(){
29800         Roo.form.Field.superclass.onResize.apply(this, arguments);
29801     },
29802
29803     initEvents : function(){
29804         // Roo.form.Checkbox.superclass.initEvents.call(this);
29805         // has no events...
29806        
29807     },
29808
29809
29810     getResizeEl : function(){
29811         return this.wrap;
29812     },
29813
29814     getPositionEl : function(){
29815         return this.wrap;
29816     },
29817
29818     // private
29819     onRender : function(ct, position){
29820         
29821         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29822         var style = this.style;
29823         delete this.style;
29824         
29825         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29826         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29827         this.viewEl = this.wrap.createChild({ tag: 'div' });
29828         if (style) {
29829             this.viewEl.applyStyles(style);
29830         }
29831         if (this.width) {
29832             this.viewEl.setWidth(this.width);
29833         }
29834         if (this.height) {
29835             this.viewEl.setHeight(this.height);
29836         }
29837         //if(this.inputValue !== undefined){
29838         //this.setValue(this.value);
29839         
29840         
29841         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29842         
29843         
29844         this.grid.render();
29845         this.grid.getDataSource().on('remove', this.refreshValue, this);
29846         this.grid.getDataSource().on('update', this.refreshValue, this);
29847         this.grid.on('afteredit', this.refreshValue, this);
29848  
29849     },
29850      
29851     
29852     /**
29853      * Sets the value of the item. 
29854      * @param {String} either an object  or a string..
29855      */
29856     setValue : function(v){
29857         //this.value = v;
29858         v = v || []; // empty set..
29859         // this does not seem smart - it really only affects memoryproxy grids..
29860         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29861             var ds = this.grid.getDataSource();
29862             // assumes a json reader..
29863             var data = {}
29864             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29865             ds.loadData( data);
29866         }
29867         // clear selection so it does not get stale.
29868         if (this.grid.sm) { 
29869             this.grid.sm.clearSelections();
29870         }
29871         
29872         Roo.form.GridField.superclass.setValue.call(this, v);
29873         this.refreshValue();
29874         // should load data in the grid really....
29875     },
29876     
29877     // private
29878     refreshValue: function() {
29879          var val = [];
29880         this.grid.getDataSource().each(function(r) {
29881             val.push(r.data);
29882         });
29883         this.el.dom.value = Roo.encode(val);
29884     }
29885     
29886      
29887     
29888     
29889 });/*
29890  * Based on:
29891  * Ext JS Library 1.1.1
29892  * Copyright(c) 2006-2007, Ext JS, LLC.
29893  *
29894  * Originally Released Under LGPL - original licence link has changed is not relivant.
29895  *
29896  * Fork - LGPL
29897  * <script type="text/javascript">
29898  */
29899 /**
29900  * @class Roo.form.DisplayField
29901  * @extends Roo.form.Field
29902  * A generic Field to display non-editable data.
29903  * @constructor
29904  * Creates a new Display Field item.
29905  * @param {Object} config Configuration options
29906  */
29907 Roo.form.DisplayField = function(config){
29908     Roo.form.DisplayField.superclass.constructor.call(this, config);
29909     
29910 };
29911
29912 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29913     inputType:      'hidden',
29914     allowBlank:     true,
29915     readOnly:         true,
29916     
29917  
29918     /**
29919      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29920      */
29921     focusClass : undefined,
29922     /**
29923      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29924      */
29925     fieldClass: 'x-form-field',
29926     
29927      /**
29928      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29929      */
29930     valueRenderer: undefined,
29931     
29932     width: 100,
29933     /**
29934      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29935      * {tag: "input", type: "checkbox", autocomplete: "off"})
29936      */
29937      
29938  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29939
29940     onResize : function(){
29941         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29942         
29943     },
29944
29945     initEvents : function(){
29946         // Roo.form.Checkbox.superclass.initEvents.call(this);
29947         // has no events...
29948        
29949     },
29950
29951
29952     getResizeEl : function(){
29953         return this.wrap;
29954     },
29955
29956     getPositionEl : function(){
29957         return this.wrap;
29958     },
29959
29960     // private
29961     onRender : function(ct, position){
29962         
29963         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29964         //if(this.inputValue !== undefined){
29965         this.wrap = this.el.wrap();
29966         
29967         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29968         
29969         if (this.bodyStyle) {
29970             this.viewEl.applyStyles(this.bodyStyle);
29971         }
29972         //this.viewEl.setStyle('padding', '2px');
29973         
29974         this.setValue(this.value);
29975         
29976     },
29977 /*
29978     // private
29979     initValue : Roo.emptyFn,
29980
29981   */
29982
29983         // private
29984     onClick : function(){
29985         
29986     },
29987
29988     /**
29989      * Sets the checked state of the checkbox.
29990      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29991      */
29992     setValue : function(v){
29993         this.value = v;
29994         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29995         // this might be called before we have a dom element..
29996         if (!this.viewEl) {
29997             return;
29998         }
29999         this.viewEl.dom.innerHTML = html;
30000         Roo.form.DisplayField.superclass.setValue.call(this, v);
30001
30002     }
30003 });/*
30004  * 
30005  * Licence- LGPL
30006  * 
30007  */
30008
30009 /**
30010  * @class Roo.form.DayPicker
30011  * @extends Roo.form.Field
30012  * A Day picker show [M] [T] [W] ....
30013  * @constructor
30014  * Creates a new Day Picker
30015  * @param {Object} config Configuration options
30016  */
30017 Roo.form.DayPicker= function(config){
30018     Roo.form.DayPicker.superclass.constructor.call(this, config);
30019      
30020 };
30021
30022 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30023     /**
30024      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30025      */
30026     focusClass : undefined,
30027     /**
30028      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30029      */
30030     fieldClass: "x-form-field",
30031    
30032     /**
30033      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30034      * {tag: "input", type: "checkbox", autocomplete: "off"})
30035      */
30036     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30037     
30038    
30039     actionMode : 'viewEl', 
30040     //
30041     // private
30042  
30043     inputType : 'hidden',
30044     
30045      
30046     inputElement: false, // real input element?
30047     basedOn: false, // ????
30048     
30049     isFormField: true, // not sure where this is needed!!!!
30050
30051     onResize : function(){
30052         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30053         if(!this.boxLabel){
30054             this.el.alignTo(this.wrap, 'c-c');
30055         }
30056     },
30057
30058     initEvents : function(){
30059         Roo.form.Checkbox.superclass.initEvents.call(this);
30060         this.el.on("click", this.onClick,  this);
30061         this.el.on("change", this.onClick,  this);
30062     },
30063
30064
30065     getResizeEl : function(){
30066         return this.wrap;
30067     },
30068
30069     getPositionEl : function(){
30070         return this.wrap;
30071     },
30072
30073     
30074     // private
30075     onRender : function(ct, position){
30076         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30077        
30078         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30079         
30080         var r1 = '<table><tr>';
30081         var r2 = '<tr class="x-form-daypick-icons">';
30082         for (var i=0; i < 7; i++) {
30083             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30084             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30085         }
30086         
30087         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30088         viewEl.select('img').on('click', this.onClick, this);
30089         this.viewEl = viewEl;   
30090         
30091         
30092         // this will not work on Chrome!!!
30093         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30094         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30095         
30096         
30097           
30098
30099     },
30100
30101     // private
30102     initValue : Roo.emptyFn,
30103
30104     /**
30105      * Returns the checked state of the checkbox.
30106      * @return {Boolean} True if checked, else false
30107      */
30108     getValue : function(){
30109         return this.el.dom.value;
30110         
30111     },
30112
30113         // private
30114     onClick : function(e){ 
30115         //this.setChecked(!this.checked);
30116         Roo.get(e.target).toggleClass('x-menu-item-checked');
30117         this.refreshValue();
30118         //if(this.el.dom.checked != this.checked){
30119         //    this.setValue(this.el.dom.checked);
30120        // }
30121     },
30122     
30123     // private
30124     refreshValue : function()
30125     {
30126         var val = '';
30127         this.viewEl.select('img',true).each(function(e,i,n)  {
30128             val += e.is(".x-menu-item-checked") ? String(n) : '';
30129         });
30130         this.setValue(val, true);
30131     },
30132
30133     /**
30134      * Sets the checked state of the checkbox.
30135      * On is always based on a string comparison between inputValue and the param.
30136      * @param {Boolean/String} value - the value to set 
30137      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30138      */
30139     setValue : function(v,suppressEvent){
30140         if (!this.el.dom) {
30141             return;
30142         }
30143         var old = this.el.dom.value ;
30144         this.el.dom.value = v;
30145         if (suppressEvent) {
30146             return ;
30147         }
30148          
30149         // update display..
30150         this.viewEl.select('img',true).each(function(e,i,n)  {
30151             
30152             var on = e.is(".x-menu-item-checked");
30153             var newv = v.indexOf(String(n)) > -1;
30154             if (on != newv) {
30155                 e.toggleClass('x-menu-item-checked');
30156             }
30157             
30158         });
30159         
30160         
30161         this.fireEvent('change', this, v, old);
30162         
30163         
30164     },
30165    
30166     // handle setting of hidden value by some other method!!?!?
30167     setFromHidden: function()
30168     {
30169         if(!this.el){
30170             return;
30171         }
30172         //console.log("SET FROM HIDDEN");
30173         //alert('setFrom hidden');
30174         this.setValue(this.el.dom.value);
30175     },
30176     
30177     onDestroy : function()
30178     {
30179         if(this.viewEl){
30180             Roo.get(this.viewEl).remove();
30181         }
30182          
30183         Roo.form.DayPicker.superclass.onDestroy.call(this);
30184     }
30185
30186 });/*
30187  * RooJS Library 1.1.1
30188  * Copyright(c) 2008-2011  Alan Knowles
30189  *
30190  * License - LGPL
30191  */
30192  
30193
30194 /**
30195  * @class Roo.form.ComboCheck
30196  * @extends Roo.form.ComboBox
30197  * A combobox for multiple select items.
30198  *
30199  * FIXME - could do with a reset button..
30200  * 
30201  * @constructor
30202  * Create a new ComboCheck
30203  * @param {Object} config Configuration options
30204  */
30205 Roo.form.ComboCheck = function(config){
30206     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30207     // should verify some data...
30208     // like
30209     // hiddenName = required..
30210     // displayField = required
30211     // valudField == required
30212     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30213     var _t = this;
30214     Roo.each(req, function(e) {
30215         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30216             throw "Roo.form.ComboCheck : missing value for: " + e;
30217         }
30218     });
30219     
30220     
30221 };
30222
30223 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30224      
30225      
30226     editable : false,
30227      
30228     selectedClass: 'x-menu-item-checked', 
30229     
30230     // private
30231     onRender : function(ct, position){
30232         var _t = this;
30233         
30234         
30235         
30236         if(!this.tpl){
30237             var cls = 'x-combo-list';
30238
30239             
30240             this.tpl =  new Roo.Template({
30241                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30242                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30243                    '<span>{' + this.displayField + '}</span>' +
30244                     '</div>' 
30245                 
30246             });
30247         }
30248  
30249         
30250         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30251         this.view.singleSelect = false;
30252         this.view.multiSelect = true;
30253         this.view.toggleSelect = true;
30254         this.pageTb.add(new Roo.Toolbar.Fill(), {
30255             
30256             text: 'Done',
30257             handler: function()
30258             {
30259                 _t.collapse();
30260             }
30261         });
30262     },
30263     
30264     onViewOver : function(e, t){
30265         // do nothing...
30266         return;
30267         
30268     },
30269     
30270     onViewClick : function(doFocus,index){
30271         return;
30272         
30273     },
30274     select: function () {
30275         //Roo.log("SELECT CALLED");
30276     },
30277      
30278     selectByValue : function(xv, scrollIntoView){
30279         var ar = this.getValueArray();
30280         var sels = [];
30281         
30282         Roo.each(ar, function(v) {
30283             if(v === undefined || v === null){
30284                 return;
30285             }
30286             var r = this.findRecord(this.valueField, v);
30287             if(r){
30288                 sels.push(this.store.indexOf(r))
30289                 
30290             }
30291         },this);
30292         this.view.select(sels);
30293         return false;
30294     },
30295     
30296     
30297     
30298     onSelect : function(record, index){
30299        // Roo.log("onselect Called");
30300        // this is only called by the clear button now..
30301         this.view.clearSelections();
30302         this.setValue('[]');
30303         if (this.value != this.valueBefore) {
30304             this.fireEvent('change', this, this.value, this.valueBefore);
30305         }
30306     },
30307     getValueArray : function()
30308     {
30309         var ar = [] ;
30310         
30311         try {
30312             //Roo.log(this.value);
30313             if (typeof(this.value) == 'undefined') {
30314                 return [];
30315             }
30316             var ar = Roo.decode(this.value);
30317             return  ar instanceof Array ? ar : []; //?? valid?
30318             
30319         } catch(e) {
30320             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30321             return [];
30322         }
30323          
30324     },
30325     expand : function ()
30326     {
30327         Roo.form.ComboCheck.superclass.expand.call(this);
30328         this.valueBefore = this.value;
30329         
30330
30331     },
30332     
30333     collapse : function(){
30334         Roo.form.ComboCheck.superclass.collapse.call(this);
30335         var sl = this.view.getSelectedIndexes();
30336         var st = this.store;
30337         var nv = [];
30338         var tv = [];
30339         var r;
30340         Roo.each(sl, function(i) {
30341             r = st.getAt(i);
30342             nv.push(r.get(this.valueField));
30343         },this);
30344         this.setValue(Roo.encode(nv));
30345         if (this.value != this.valueBefore) {
30346
30347             this.fireEvent('change', this, this.value, this.valueBefore);
30348         }
30349         
30350     },
30351     
30352     setValue : function(v){
30353         // Roo.log(v);
30354         this.value = v;
30355         
30356         var vals = this.getValueArray();
30357         var tv = [];
30358         Roo.each(vals, function(k) {
30359             var r = this.findRecord(this.valueField, k);
30360             if(r){
30361                 tv.push(r.data[this.displayField]);
30362             }else if(this.valueNotFoundText !== undefined){
30363                 tv.push( this.valueNotFoundText );
30364             }
30365         },this);
30366        // Roo.log(tv);
30367         
30368         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30369         this.hiddenField.value = v;
30370         this.value = v;
30371     }
30372     
30373 });//<script type="text/javasscript">
30374  
30375
30376 /**
30377  * @class Roo.DDView
30378  * A DnD enabled version of Roo.View.
30379  * @param {Element/String} container The Element in which to create the View.
30380  * @param {String} tpl The template string used to create the markup for each element of the View
30381  * @param {Object} config The configuration properties. These include all the config options of
30382  * {@link Roo.View} plus some specific to this class.<br>
30383  * <p>
30384  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30385  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30386  * <p>
30387  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30388 .x-view-drag-insert-above {
30389         border-top:1px dotted #3366cc;
30390 }
30391 .x-view-drag-insert-below {
30392         border-bottom:1px dotted #3366cc;
30393 }
30394 </code></pre>
30395  * 
30396  */
30397  
30398 Roo.DDView = function(container, tpl, config) {
30399     Roo.DDView.superclass.constructor.apply(this, arguments);
30400     this.getEl().setStyle("outline", "0px none");
30401     this.getEl().unselectable();
30402     if (this.dragGroup) {
30403                 this.setDraggable(this.dragGroup.split(","));
30404     }
30405     if (this.dropGroup) {
30406                 this.setDroppable(this.dropGroup.split(","));
30407     }
30408     if (this.deletable) {
30409         this.setDeletable();
30410     }
30411     this.isDirtyFlag = false;
30412         this.addEvents({
30413                 "drop" : true
30414         });
30415 };
30416
30417 Roo.extend(Roo.DDView, Roo.View, {
30418 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30419 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30420 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30421 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30422
30423         isFormField: true,
30424
30425         reset: Roo.emptyFn,
30426         
30427         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30428
30429         validate: function() {
30430                 return true;
30431         },
30432         
30433         destroy: function() {
30434                 this.purgeListeners();
30435                 this.getEl.removeAllListeners();
30436                 this.getEl().remove();
30437                 if (this.dragZone) {
30438                         if (this.dragZone.destroy) {
30439                                 this.dragZone.destroy();
30440                         }
30441                 }
30442                 if (this.dropZone) {
30443                         if (this.dropZone.destroy) {
30444                                 this.dropZone.destroy();
30445                         }
30446                 }
30447         },
30448
30449 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30450         getName: function() {
30451                 return this.name;
30452         },
30453
30454 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30455         setValue: function(v) {
30456                 if (!this.store) {
30457                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30458                 }
30459                 var data = {};
30460                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30461                 this.store.proxy = new Roo.data.MemoryProxy(data);
30462                 this.store.load();
30463         },
30464
30465 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30466         getValue: function() {
30467                 var result = '(';
30468                 this.store.each(function(rec) {
30469                         result += rec.id + ',';
30470                 });
30471                 return result.substr(0, result.length - 1) + ')';
30472         },
30473         
30474         getIds: function() {
30475                 var i = 0, result = new Array(this.store.getCount());
30476                 this.store.each(function(rec) {
30477                         result[i++] = rec.id;
30478                 });
30479                 return result;
30480         },
30481         
30482         isDirty: function() {
30483                 return this.isDirtyFlag;
30484         },
30485
30486 /**
30487  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30488  *      whole Element becomes the target, and this causes the drop gesture to append.
30489  */
30490     getTargetFromEvent : function(e) {
30491                 var target = e.getTarget();
30492                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30493                 target = target.parentNode;
30494                 }
30495                 if (!target) {
30496                         target = this.el.dom.lastChild || this.el.dom;
30497                 }
30498                 return target;
30499     },
30500
30501 /**
30502  *      Create the drag data which consists of an object which has the property "ddel" as
30503  *      the drag proxy element. 
30504  */
30505     getDragData : function(e) {
30506         var target = this.findItemFromChild(e.getTarget());
30507                 if(target) {
30508                         this.handleSelection(e);
30509                         var selNodes = this.getSelectedNodes();
30510             var dragData = {
30511                 source: this,
30512                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30513                 nodes: selNodes,
30514                 records: []
30515                         };
30516                         var selectedIndices = this.getSelectedIndexes();
30517                         for (var i = 0; i < selectedIndices.length; i++) {
30518                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30519                         }
30520                         if (selNodes.length == 1) {
30521                                 dragData.ddel = target.cloneNode(true); // the div element
30522                         } else {
30523                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30524                                 div.className = 'multi-proxy';
30525                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30526                                         div.appendChild(selNodes[i].cloneNode(true));
30527                                 }
30528                                 dragData.ddel = div;
30529                         }
30530             //console.log(dragData)
30531             //console.log(dragData.ddel.innerHTML)
30532                         return dragData;
30533                 }
30534         //console.log('nodragData')
30535                 return false;
30536     },
30537     
30538 /**     Specify to which ddGroup items in this DDView may be dragged. */
30539     setDraggable: function(ddGroup) {
30540         if (ddGroup instanceof Array) {
30541                 Roo.each(ddGroup, this.setDraggable, this);
30542                 return;
30543         }
30544         if (this.dragZone) {
30545                 this.dragZone.addToGroup(ddGroup);
30546         } else {
30547                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30548                                 containerScroll: true,
30549                                 ddGroup: ddGroup 
30550
30551                         });
30552 //                      Draggability implies selection. DragZone's mousedown selects the element.
30553                         if (!this.multiSelect) { this.singleSelect = true; }
30554
30555 //                      Wire the DragZone's handlers up to methods in *this*
30556                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30557                 }
30558     },
30559
30560 /**     Specify from which ddGroup this DDView accepts drops. */
30561     setDroppable: function(ddGroup) {
30562         if (ddGroup instanceof Array) {
30563                 Roo.each(ddGroup, this.setDroppable, this);
30564                 return;
30565         }
30566         if (this.dropZone) {
30567                 this.dropZone.addToGroup(ddGroup);
30568         } else {
30569                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30570                                 containerScroll: true,
30571                                 ddGroup: ddGroup
30572                         });
30573
30574 //                      Wire the DropZone's handlers up to methods in *this*
30575                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30576                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30577                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30578                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30579                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30580                 }
30581     },
30582
30583 /**     Decide whether to drop above or below a View node. */
30584     getDropPoint : function(e, n, dd){
30585         if (n == this.el.dom) { return "above"; }
30586                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30587                 var c = t + (b - t) / 2;
30588                 var y = Roo.lib.Event.getPageY(e);
30589                 if(y <= c) {
30590                         return "above";
30591                 }else{
30592                         return "below";
30593                 }
30594     },
30595
30596     onNodeEnter : function(n, dd, e, data){
30597                 return false;
30598     },
30599     
30600     onNodeOver : function(n, dd, e, data){
30601                 var pt = this.getDropPoint(e, n, dd);
30602                 // set the insert point style on the target node
30603                 var dragElClass = this.dropNotAllowed;
30604                 if (pt) {
30605                         var targetElClass;
30606                         if (pt == "above"){
30607                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30608                                 targetElClass = "x-view-drag-insert-above";
30609                         } else {
30610                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30611                                 targetElClass = "x-view-drag-insert-below";
30612                         }
30613                         if (this.lastInsertClass != targetElClass){
30614                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30615                                 this.lastInsertClass = targetElClass;
30616                         }
30617                 }
30618                 return dragElClass;
30619         },
30620
30621     onNodeOut : function(n, dd, e, data){
30622                 this.removeDropIndicators(n);
30623     },
30624
30625     onNodeDrop : function(n, dd, e, data){
30626         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30627                 return false;
30628         }
30629         var pt = this.getDropPoint(e, n, dd);
30630                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30631                 if (pt == "below") { insertAt++; }
30632                 for (var i = 0; i < data.records.length; i++) {
30633                         var r = data.records[i];
30634                         var dup = this.store.getById(r.id);
30635                         if (dup && (dd != this.dragZone)) {
30636                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30637                         } else {
30638                                 if (data.copy) {
30639                                         this.store.insert(insertAt++, r.copy());
30640                                 } else {
30641                                         data.source.isDirtyFlag = true;
30642                                         r.store.remove(r);
30643                                         this.store.insert(insertAt++, r);
30644                                 }
30645                                 this.isDirtyFlag = true;
30646                         }
30647                 }
30648                 this.dragZone.cachedTarget = null;
30649                 return true;
30650     },
30651
30652     removeDropIndicators : function(n){
30653                 if(n){
30654                         Roo.fly(n).removeClass([
30655                                 "x-view-drag-insert-above",
30656                                 "x-view-drag-insert-below"]);
30657                         this.lastInsertClass = "_noclass";
30658                 }
30659     },
30660
30661 /**
30662  *      Utility method. Add a delete option to the DDView's context menu.
30663  *      @param {String} imageUrl The URL of the "delete" icon image.
30664  */
30665         setDeletable: function(imageUrl) {
30666                 if (!this.singleSelect && !this.multiSelect) {
30667                         this.singleSelect = true;
30668                 }
30669                 var c = this.getContextMenu();
30670                 this.contextMenu.on("itemclick", function(item) {
30671                         switch (item.id) {
30672                                 case "delete":
30673                                         this.remove(this.getSelectedIndexes());
30674                                         break;
30675                         }
30676                 }, this);
30677                 this.contextMenu.add({
30678                         icon: imageUrl,
30679                         id: "delete",
30680                         text: 'Delete'
30681                 });
30682         },
30683         
30684 /**     Return the context menu for this DDView. */
30685         getContextMenu: function() {
30686                 if (!this.contextMenu) {
30687 //                      Create the View's context menu
30688                         this.contextMenu = new Roo.menu.Menu({
30689                                 id: this.id + "-contextmenu"
30690                         });
30691                         this.el.on("contextmenu", this.showContextMenu, this);
30692                 }
30693                 return this.contextMenu;
30694         },
30695         
30696         disableContextMenu: function() {
30697                 if (this.contextMenu) {
30698                         this.el.un("contextmenu", this.showContextMenu, this);
30699                 }
30700         },
30701
30702         showContextMenu: function(e, item) {
30703         item = this.findItemFromChild(e.getTarget());
30704                 if (item) {
30705                         e.stopEvent();
30706                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30707                         this.contextMenu.showAt(e.getXY());
30708             }
30709     },
30710
30711 /**
30712  *      Remove {@link Roo.data.Record}s at the specified indices.
30713  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30714  */
30715     remove: function(selectedIndices) {
30716                 selectedIndices = [].concat(selectedIndices);
30717                 for (var i = 0; i < selectedIndices.length; i++) {
30718                         var rec = this.store.getAt(selectedIndices[i]);
30719                         this.store.remove(rec);
30720                 }
30721     },
30722
30723 /**
30724  *      Double click fires the event, but also, if this is draggable, and there is only one other
30725  *      related DropZone, it transfers the selected node.
30726  */
30727     onDblClick : function(e){
30728         var item = this.findItemFromChild(e.getTarget());
30729         if(item){
30730             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30731                 return false;
30732             }
30733             if (this.dragGroup) {
30734                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30735                     while (targets.indexOf(this.dropZone) > -1) {
30736                             targets.remove(this.dropZone);
30737                                 }
30738                     if (targets.length == 1) {
30739                                         this.dragZone.cachedTarget = null;
30740                         var el = Roo.get(targets[0].getEl());
30741                         var box = el.getBox(true);
30742                         targets[0].onNodeDrop(el.dom, {
30743                                 target: el.dom,
30744                                 xy: [box.x, box.y + box.height - 1]
30745                         }, null, this.getDragData(e));
30746                     }
30747                 }
30748         }
30749     },
30750     
30751     handleSelection: function(e) {
30752                 this.dragZone.cachedTarget = null;
30753         var item = this.findItemFromChild(e.getTarget());
30754         if (!item) {
30755                 this.clearSelections(true);
30756                 return;
30757         }
30758                 if (item && (this.multiSelect || this.singleSelect)){
30759                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30760                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30761                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30762                                 this.unselect(item);
30763                         } else {
30764                                 this.select(item, this.multiSelect && e.ctrlKey);
30765                                 this.lastSelection = item;
30766                         }
30767                 }
30768     },
30769
30770     onItemClick : function(item, index, e){
30771                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30772                         return false;
30773                 }
30774                 return true;
30775     },
30776
30777     unselect : function(nodeInfo, suppressEvent){
30778                 var node = this.getNode(nodeInfo);
30779                 if(node && this.isSelected(node)){
30780                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30781                                 Roo.fly(node).removeClass(this.selectedClass);
30782                                 this.selections.remove(node);
30783                                 if(!suppressEvent){
30784                                         this.fireEvent("selectionchange", this, this.selections);
30785                                 }
30786                         }
30787                 }
30788     }
30789 });
30790 /*
30791  * Based on:
30792  * Ext JS Library 1.1.1
30793  * Copyright(c) 2006-2007, Ext JS, LLC.
30794  *
30795  * Originally Released Under LGPL - original licence link has changed is not relivant.
30796  *
30797  * Fork - LGPL
30798  * <script type="text/javascript">
30799  */
30800  
30801 /**
30802  * @class Roo.LayoutManager
30803  * @extends Roo.util.Observable
30804  * Base class for layout managers.
30805  */
30806 Roo.LayoutManager = function(container, config){
30807     Roo.LayoutManager.superclass.constructor.call(this);
30808     this.el = Roo.get(container);
30809     // ie scrollbar fix
30810     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30811         document.body.scroll = "no";
30812     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30813         this.el.position('relative');
30814     }
30815     this.id = this.el.id;
30816     this.el.addClass("x-layout-container");
30817     /** false to disable window resize monitoring @type Boolean */
30818     this.monitorWindowResize = true;
30819     this.regions = {};
30820     this.addEvents({
30821         /**
30822          * @event layout
30823          * Fires when a layout is performed. 
30824          * @param {Roo.LayoutManager} this
30825          */
30826         "layout" : true,
30827         /**
30828          * @event regionresized
30829          * Fires when the user resizes a region. 
30830          * @param {Roo.LayoutRegion} region The resized region
30831          * @param {Number} newSize The new size (width for east/west, height for north/south)
30832          */
30833         "regionresized" : true,
30834         /**
30835          * @event regioncollapsed
30836          * Fires when a region is collapsed. 
30837          * @param {Roo.LayoutRegion} region The collapsed region
30838          */
30839         "regioncollapsed" : true,
30840         /**
30841          * @event regionexpanded
30842          * Fires when a region is expanded.  
30843          * @param {Roo.LayoutRegion} region The expanded region
30844          */
30845         "regionexpanded" : true
30846     });
30847     this.updating = false;
30848     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30849 };
30850
30851 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30852     /**
30853      * Returns true if this layout is currently being updated
30854      * @return {Boolean}
30855      */
30856     isUpdating : function(){
30857         return this.updating; 
30858     },
30859     
30860     /**
30861      * Suspend the LayoutManager from doing auto-layouts while
30862      * making multiple add or remove calls
30863      */
30864     beginUpdate : function(){
30865         this.updating = true;    
30866     },
30867     
30868     /**
30869      * Restore auto-layouts and optionally disable the manager from performing a layout
30870      * @param {Boolean} noLayout true to disable a layout update 
30871      */
30872     endUpdate : function(noLayout){
30873         this.updating = false;
30874         if(!noLayout){
30875             this.layout();
30876         }    
30877     },
30878     
30879     layout: function(){
30880         
30881     },
30882     
30883     onRegionResized : function(region, newSize){
30884         this.fireEvent("regionresized", region, newSize);
30885         this.layout();
30886     },
30887     
30888     onRegionCollapsed : function(region){
30889         this.fireEvent("regioncollapsed", region);
30890     },
30891     
30892     onRegionExpanded : function(region){
30893         this.fireEvent("regionexpanded", region);
30894     },
30895         
30896     /**
30897      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30898      * performs box-model adjustments.
30899      * @return {Object} The size as an object {width: (the width), height: (the height)}
30900      */
30901     getViewSize : function(){
30902         var size;
30903         if(this.el.dom != document.body){
30904             size = this.el.getSize();
30905         }else{
30906             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30907         }
30908         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30909         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30910         return size;
30911     },
30912     
30913     /**
30914      * Returns the Element this layout is bound to.
30915      * @return {Roo.Element}
30916      */
30917     getEl : function(){
30918         return this.el;
30919     },
30920     
30921     /**
30922      * Returns the specified region.
30923      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30924      * @return {Roo.LayoutRegion}
30925      */
30926     getRegion : function(target){
30927         return this.regions[target.toLowerCase()];
30928     },
30929     
30930     onWindowResize : function(){
30931         if(this.monitorWindowResize){
30932             this.layout();
30933         }
30934     }
30935 });/*
30936  * Based on:
30937  * Ext JS Library 1.1.1
30938  * Copyright(c) 2006-2007, Ext JS, LLC.
30939  *
30940  * Originally Released Under LGPL - original licence link has changed is not relivant.
30941  *
30942  * Fork - LGPL
30943  * <script type="text/javascript">
30944  */
30945 /**
30946  * @class Roo.BorderLayout
30947  * @extends Roo.LayoutManager
30948  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30949  * please see: <br><br>
30950  * <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>
30951  * <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>
30952  * Example:
30953  <pre><code>
30954  var layout = new Roo.BorderLayout(document.body, {
30955     north: {
30956         initialSize: 25,
30957         titlebar: false
30958     },
30959     west: {
30960         split:true,
30961         initialSize: 200,
30962         minSize: 175,
30963         maxSize: 400,
30964         titlebar: true,
30965         collapsible: true
30966     },
30967     east: {
30968         split:true,
30969         initialSize: 202,
30970         minSize: 175,
30971         maxSize: 400,
30972         titlebar: true,
30973         collapsible: true
30974     },
30975     south: {
30976         split:true,
30977         initialSize: 100,
30978         minSize: 100,
30979         maxSize: 200,
30980         titlebar: true,
30981         collapsible: true
30982     },
30983     center: {
30984         titlebar: true,
30985         autoScroll:true,
30986         resizeTabs: true,
30987         minTabWidth: 50,
30988         preferredTabWidth: 150
30989     }
30990 });
30991
30992 // shorthand
30993 var CP = Roo.ContentPanel;
30994
30995 layout.beginUpdate();
30996 layout.add("north", new CP("north", "North"));
30997 layout.add("south", new CP("south", {title: "South", closable: true}));
30998 layout.add("west", new CP("west", {title: "West"}));
30999 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
31000 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
31001 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
31002 layout.getRegion("center").showPanel("center1");
31003 layout.endUpdate();
31004 </code></pre>
31005
31006 <b>The container the layout is rendered into can be either the body element or any other element.
31007 If it is not the body element, the container needs to either be an absolute positioned element,
31008 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31009 the container size if it is not the body element.</b>
31010
31011 * @constructor
31012 * Create a new BorderLayout
31013 * @param {String/HTMLElement/Element} container The container this layout is bound to
31014 * @param {Object} config Configuration options
31015  */
31016 Roo.BorderLayout = function(container, config){
31017     config = config || {};
31018     Roo.BorderLayout.superclass.constructor.call(this, container, config);
31019     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
31020     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
31021         var target = this.factory.validRegions[i];
31022         if(config[target]){
31023             this.addRegion(target, config[target]);
31024         }
31025     }
31026 };
31027
31028 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31029     /**
31030      * Creates and adds a new region if it doesn't already exist.
31031      * @param {String} target The target region key (north, south, east, west or center).
31032      * @param {Object} config The regions config object
31033      * @return {BorderLayoutRegion} The new region
31034      */
31035     addRegion : function(target, config){
31036         if(!this.regions[target]){
31037             var r = this.factory.create(target, this, config);
31038             this.bindRegion(target, r);
31039         }
31040         return this.regions[target];
31041     },
31042
31043     // private (kinda)
31044     bindRegion : function(name, r){
31045         this.regions[name] = r;
31046         r.on("visibilitychange", this.layout, this);
31047         r.on("paneladded", this.layout, this);
31048         r.on("panelremoved", this.layout, this);
31049         r.on("invalidated", this.layout, this);
31050         r.on("resized", this.onRegionResized, this);
31051         r.on("collapsed", this.onRegionCollapsed, this);
31052         r.on("expanded", this.onRegionExpanded, this);
31053     },
31054
31055     /**
31056      * Performs a layout update.
31057      */
31058     layout : function(){
31059         if(this.updating) return;
31060         var size = this.getViewSize();
31061         var w = size.width;
31062         var h = size.height;
31063         var centerW = w;
31064         var centerH = h;
31065         var centerY = 0;
31066         var centerX = 0;
31067         //var x = 0, y = 0;
31068
31069         var rs = this.regions;
31070         var north = rs["north"];
31071         var south = rs["south"]; 
31072         var west = rs["west"];
31073         var east = rs["east"];
31074         var center = rs["center"];
31075         //if(this.hideOnLayout){ // not supported anymore
31076             //c.el.setStyle("display", "none");
31077         //}
31078         if(north && north.isVisible()){
31079             var b = north.getBox();
31080             var m = north.getMargins();
31081             b.width = w - (m.left+m.right);
31082             b.x = m.left;
31083             b.y = m.top;
31084             centerY = b.height + b.y + m.bottom;
31085             centerH -= centerY;
31086             north.updateBox(this.safeBox(b));
31087         }
31088         if(south && south.isVisible()){
31089             var b = south.getBox();
31090             var m = south.getMargins();
31091             b.width = w - (m.left+m.right);
31092             b.x = m.left;
31093             var totalHeight = (b.height + m.top + m.bottom);
31094             b.y = h - totalHeight + m.top;
31095             centerH -= totalHeight;
31096             south.updateBox(this.safeBox(b));
31097         }
31098         if(west && west.isVisible()){
31099             var b = west.getBox();
31100             var m = west.getMargins();
31101             b.height = centerH - (m.top+m.bottom);
31102             b.x = m.left;
31103             b.y = centerY + m.top;
31104             var totalWidth = (b.width + m.left + m.right);
31105             centerX += totalWidth;
31106             centerW -= totalWidth;
31107             west.updateBox(this.safeBox(b));
31108         }
31109         if(east && east.isVisible()){
31110             var b = east.getBox();
31111             var m = east.getMargins();
31112             b.height = centerH - (m.top+m.bottom);
31113             var totalWidth = (b.width + m.left + m.right);
31114             b.x = w - totalWidth + m.left;
31115             b.y = centerY + m.top;
31116             centerW -= totalWidth;
31117             east.updateBox(this.safeBox(b));
31118         }
31119         if(center){
31120             var m = center.getMargins();
31121             var centerBox = {
31122                 x: centerX + m.left,
31123                 y: centerY + m.top,
31124                 width: centerW - (m.left+m.right),
31125                 height: centerH - (m.top+m.bottom)
31126             };
31127             //if(this.hideOnLayout){
31128                 //center.el.setStyle("display", "block");
31129             //}
31130             center.updateBox(this.safeBox(centerBox));
31131         }
31132         this.el.repaint();
31133         this.fireEvent("layout", this);
31134     },
31135
31136     // private
31137     safeBox : function(box){
31138         box.width = Math.max(0, box.width);
31139         box.height = Math.max(0, box.height);
31140         return box;
31141     },
31142
31143     /**
31144      * Adds a ContentPanel (or subclass) to this layout.
31145      * @param {String} target The target region key (north, south, east, west or center).
31146      * @param {Roo.ContentPanel} panel The panel to add
31147      * @return {Roo.ContentPanel} The added panel
31148      */
31149     add : function(target, panel){
31150          
31151         target = target.toLowerCase();
31152         return this.regions[target].add(panel);
31153     },
31154
31155     /**
31156      * Remove a ContentPanel (or subclass) to this layout.
31157      * @param {String} target The target region key (north, south, east, west or center).
31158      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31159      * @return {Roo.ContentPanel} The removed panel
31160      */
31161     remove : function(target, panel){
31162         target = target.toLowerCase();
31163         return this.regions[target].remove(panel);
31164     },
31165
31166     /**
31167      * Searches all regions for a panel with the specified id
31168      * @param {String} panelId
31169      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31170      */
31171     findPanel : function(panelId){
31172         var rs = this.regions;
31173         for(var target in rs){
31174             if(typeof rs[target] != "function"){
31175                 var p = rs[target].getPanel(panelId);
31176                 if(p){
31177                     return p;
31178                 }
31179             }
31180         }
31181         return null;
31182     },
31183
31184     /**
31185      * Searches all regions for a panel with the specified id and activates (shows) it.
31186      * @param {String/ContentPanel} panelId The panels id or the panel itself
31187      * @return {Roo.ContentPanel} The shown panel or null
31188      */
31189     showPanel : function(panelId) {
31190       var rs = this.regions;
31191       for(var target in rs){
31192          var r = rs[target];
31193          if(typeof r != "function"){
31194             if(r.hasPanel(panelId)){
31195                return r.showPanel(panelId);
31196             }
31197          }
31198       }
31199       return null;
31200    },
31201
31202    /**
31203      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31204      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31205      */
31206     restoreState : function(provider){
31207         if(!provider){
31208             provider = Roo.state.Manager;
31209         }
31210         var sm = new Roo.LayoutStateManager();
31211         sm.init(this, provider);
31212     },
31213
31214     /**
31215      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31216      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31217      * a valid ContentPanel config object.  Example:
31218      * <pre><code>
31219 // Create the main layout
31220 var layout = new Roo.BorderLayout('main-ct', {
31221     west: {
31222         split:true,
31223         minSize: 175,
31224         titlebar: true
31225     },
31226     center: {
31227         title:'Components'
31228     }
31229 }, 'main-ct');
31230
31231 // Create and add multiple ContentPanels at once via configs
31232 layout.batchAdd({
31233    west: {
31234        id: 'source-files',
31235        autoCreate:true,
31236        title:'Ext Source Files',
31237        autoScroll:true,
31238        fitToFrame:true
31239    },
31240    center : {
31241        el: cview,
31242        autoScroll:true,
31243        fitToFrame:true,
31244        toolbar: tb,
31245        resizeEl:'cbody'
31246    }
31247 });
31248 </code></pre>
31249      * @param {Object} regions An object containing ContentPanel configs by region name
31250      */
31251     batchAdd : function(regions){
31252         this.beginUpdate();
31253         for(var rname in regions){
31254             var lr = this.regions[rname];
31255             if(lr){
31256                 this.addTypedPanels(lr, regions[rname]);
31257             }
31258         }
31259         this.endUpdate();
31260     },
31261
31262     // private
31263     addTypedPanels : function(lr, ps){
31264         if(typeof ps == 'string'){
31265             lr.add(new Roo.ContentPanel(ps));
31266         }
31267         else if(ps instanceof Array){
31268             for(var i =0, len = ps.length; i < len; i++){
31269                 this.addTypedPanels(lr, ps[i]);
31270             }
31271         }
31272         else if(!ps.events){ // raw config?
31273             var el = ps.el;
31274             delete ps.el; // prevent conflict
31275             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31276         }
31277         else {  // panel object assumed!
31278             lr.add(ps);
31279         }
31280     },
31281     /**
31282      * Adds a xtype elements to the layout.
31283      * <pre><code>
31284
31285 layout.addxtype({
31286        xtype : 'ContentPanel',
31287        region: 'west',
31288        items: [ .... ]
31289    }
31290 );
31291
31292 layout.addxtype({
31293         xtype : 'NestedLayoutPanel',
31294         region: 'west',
31295         layout: {
31296            center: { },
31297            west: { }   
31298         },
31299         items : [ ... list of content panels or nested layout panels.. ]
31300    }
31301 );
31302 </code></pre>
31303      * @param {Object} cfg Xtype definition of item to add.
31304      */
31305     addxtype : function(cfg)
31306     {
31307         // basically accepts a pannel...
31308         // can accept a layout region..!?!?
31309         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31310         
31311         if (!cfg.xtype.match(/Panel$/)) {
31312             return false;
31313         }
31314         var ret = false;
31315         
31316         if (typeof(cfg.region) == 'undefined') {
31317             Roo.log("Failed to add Panel, region was not set");
31318             Roo.log(cfg);
31319             return false;
31320         }
31321         var region = cfg.region;
31322         delete cfg.region;
31323         
31324           
31325         var xitems = [];
31326         if (cfg.items) {
31327             xitems = cfg.items;
31328             delete cfg.items;
31329         }
31330         var nb = false;
31331         
31332         switch(cfg.xtype) 
31333         {
31334             case 'ContentPanel':  // ContentPanel (el, cfg)
31335             case 'ScrollPanel':  // ContentPanel (el, cfg)
31336                 if(cfg.autoCreate) {
31337                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31338                 } else {
31339                     var el = this.el.createChild();
31340                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31341                 }
31342                 
31343                 this.add(region, ret);
31344                 break;
31345             
31346             
31347             case 'TreePanel': // our new panel!
31348                 cfg.el = this.el.createChild();
31349                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31350                 this.add(region, ret);
31351                 break;
31352             
31353             case 'NestedLayoutPanel': 
31354                 // create a new Layout (which is  a Border Layout...
31355                 var el = this.el.createChild();
31356                 var clayout = cfg.layout;
31357                 delete cfg.layout;
31358                 clayout.items   = clayout.items  || [];
31359                 // replace this exitems with the clayout ones..
31360                 xitems = clayout.items;
31361                  
31362                 
31363                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31364                     cfg.background = false;
31365                 }
31366                 var layout = new Roo.BorderLayout(el, clayout);
31367                 
31368                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31369                 //console.log('adding nested layout panel '  + cfg.toSource());
31370                 this.add(region, ret);
31371                 nb = {}; /// find first...
31372                 break;
31373                 
31374             case 'GridPanel': 
31375             
31376                 // needs grid and region
31377                 
31378                 //var el = this.getRegion(region).el.createChild();
31379                 var el = this.el.createChild();
31380                 // create the grid first...
31381                 
31382                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31383                 delete cfg.grid;
31384                 if (region == 'center' && this.active ) {
31385                     cfg.background = false;
31386                 }
31387                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31388                 
31389                 this.add(region, ret);
31390                 if (cfg.background) {
31391                     ret.on('activate', function(gp) {
31392                         if (!gp.grid.rendered) {
31393                             gp.grid.render();
31394                         }
31395                     });
31396                 } else {
31397                     grid.render();
31398                 }
31399                 break;
31400            
31401                
31402                 
31403                 
31404             default: 
31405                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31406                 return null;
31407              // GridPanel (grid, cfg)
31408             
31409         }
31410         this.beginUpdate();
31411         // add children..
31412         var region = '';
31413         var abn = {};
31414         Roo.each(xitems, function(i)  {
31415             region = nb && i.region ? i.region : false;
31416             
31417             var add = ret.addxtype(i);
31418            
31419             if (region) {
31420                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31421                 if (!i.background) {
31422                     abn[region] = nb[region] ;
31423                 }
31424             }
31425             
31426         });
31427         this.endUpdate();
31428
31429         // make the last non-background panel active..
31430         //if (nb) { Roo.log(abn); }
31431         if (nb) {
31432             
31433             for(var r in abn) {
31434                 region = this.getRegion(r);
31435                 if (region) {
31436                     // tried using nb[r], but it does not work..
31437                      
31438                     region.showPanel(abn[r]);
31439                    
31440                 }
31441             }
31442         }
31443         return ret;
31444         
31445     }
31446 });
31447
31448 /**
31449  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31450  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31451  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31452  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31453  * <pre><code>
31454 // shorthand
31455 var CP = Roo.ContentPanel;
31456
31457 var layout = Roo.BorderLayout.create({
31458     north: {
31459         initialSize: 25,
31460         titlebar: false,
31461         panels: [new CP("north", "North")]
31462     },
31463     west: {
31464         split:true,
31465         initialSize: 200,
31466         minSize: 175,
31467         maxSize: 400,
31468         titlebar: true,
31469         collapsible: true,
31470         panels: [new CP("west", {title: "West"})]
31471     },
31472     east: {
31473         split:true,
31474         initialSize: 202,
31475         minSize: 175,
31476         maxSize: 400,
31477         titlebar: true,
31478         collapsible: true,
31479         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31480     },
31481     south: {
31482         split:true,
31483         initialSize: 100,
31484         minSize: 100,
31485         maxSize: 200,
31486         titlebar: true,
31487         collapsible: true,
31488         panels: [new CP("south", {title: "South", closable: true})]
31489     },
31490     center: {
31491         titlebar: true,
31492         autoScroll:true,
31493         resizeTabs: true,
31494         minTabWidth: 50,
31495         preferredTabWidth: 150,
31496         panels: [
31497             new CP("center1", {title: "Close Me", closable: true}),
31498             new CP("center2", {title: "Center Panel", closable: false})
31499         ]
31500     }
31501 }, document.body);
31502
31503 layout.getRegion("center").showPanel("center1");
31504 </code></pre>
31505  * @param config
31506  * @param targetEl
31507  */
31508 Roo.BorderLayout.create = function(config, targetEl){
31509     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31510     layout.beginUpdate();
31511     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31512     for(var j = 0, jlen = regions.length; j < jlen; j++){
31513         var lr = regions[j];
31514         if(layout.regions[lr] && config[lr].panels){
31515             var r = layout.regions[lr];
31516             var ps = config[lr].panels;
31517             layout.addTypedPanels(r, ps);
31518         }
31519     }
31520     layout.endUpdate();
31521     return layout;
31522 };
31523
31524 // private
31525 Roo.BorderLayout.RegionFactory = {
31526     // private
31527     validRegions : ["north","south","east","west","center"],
31528
31529     // private
31530     create : function(target, mgr, config){
31531         target = target.toLowerCase();
31532         if(config.lightweight || config.basic){
31533             return new Roo.BasicLayoutRegion(mgr, config, target);
31534         }
31535         switch(target){
31536             case "north":
31537                 return new Roo.NorthLayoutRegion(mgr, config);
31538             case "south":
31539                 return new Roo.SouthLayoutRegion(mgr, config);
31540             case "east":
31541                 return new Roo.EastLayoutRegion(mgr, config);
31542             case "west":
31543                 return new Roo.WestLayoutRegion(mgr, config);
31544             case "center":
31545                 return new Roo.CenterLayoutRegion(mgr, config);
31546         }
31547         throw 'Layout region "'+target+'" not supported.';
31548     }
31549 };/*
31550  * Based on:
31551  * Ext JS Library 1.1.1
31552  * Copyright(c) 2006-2007, Ext JS, LLC.
31553  *
31554  * Originally Released Under LGPL - original licence link has changed is not relivant.
31555  *
31556  * Fork - LGPL
31557  * <script type="text/javascript">
31558  */
31559  
31560 /**
31561  * @class Roo.BasicLayoutRegion
31562  * @extends Roo.util.Observable
31563  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31564  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31565  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31566  */
31567 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31568     this.mgr = mgr;
31569     this.position  = pos;
31570     this.events = {
31571         /**
31572          * @scope Roo.BasicLayoutRegion
31573          */
31574         
31575         /**
31576          * @event beforeremove
31577          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31578          * @param {Roo.LayoutRegion} this
31579          * @param {Roo.ContentPanel} panel The panel
31580          * @param {Object} e The cancel event object
31581          */
31582         "beforeremove" : true,
31583         /**
31584          * @event invalidated
31585          * Fires when the layout for this region is changed.
31586          * @param {Roo.LayoutRegion} this
31587          */
31588         "invalidated" : true,
31589         /**
31590          * @event visibilitychange
31591          * Fires when this region is shown or hidden 
31592          * @param {Roo.LayoutRegion} this
31593          * @param {Boolean} visibility true or false
31594          */
31595         "visibilitychange" : true,
31596         /**
31597          * @event paneladded
31598          * Fires when a panel is added. 
31599          * @param {Roo.LayoutRegion} this
31600          * @param {Roo.ContentPanel} panel The panel
31601          */
31602         "paneladded" : true,
31603         /**
31604          * @event panelremoved
31605          * Fires when a panel is removed. 
31606          * @param {Roo.LayoutRegion} this
31607          * @param {Roo.ContentPanel} panel The panel
31608          */
31609         "panelremoved" : true,
31610         /**
31611          * @event collapsed
31612          * Fires when this region is collapsed.
31613          * @param {Roo.LayoutRegion} this
31614          */
31615         "collapsed" : true,
31616         /**
31617          * @event expanded
31618          * Fires when this region is expanded.
31619          * @param {Roo.LayoutRegion} this
31620          */
31621         "expanded" : true,
31622         /**
31623          * @event slideshow
31624          * Fires when this region is slid into view.
31625          * @param {Roo.LayoutRegion} this
31626          */
31627         "slideshow" : true,
31628         /**
31629          * @event slidehide
31630          * Fires when this region slides out of view. 
31631          * @param {Roo.LayoutRegion} this
31632          */
31633         "slidehide" : true,
31634         /**
31635          * @event panelactivated
31636          * Fires when a panel is activated. 
31637          * @param {Roo.LayoutRegion} this
31638          * @param {Roo.ContentPanel} panel The activated panel
31639          */
31640         "panelactivated" : true,
31641         /**
31642          * @event resized
31643          * Fires when the user resizes this region. 
31644          * @param {Roo.LayoutRegion} this
31645          * @param {Number} newSize The new size (width for east/west, height for north/south)
31646          */
31647         "resized" : true
31648     };
31649     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31650     this.panels = new Roo.util.MixedCollection();
31651     this.panels.getKey = this.getPanelId.createDelegate(this);
31652     this.box = null;
31653     this.activePanel = null;
31654     // ensure listeners are added...
31655     
31656     if (config.listeners || config.events) {
31657         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31658             listeners : config.listeners || {},
31659             events : config.events || {}
31660         });
31661     }
31662     
31663     if(skipConfig !== true){
31664         this.applyConfig(config);
31665     }
31666 };
31667
31668 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31669     getPanelId : function(p){
31670         return p.getId();
31671     },
31672     
31673     applyConfig : function(config){
31674         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31675         this.config = config;
31676         
31677     },
31678     
31679     /**
31680      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31681      * the width, for horizontal (north, south) the height.
31682      * @param {Number} newSize The new width or height
31683      */
31684     resizeTo : function(newSize){
31685         var el = this.el ? this.el :
31686                  (this.activePanel ? this.activePanel.getEl() : null);
31687         if(el){
31688             switch(this.position){
31689                 case "east":
31690                 case "west":
31691                     el.setWidth(newSize);
31692                     this.fireEvent("resized", this, newSize);
31693                 break;
31694                 case "north":
31695                 case "south":
31696                     el.setHeight(newSize);
31697                     this.fireEvent("resized", this, newSize);
31698                 break;                
31699             }
31700         }
31701     },
31702     
31703     getBox : function(){
31704         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31705     },
31706     
31707     getMargins : function(){
31708         return this.margins;
31709     },
31710     
31711     updateBox : function(box){
31712         this.box = box;
31713         var el = this.activePanel.getEl();
31714         el.dom.style.left = box.x + "px";
31715         el.dom.style.top = box.y + "px";
31716         this.activePanel.setSize(box.width, box.height);
31717     },
31718     
31719     /**
31720      * Returns the container element for this region.
31721      * @return {Roo.Element}
31722      */
31723     getEl : function(){
31724         return this.activePanel;
31725     },
31726     
31727     /**
31728      * Returns true if this region is currently visible.
31729      * @return {Boolean}
31730      */
31731     isVisible : function(){
31732         return this.activePanel ? true : false;
31733     },
31734     
31735     setActivePanel : function(panel){
31736         panel = this.getPanel(panel);
31737         if(this.activePanel && this.activePanel != panel){
31738             this.activePanel.setActiveState(false);
31739             this.activePanel.getEl().setLeftTop(-10000,-10000);
31740         }
31741         this.activePanel = panel;
31742         panel.setActiveState(true);
31743         if(this.box){
31744             panel.setSize(this.box.width, this.box.height);
31745         }
31746         this.fireEvent("panelactivated", this, panel);
31747         this.fireEvent("invalidated");
31748     },
31749     
31750     /**
31751      * Show the specified panel.
31752      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31753      * @return {Roo.ContentPanel} The shown panel or null
31754      */
31755     showPanel : function(panel){
31756         if(panel = this.getPanel(panel)){
31757             this.setActivePanel(panel);
31758         }
31759         return panel;
31760     },
31761     
31762     /**
31763      * Get the active panel for this region.
31764      * @return {Roo.ContentPanel} The active panel or null
31765      */
31766     getActivePanel : function(){
31767         return this.activePanel;
31768     },
31769     
31770     /**
31771      * Add the passed ContentPanel(s)
31772      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31773      * @return {Roo.ContentPanel} The panel added (if only one was added)
31774      */
31775     add : function(panel){
31776         if(arguments.length > 1){
31777             for(var i = 0, len = arguments.length; i < len; i++) {
31778                 this.add(arguments[i]);
31779             }
31780             return null;
31781         }
31782         if(this.hasPanel(panel)){
31783             this.showPanel(panel);
31784             return panel;
31785         }
31786         var el = panel.getEl();
31787         if(el.dom.parentNode != this.mgr.el.dom){
31788             this.mgr.el.dom.appendChild(el.dom);
31789         }
31790         if(panel.setRegion){
31791             panel.setRegion(this);
31792         }
31793         this.panels.add(panel);
31794         el.setStyle("position", "absolute");
31795         if(!panel.background){
31796             this.setActivePanel(panel);
31797             if(this.config.initialSize && this.panels.getCount()==1){
31798                 this.resizeTo(this.config.initialSize);
31799             }
31800         }
31801         this.fireEvent("paneladded", this, panel);
31802         return panel;
31803     },
31804     
31805     /**
31806      * Returns true if the panel is in this region.
31807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31808      * @return {Boolean}
31809      */
31810     hasPanel : function(panel){
31811         if(typeof panel == "object"){ // must be panel obj
31812             panel = panel.getId();
31813         }
31814         return this.getPanel(panel) ? true : false;
31815     },
31816     
31817     /**
31818      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31819      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31820      * @param {Boolean} preservePanel Overrides the config preservePanel option
31821      * @return {Roo.ContentPanel} The panel that was removed
31822      */
31823     remove : function(panel, preservePanel){
31824         panel = this.getPanel(panel);
31825         if(!panel){
31826             return null;
31827         }
31828         var e = {};
31829         this.fireEvent("beforeremove", this, panel, e);
31830         if(e.cancel === true){
31831             return null;
31832         }
31833         var panelId = panel.getId();
31834         this.panels.removeKey(panelId);
31835         return panel;
31836     },
31837     
31838     /**
31839      * Returns the panel specified or null if it's not in this region.
31840      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31841      * @return {Roo.ContentPanel}
31842      */
31843     getPanel : function(id){
31844         if(typeof id == "object"){ // must be panel obj
31845             return id;
31846         }
31847         return this.panels.get(id);
31848     },
31849     
31850     /**
31851      * Returns this regions position (north/south/east/west/center).
31852      * @return {String} 
31853      */
31854     getPosition: function(){
31855         return this.position;    
31856     }
31857 });/*
31858  * Based on:
31859  * Ext JS Library 1.1.1
31860  * Copyright(c) 2006-2007, Ext JS, LLC.
31861  *
31862  * Originally Released Under LGPL - original licence link has changed is not relivant.
31863  *
31864  * Fork - LGPL
31865  * <script type="text/javascript">
31866  */
31867  
31868 /**
31869  * @class Roo.LayoutRegion
31870  * @extends Roo.BasicLayoutRegion
31871  * This class represents a region in a layout manager.
31872  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31873  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31874  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31875  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31876  * @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})
31877  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31878  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31879  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31880  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31881  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31882  * @cfg {String}    title           The title for the region (overrides panel titles)
31883  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31884  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31885  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31886  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31887  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31888  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31889  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31890  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31891  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31892  * @cfg {Boolean}   showPin         True to show a pin button
31893  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31894  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31895  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31896  * @cfg {Number}    width           For East/West panels
31897  * @cfg {Number}    height          For North/South panels
31898  * @cfg {Boolean}   split           To show the splitter
31899  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31900  */
31901 Roo.LayoutRegion = function(mgr, config, pos){
31902     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31903     var dh = Roo.DomHelper;
31904     /** This region's container element 
31905     * @type Roo.Element */
31906     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31907     /** This region's title element 
31908     * @type Roo.Element */
31909
31910     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31911         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31912         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31913     ]}, true);
31914     this.titleEl.enableDisplayMode();
31915     /** This region's title text element 
31916     * @type HTMLElement */
31917     this.titleTextEl = this.titleEl.dom.firstChild;
31918     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31919     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31920     this.closeBtn.enableDisplayMode();
31921     this.closeBtn.on("click", this.closeClicked, this);
31922     this.closeBtn.hide();
31923
31924     this.createBody(config);
31925     this.visible = true;
31926     this.collapsed = false;
31927
31928     if(config.hideWhenEmpty){
31929         this.hide();
31930         this.on("paneladded", this.validateVisibility, this);
31931         this.on("panelremoved", this.validateVisibility, this);
31932     }
31933     this.applyConfig(config);
31934 };
31935
31936 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31937
31938     createBody : function(){
31939         /** This region's body element 
31940         * @type Roo.Element */
31941         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31942     },
31943
31944     applyConfig : function(c){
31945         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31946             var dh = Roo.DomHelper;
31947             if(c.titlebar !== false){
31948                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31949                 this.collapseBtn.on("click", this.collapse, this);
31950                 this.collapseBtn.enableDisplayMode();
31951
31952                 if(c.showPin === true || this.showPin){
31953                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31954                     this.stickBtn.enableDisplayMode();
31955                     this.stickBtn.on("click", this.expand, this);
31956                     this.stickBtn.hide();
31957                 }
31958             }
31959             /** This region's collapsed element
31960             * @type Roo.Element */
31961             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31962                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31963             ]}, true);
31964             if(c.floatable !== false){
31965                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31966                this.collapsedEl.on("click", this.collapseClick, this);
31967             }
31968
31969             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31970                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31971                    id: "message", unselectable: "on", style:{"float":"left"}});
31972                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31973              }
31974             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31975             this.expandBtn.on("click", this.expand, this);
31976         }
31977         if(this.collapseBtn){
31978             this.collapseBtn.setVisible(c.collapsible == true);
31979         }
31980         this.cmargins = c.cmargins || this.cmargins ||
31981                          (this.position == "west" || this.position == "east" ?
31982                              {top: 0, left: 2, right:2, bottom: 0} :
31983                              {top: 2, left: 0, right:0, bottom: 2});
31984         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31985         this.bottomTabs = c.tabPosition != "top";
31986         this.autoScroll = c.autoScroll || false;
31987         if(this.autoScroll){
31988             this.bodyEl.setStyle("overflow", "auto");
31989         }else{
31990             this.bodyEl.setStyle("overflow", "hidden");
31991         }
31992         //if(c.titlebar !== false){
31993             if((!c.titlebar && !c.title) || c.titlebar === false){
31994                 this.titleEl.hide();
31995             }else{
31996                 this.titleEl.show();
31997                 if(c.title){
31998                     this.titleTextEl.innerHTML = c.title;
31999                 }
32000             }
32001         //}
32002         this.duration = c.duration || .30;
32003         this.slideDuration = c.slideDuration || .45;
32004         this.config = c;
32005         if(c.collapsed){
32006             this.collapse(true);
32007         }
32008         if(c.hidden){
32009             this.hide();
32010         }
32011     },
32012     /**
32013      * Returns true if this region is currently visible.
32014      * @return {Boolean}
32015      */
32016     isVisible : function(){
32017         return this.visible;
32018     },
32019
32020     /**
32021      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32022      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32023      */
32024     setCollapsedTitle : function(title){
32025         title = title || "&#160;";
32026         if(this.collapsedTitleTextEl){
32027             this.collapsedTitleTextEl.innerHTML = title;
32028         }
32029     },
32030
32031     getBox : function(){
32032         var b;
32033         if(!this.collapsed){
32034             b = this.el.getBox(false, true);
32035         }else{
32036             b = this.collapsedEl.getBox(false, true);
32037         }
32038         return b;
32039     },
32040
32041     getMargins : function(){
32042         return this.collapsed ? this.cmargins : this.margins;
32043     },
32044
32045     highlight : function(){
32046         this.el.addClass("x-layout-panel-dragover");
32047     },
32048
32049     unhighlight : function(){
32050         this.el.removeClass("x-layout-panel-dragover");
32051     },
32052
32053     updateBox : function(box){
32054         this.box = box;
32055         if(!this.collapsed){
32056             this.el.dom.style.left = box.x + "px";
32057             this.el.dom.style.top = box.y + "px";
32058             this.updateBody(box.width, box.height);
32059         }else{
32060             this.collapsedEl.dom.style.left = box.x + "px";
32061             this.collapsedEl.dom.style.top = box.y + "px";
32062             this.collapsedEl.setSize(box.width, box.height);
32063         }
32064         if(this.tabs){
32065             this.tabs.autoSizeTabs();
32066         }
32067     },
32068
32069     updateBody : function(w, h){
32070         if(w !== null){
32071             this.el.setWidth(w);
32072             w -= this.el.getBorderWidth("rl");
32073             if(this.config.adjustments){
32074                 w += this.config.adjustments[0];
32075             }
32076         }
32077         if(h !== null){
32078             this.el.setHeight(h);
32079             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32080             h -= this.el.getBorderWidth("tb");
32081             if(this.config.adjustments){
32082                 h += this.config.adjustments[1];
32083             }
32084             this.bodyEl.setHeight(h);
32085             if(this.tabs){
32086                 h = this.tabs.syncHeight(h);
32087             }
32088         }
32089         if(this.panelSize){
32090             w = w !== null ? w : this.panelSize.width;
32091             h = h !== null ? h : this.panelSize.height;
32092         }
32093         if(this.activePanel){
32094             var el = this.activePanel.getEl();
32095             w = w !== null ? w : el.getWidth();
32096             h = h !== null ? h : el.getHeight();
32097             this.panelSize = {width: w, height: h};
32098             this.activePanel.setSize(w, h);
32099         }
32100         if(Roo.isIE && this.tabs){
32101             this.tabs.el.repaint();
32102         }
32103     },
32104
32105     /**
32106      * Returns the container element for this region.
32107      * @return {Roo.Element}
32108      */
32109     getEl : function(){
32110         return this.el;
32111     },
32112
32113     /**
32114      * Hides this region.
32115      */
32116     hide : function(){
32117         if(!this.collapsed){
32118             this.el.dom.style.left = "-2000px";
32119             this.el.hide();
32120         }else{
32121             this.collapsedEl.dom.style.left = "-2000px";
32122             this.collapsedEl.hide();
32123         }
32124         this.visible = false;
32125         this.fireEvent("visibilitychange", this, false);
32126     },
32127
32128     /**
32129      * Shows this region if it was previously hidden.
32130      */
32131     show : function(){
32132         if(!this.collapsed){
32133             this.el.show();
32134         }else{
32135             this.collapsedEl.show();
32136         }
32137         this.visible = true;
32138         this.fireEvent("visibilitychange", this, true);
32139     },
32140
32141     closeClicked : function(){
32142         if(this.activePanel){
32143             this.remove(this.activePanel);
32144         }
32145     },
32146
32147     collapseClick : function(e){
32148         if(this.isSlid){
32149            e.stopPropagation();
32150            this.slideIn();
32151         }else{
32152            e.stopPropagation();
32153            this.slideOut();
32154         }
32155     },
32156
32157     /**
32158      * Collapses this region.
32159      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32160      */
32161     collapse : function(skipAnim){
32162         if(this.collapsed) return;
32163         this.collapsed = true;
32164         if(this.split){
32165             this.split.el.hide();
32166         }
32167         if(this.config.animate && skipAnim !== true){
32168             this.fireEvent("invalidated", this);
32169             this.animateCollapse();
32170         }else{
32171             this.el.setLocation(-20000,-20000);
32172             this.el.hide();
32173             this.collapsedEl.show();
32174             this.fireEvent("collapsed", this);
32175             this.fireEvent("invalidated", this);
32176         }
32177     },
32178
32179     animateCollapse : function(){
32180         // overridden
32181     },
32182
32183     /**
32184      * Expands this region if it was previously collapsed.
32185      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32186      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32187      */
32188     expand : function(e, skipAnim){
32189         if(e) e.stopPropagation();
32190         if(!this.collapsed || this.el.hasActiveFx()) return;
32191         if(this.isSlid){
32192             this.afterSlideIn();
32193             skipAnim = true;
32194         }
32195         this.collapsed = false;
32196         if(this.config.animate && skipAnim !== true){
32197             this.animateExpand();
32198         }else{
32199             this.el.show();
32200             if(this.split){
32201                 this.split.el.show();
32202             }
32203             this.collapsedEl.setLocation(-2000,-2000);
32204             this.collapsedEl.hide();
32205             this.fireEvent("invalidated", this);
32206             this.fireEvent("expanded", this);
32207         }
32208     },
32209
32210     animateExpand : function(){
32211         // overridden
32212     },
32213
32214     initTabs : function()
32215     {
32216         this.bodyEl.setStyle("overflow", "hidden");
32217         var ts = new Roo.TabPanel(
32218                 this.bodyEl.dom,
32219                 {
32220                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32221                     disableTooltips: this.config.disableTabTips,
32222                     toolbar : this.config.toolbar
32223                 }
32224         );
32225         if(this.config.hideTabs){
32226             ts.stripWrap.setDisplayed(false);
32227         }
32228         this.tabs = ts;
32229         ts.resizeTabs = this.config.resizeTabs === true;
32230         ts.minTabWidth = this.config.minTabWidth || 40;
32231         ts.maxTabWidth = this.config.maxTabWidth || 250;
32232         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32233         ts.monitorResize = false;
32234         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32235         ts.bodyEl.addClass('x-layout-tabs-body');
32236         this.panels.each(this.initPanelAsTab, this);
32237     },
32238
32239     initPanelAsTab : function(panel){
32240         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32241                     this.config.closeOnTab && panel.isClosable());
32242         if(panel.tabTip !== undefined){
32243             ti.setTooltip(panel.tabTip);
32244         }
32245         ti.on("activate", function(){
32246               this.setActivePanel(panel);
32247         }, this);
32248         if(this.config.closeOnTab){
32249             ti.on("beforeclose", function(t, e){
32250                 e.cancel = true;
32251                 this.remove(panel);
32252             }, this);
32253         }
32254         return ti;
32255     },
32256
32257     updatePanelTitle : function(panel, title){
32258         if(this.activePanel == panel){
32259             this.updateTitle(title);
32260         }
32261         if(this.tabs){
32262             var ti = this.tabs.getTab(panel.getEl().id);
32263             ti.setText(title);
32264             if(panel.tabTip !== undefined){
32265                 ti.setTooltip(panel.tabTip);
32266             }
32267         }
32268     },
32269
32270     updateTitle : function(title){
32271         if(this.titleTextEl && !this.config.title){
32272             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32273         }
32274     },
32275
32276     setActivePanel : function(panel){
32277         panel = this.getPanel(panel);
32278         if(this.activePanel && this.activePanel != panel){
32279             this.activePanel.setActiveState(false);
32280         }
32281         this.activePanel = panel;
32282         panel.setActiveState(true);
32283         if(this.panelSize){
32284             panel.setSize(this.panelSize.width, this.panelSize.height);
32285         }
32286         if(this.closeBtn){
32287             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32288         }
32289         this.updateTitle(panel.getTitle());
32290         if(this.tabs){
32291             this.fireEvent("invalidated", this);
32292         }
32293         this.fireEvent("panelactivated", this, panel);
32294     },
32295
32296     /**
32297      * Shows the specified panel.
32298      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32299      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32300      */
32301     showPanel : function(panel){
32302         if(panel = this.getPanel(panel)){
32303             if(this.tabs){
32304                 var tab = this.tabs.getTab(panel.getEl().id);
32305                 if(tab.isHidden()){
32306                     this.tabs.unhideTab(tab.id);
32307                 }
32308                 tab.activate();
32309             }else{
32310                 this.setActivePanel(panel);
32311             }
32312         }
32313         return panel;
32314     },
32315
32316     /**
32317      * Get the active panel for this region.
32318      * @return {Roo.ContentPanel} The active panel or null
32319      */
32320     getActivePanel : function(){
32321         return this.activePanel;
32322     },
32323
32324     validateVisibility : function(){
32325         if(this.panels.getCount() < 1){
32326             this.updateTitle("&#160;");
32327             this.closeBtn.hide();
32328             this.hide();
32329         }else{
32330             if(!this.isVisible()){
32331                 this.show();
32332             }
32333         }
32334     },
32335
32336     /**
32337      * Adds the passed ContentPanel(s) to this region.
32338      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32339      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32340      */
32341     add : function(panel){
32342         if(arguments.length > 1){
32343             for(var i = 0, len = arguments.length; i < len; i++) {
32344                 this.add(arguments[i]);
32345             }
32346             return null;
32347         }
32348         if(this.hasPanel(panel)){
32349             this.showPanel(panel);
32350             return panel;
32351         }
32352         panel.setRegion(this);
32353         this.panels.add(panel);
32354         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32355             this.bodyEl.dom.appendChild(panel.getEl().dom);
32356             if(panel.background !== true){
32357                 this.setActivePanel(panel);
32358             }
32359             this.fireEvent("paneladded", this, panel);
32360             return panel;
32361         }
32362         if(!this.tabs){
32363             this.initTabs();
32364         }else{
32365             this.initPanelAsTab(panel);
32366         }
32367         if(panel.background !== true){
32368             this.tabs.activate(panel.getEl().id);
32369         }
32370         this.fireEvent("paneladded", this, panel);
32371         return panel;
32372     },
32373
32374     /**
32375      * Hides the tab for the specified panel.
32376      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32377      */
32378     hidePanel : function(panel){
32379         if(this.tabs && (panel = this.getPanel(panel))){
32380             this.tabs.hideTab(panel.getEl().id);
32381         }
32382     },
32383
32384     /**
32385      * Unhides the tab for a previously hidden panel.
32386      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32387      */
32388     unhidePanel : function(panel){
32389         if(this.tabs && (panel = this.getPanel(panel))){
32390             this.tabs.unhideTab(panel.getEl().id);
32391         }
32392     },
32393
32394     clearPanels : function(){
32395         while(this.panels.getCount() > 0){
32396              this.remove(this.panels.first());
32397         }
32398     },
32399
32400     /**
32401      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32402      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32403      * @param {Boolean} preservePanel Overrides the config preservePanel option
32404      * @return {Roo.ContentPanel} The panel that was removed
32405      */
32406     remove : function(panel, preservePanel){
32407         panel = this.getPanel(panel);
32408         if(!panel){
32409             return null;
32410         }
32411         var e = {};
32412         this.fireEvent("beforeremove", this, panel, e);
32413         if(e.cancel === true){
32414             return null;
32415         }
32416         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32417         var panelId = panel.getId();
32418         this.panels.removeKey(panelId);
32419         if(preservePanel){
32420             document.body.appendChild(panel.getEl().dom);
32421         }
32422         if(this.tabs){
32423             this.tabs.removeTab(panel.getEl().id);
32424         }else if (!preservePanel){
32425             this.bodyEl.dom.removeChild(panel.getEl().dom);
32426         }
32427         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32428             var p = this.panels.first();
32429             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32430             tempEl.appendChild(p.getEl().dom);
32431             this.bodyEl.update("");
32432             this.bodyEl.dom.appendChild(p.getEl().dom);
32433             tempEl = null;
32434             this.updateTitle(p.getTitle());
32435             this.tabs = null;
32436             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32437             this.setActivePanel(p);
32438         }
32439         panel.setRegion(null);
32440         if(this.activePanel == panel){
32441             this.activePanel = null;
32442         }
32443         if(this.config.autoDestroy !== false && preservePanel !== true){
32444             try{panel.destroy();}catch(e){}
32445         }
32446         this.fireEvent("panelremoved", this, panel);
32447         return panel;
32448     },
32449
32450     /**
32451      * Returns the TabPanel component used by this region
32452      * @return {Roo.TabPanel}
32453      */
32454     getTabs : function(){
32455         return this.tabs;
32456     },
32457
32458     createTool : function(parentEl, className){
32459         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32460             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32461         btn.addClassOnOver("x-layout-tools-button-over");
32462         return btn;
32463     }
32464 });/*
32465  * Based on:
32466  * Ext JS Library 1.1.1
32467  * Copyright(c) 2006-2007, Ext JS, LLC.
32468  *
32469  * Originally Released Under LGPL - original licence link has changed is not relivant.
32470  *
32471  * Fork - LGPL
32472  * <script type="text/javascript">
32473  */
32474  
32475
32476
32477 /**
32478  * @class Roo.SplitLayoutRegion
32479  * @extends Roo.LayoutRegion
32480  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32481  */
32482 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32483     this.cursor = cursor;
32484     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32485 };
32486
32487 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32488     splitTip : "Drag to resize.",
32489     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32490     useSplitTips : false,
32491
32492     applyConfig : function(config){
32493         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32494         if(config.split){
32495             if(!this.split){
32496                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32497                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32498                 /** The SplitBar for this region 
32499                 * @type Roo.SplitBar */
32500                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32501                 this.split.on("moved", this.onSplitMove, this);
32502                 this.split.useShim = config.useShim === true;
32503                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32504                 if(this.useSplitTips){
32505                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32506                 }
32507                 if(config.collapsible){
32508                     this.split.el.on("dblclick", this.collapse,  this);
32509                 }
32510             }
32511             if(typeof config.minSize != "undefined"){
32512                 this.split.minSize = config.minSize;
32513             }
32514             if(typeof config.maxSize != "undefined"){
32515                 this.split.maxSize = config.maxSize;
32516             }
32517             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32518                 this.hideSplitter();
32519             }
32520         }
32521     },
32522
32523     getHMaxSize : function(){
32524          var cmax = this.config.maxSize || 10000;
32525          var center = this.mgr.getRegion("center");
32526          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32527     },
32528
32529     getVMaxSize : function(){
32530          var cmax = this.config.maxSize || 10000;
32531          var center = this.mgr.getRegion("center");
32532          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32533     },
32534
32535     onSplitMove : function(split, newSize){
32536         this.fireEvent("resized", this, newSize);
32537     },
32538     
32539     /** 
32540      * Returns the {@link Roo.SplitBar} for this region.
32541      * @return {Roo.SplitBar}
32542      */
32543     getSplitBar : function(){
32544         return this.split;
32545     },
32546     
32547     hide : function(){
32548         this.hideSplitter();
32549         Roo.SplitLayoutRegion.superclass.hide.call(this);
32550     },
32551
32552     hideSplitter : function(){
32553         if(this.split){
32554             this.split.el.setLocation(-2000,-2000);
32555             this.split.el.hide();
32556         }
32557     },
32558
32559     show : function(){
32560         if(this.split){
32561             this.split.el.show();
32562         }
32563         Roo.SplitLayoutRegion.superclass.show.call(this);
32564     },
32565     
32566     beforeSlide: function(){
32567         if(Roo.isGecko){// firefox overflow auto bug workaround
32568             this.bodyEl.clip();
32569             if(this.tabs) this.tabs.bodyEl.clip();
32570             if(this.activePanel){
32571                 this.activePanel.getEl().clip();
32572                 
32573                 if(this.activePanel.beforeSlide){
32574                     this.activePanel.beforeSlide();
32575                 }
32576             }
32577         }
32578     },
32579     
32580     afterSlide : function(){
32581         if(Roo.isGecko){// firefox overflow auto bug workaround
32582             this.bodyEl.unclip();
32583             if(this.tabs) this.tabs.bodyEl.unclip();
32584             if(this.activePanel){
32585                 this.activePanel.getEl().unclip();
32586                 if(this.activePanel.afterSlide){
32587                     this.activePanel.afterSlide();
32588                 }
32589             }
32590         }
32591     },
32592
32593     initAutoHide : function(){
32594         if(this.autoHide !== false){
32595             if(!this.autoHideHd){
32596                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32597                 this.autoHideHd = {
32598                     "mouseout": function(e){
32599                         if(!e.within(this.el, true)){
32600                             st.delay(500);
32601                         }
32602                     },
32603                     "mouseover" : function(e){
32604                         st.cancel();
32605                     },
32606                     scope : this
32607                 };
32608             }
32609             this.el.on(this.autoHideHd);
32610         }
32611     },
32612
32613     clearAutoHide : function(){
32614         if(this.autoHide !== false){
32615             this.el.un("mouseout", this.autoHideHd.mouseout);
32616             this.el.un("mouseover", this.autoHideHd.mouseover);
32617         }
32618     },
32619
32620     clearMonitor : function(){
32621         Roo.get(document).un("click", this.slideInIf, this);
32622     },
32623
32624     // these names are backwards but not changed for compat
32625     slideOut : function(){
32626         if(this.isSlid || this.el.hasActiveFx()){
32627             return;
32628         }
32629         this.isSlid = true;
32630         if(this.collapseBtn){
32631             this.collapseBtn.hide();
32632         }
32633         this.closeBtnState = this.closeBtn.getStyle('display');
32634         this.closeBtn.hide();
32635         if(this.stickBtn){
32636             this.stickBtn.show();
32637         }
32638         this.el.show();
32639         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32640         this.beforeSlide();
32641         this.el.setStyle("z-index", 10001);
32642         this.el.slideIn(this.getSlideAnchor(), {
32643             callback: function(){
32644                 this.afterSlide();
32645                 this.initAutoHide();
32646                 Roo.get(document).on("click", this.slideInIf, this);
32647                 this.fireEvent("slideshow", this);
32648             },
32649             scope: this,
32650             block: true
32651         });
32652     },
32653
32654     afterSlideIn : function(){
32655         this.clearAutoHide();
32656         this.isSlid = false;
32657         this.clearMonitor();
32658         this.el.setStyle("z-index", "");
32659         if(this.collapseBtn){
32660             this.collapseBtn.show();
32661         }
32662         this.closeBtn.setStyle('display', this.closeBtnState);
32663         if(this.stickBtn){
32664             this.stickBtn.hide();
32665         }
32666         this.fireEvent("slidehide", this);
32667     },
32668
32669     slideIn : function(cb){
32670         if(!this.isSlid || this.el.hasActiveFx()){
32671             Roo.callback(cb);
32672             return;
32673         }
32674         this.isSlid = false;
32675         this.beforeSlide();
32676         this.el.slideOut(this.getSlideAnchor(), {
32677             callback: function(){
32678                 this.el.setLeftTop(-10000, -10000);
32679                 this.afterSlide();
32680                 this.afterSlideIn();
32681                 Roo.callback(cb);
32682             },
32683             scope: this,
32684             block: true
32685         });
32686     },
32687     
32688     slideInIf : function(e){
32689         if(!e.within(this.el)){
32690             this.slideIn();
32691         }
32692     },
32693
32694     animateCollapse : function(){
32695         this.beforeSlide();
32696         this.el.setStyle("z-index", 20000);
32697         var anchor = this.getSlideAnchor();
32698         this.el.slideOut(anchor, {
32699             callback : function(){
32700                 this.el.setStyle("z-index", "");
32701                 this.collapsedEl.slideIn(anchor, {duration:.3});
32702                 this.afterSlide();
32703                 this.el.setLocation(-10000,-10000);
32704                 this.el.hide();
32705                 this.fireEvent("collapsed", this);
32706             },
32707             scope: this,
32708             block: true
32709         });
32710     },
32711
32712     animateExpand : function(){
32713         this.beforeSlide();
32714         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32715         this.el.setStyle("z-index", 20000);
32716         this.collapsedEl.hide({
32717             duration:.1
32718         });
32719         this.el.slideIn(this.getSlideAnchor(), {
32720             callback : function(){
32721                 this.el.setStyle("z-index", "");
32722                 this.afterSlide();
32723                 if(this.split){
32724                     this.split.el.show();
32725                 }
32726                 this.fireEvent("invalidated", this);
32727                 this.fireEvent("expanded", this);
32728             },
32729             scope: this,
32730             block: true
32731         });
32732     },
32733
32734     anchors : {
32735         "west" : "left",
32736         "east" : "right",
32737         "north" : "top",
32738         "south" : "bottom"
32739     },
32740
32741     sanchors : {
32742         "west" : "l",
32743         "east" : "r",
32744         "north" : "t",
32745         "south" : "b"
32746     },
32747
32748     canchors : {
32749         "west" : "tl-tr",
32750         "east" : "tr-tl",
32751         "north" : "tl-bl",
32752         "south" : "bl-tl"
32753     },
32754
32755     getAnchor : function(){
32756         return this.anchors[this.position];
32757     },
32758
32759     getCollapseAnchor : function(){
32760         return this.canchors[this.position];
32761     },
32762
32763     getSlideAnchor : function(){
32764         return this.sanchors[this.position];
32765     },
32766
32767     getAlignAdj : function(){
32768         var cm = this.cmargins;
32769         switch(this.position){
32770             case "west":
32771                 return [0, 0];
32772             break;
32773             case "east":
32774                 return [0, 0];
32775             break;
32776             case "north":
32777                 return [0, 0];
32778             break;
32779             case "south":
32780                 return [0, 0];
32781             break;
32782         }
32783     },
32784
32785     getExpandAdj : function(){
32786         var c = this.collapsedEl, cm = this.cmargins;
32787         switch(this.position){
32788             case "west":
32789                 return [-(cm.right+c.getWidth()+cm.left), 0];
32790             break;
32791             case "east":
32792                 return [cm.right+c.getWidth()+cm.left, 0];
32793             break;
32794             case "north":
32795                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32796             break;
32797             case "south":
32798                 return [0, cm.top+cm.bottom+c.getHeight()];
32799             break;
32800         }
32801     }
32802 });/*
32803  * Based on:
32804  * Ext JS Library 1.1.1
32805  * Copyright(c) 2006-2007, Ext JS, LLC.
32806  *
32807  * Originally Released Under LGPL - original licence link has changed is not relivant.
32808  *
32809  * Fork - LGPL
32810  * <script type="text/javascript">
32811  */
32812 /*
32813  * These classes are private internal classes
32814  */
32815 Roo.CenterLayoutRegion = function(mgr, config){
32816     Roo.LayoutRegion.call(this, mgr, config, "center");
32817     this.visible = true;
32818     this.minWidth = config.minWidth || 20;
32819     this.minHeight = config.minHeight || 20;
32820 };
32821
32822 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32823     hide : function(){
32824         // center panel can't be hidden
32825     },
32826     
32827     show : function(){
32828         // center panel can't be hidden
32829     },
32830     
32831     getMinWidth: function(){
32832         return this.minWidth;
32833     },
32834     
32835     getMinHeight: function(){
32836         return this.minHeight;
32837     }
32838 });
32839
32840
32841 Roo.NorthLayoutRegion = function(mgr, config){
32842     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32843     if(this.split){
32844         this.split.placement = Roo.SplitBar.TOP;
32845         this.split.orientation = Roo.SplitBar.VERTICAL;
32846         this.split.el.addClass("x-layout-split-v");
32847     }
32848     var size = config.initialSize || config.height;
32849     if(typeof size != "undefined"){
32850         this.el.setHeight(size);
32851     }
32852 };
32853 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32854     orientation: Roo.SplitBar.VERTICAL,
32855     getBox : function(){
32856         if(this.collapsed){
32857             return this.collapsedEl.getBox();
32858         }
32859         var box = this.el.getBox();
32860         if(this.split){
32861             box.height += this.split.el.getHeight();
32862         }
32863         return box;
32864     },
32865     
32866     updateBox : function(box){
32867         if(this.split && !this.collapsed){
32868             box.height -= this.split.el.getHeight();
32869             this.split.el.setLeft(box.x);
32870             this.split.el.setTop(box.y+box.height);
32871             this.split.el.setWidth(box.width);
32872         }
32873         if(this.collapsed){
32874             this.updateBody(box.width, null);
32875         }
32876         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32877     }
32878 });
32879
32880 Roo.SouthLayoutRegion = function(mgr, config){
32881     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32882     if(this.split){
32883         this.split.placement = Roo.SplitBar.BOTTOM;
32884         this.split.orientation = Roo.SplitBar.VERTICAL;
32885         this.split.el.addClass("x-layout-split-v");
32886     }
32887     var size = config.initialSize || config.height;
32888     if(typeof size != "undefined"){
32889         this.el.setHeight(size);
32890     }
32891 };
32892 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32893     orientation: Roo.SplitBar.VERTICAL,
32894     getBox : function(){
32895         if(this.collapsed){
32896             return this.collapsedEl.getBox();
32897         }
32898         var box = this.el.getBox();
32899         if(this.split){
32900             var sh = this.split.el.getHeight();
32901             box.height += sh;
32902             box.y -= sh;
32903         }
32904         return box;
32905     },
32906     
32907     updateBox : function(box){
32908         if(this.split && !this.collapsed){
32909             var sh = this.split.el.getHeight();
32910             box.height -= sh;
32911             box.y += sh;
32912             this.split.el.setLeft(box.x);
32913             this.split.el.setTop(box.y-sh);
32914             this.split.el.setWidth(box.width);
32915         }
32916         if(this.collapsed){
32917             this.updateBody(box.width, null);
32918         }
32919         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32920     }
32921 });
32922
32923 Roo.EastLayoutRegion = function(mgr, config){
32924     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32925     if(this.split){
32926         this.split.placement = Roo.SplitBar.RIGHT;
32927         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32928         this.split.el.addClass("x-layout-split-h");
32929     }
32930     var size = config.initialSize || config.width;
32931     if(typeof size != "undefined"){
32932         this.el.setWidth(size);
32933     }
32934 };
32935 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32936     orientation: Roo.SplitBar.HORIZONTAL,
32937     getBox : function(){
32938         if(this.collapsed){
32939             return this.collapsedEl.getBox();
32940         }
32941         var box = this.el.getBox();
32942         if(this.split){
32943             var sw = this.split.el.getWidth();
32944             box.width += sw;
32945             box.x -= sw;
32946         }
32947         return box;
32948     },
32949
32950     updateBox : function(box){
32951         if(this.split && !this.collapsed){
32952             var sw = this.split.el.getWidth();
32953             box.width -= sw;
32954             this.split.el.setLeft(box.x);
32955             this.split.el.setTop(box.y);
32956             this.split.el.setHeight(box.height);
32957             box.x += sw;
32958         }
32959         if(this.collapsed){
32960             this.updateBody(null, box.height);
32961         }
32962         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32963     }
32964 });
32965
32966 Roo.WestLayoutRegion = function(mgr, config){
32967     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32968     if(this.split){
32969         this.split.placement = Roo.SplitBar.LEFT;
32970         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32971         this.split.el.addClass("x-layout-split-h");
32972     }
32973     var size = config.initialSize || config.width;
32974     if(typeof size != "undefined"){
32975         this.el.setWidth(size);
32976     }
32977 };
32978 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32979     orientation: Roo.SplitBar.HORIZONTAL,
32980     getBox : function(){
32981         if(this.collapsed){
32982             return this.collapsedEl.getBox();
32983         }
32984         var box = this.el.getBox();
32985         if(this.split){
32986             box.width += this.split.el.getWidth();
32987         }
32988         return box;
32989     },
32990     
32991     updateBox : function(box){
32992         if(this.split && !this.collapsed){
32993             var sw = this.split.el.getWidth();
32994             box.width -= sw;
32995             this.split.el.setLeft(box.x+box.width);
32996             this.split.el.setTop(box.y);
32997             this.split.el.setHeight(box.height);
32998         }
32999         if(this.collapsed){
33000             this.updateBody(null, box.height);
33001         }
33002         Roo.LayoutRegion.prototype.updateBox.call(this, box);
33003     }
33004 });
33005 /*
33006  * Based on:
33007  * Ext JS Library 1.1.1
33008  * Copyright(c) 2006-2007, Ext JS, LLC.
33009  *
33010  * Originally Released Under LGPL - original licence link has changed is not relivant.
33011  *
33012  * Fork - LGPL
33013  * <script type="text/javascript">
33014  */
33015  
33016  
33017 /*
33018  * Private internal class for reading and applying state
33019  */
33020 Roo.LayoutStateManager = function(layout){
33021      // default empty state
33022      this.state = {
33023         north: {},
33024         south: {},
33025         east: {},
33026         west: {}       
33027     };
33028 };
33029
33030 Roo.LayoutStateManager.prototype = {
33031     init : function(layout, provider){
33032         this.provider = provider;
33033         var state = provider.get(layout.id+"-layout-state");
33034         if(state){
33035             var wasUpdating = layout.isUpdating();
33036             if(!wasUpdating){
33037                 layout.beginUpdate();
33038             }
33039             for(var key in state){
33040                 if(typeof state[key] != "function"){
33041                     var rstate = state[key];
33042                     var r = layout.getRegion(key);
33043                     if(r && rstate){
33044                         if(rstate.size){
33045                             r.resizeTo(rstate.size);
33046                         }
33047                         if(rstate.collapsed == true){
33048                             r.collapse(true);
33049                         }else{
33050                             r.expand(null, true);
33051                         }
33052                     }
33053                 }
33054             }
33055             if(!wasUpdating){
33056                 layout.endUpdate();
33057             }
33058             this.state = state; 
33059         }
33060         this.layout = layout;
33061         layout.on("regionresized", this.onRegionResized, this);
33062         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33063         layout.on("regionexpanded", this.onRegionExpanded, this);
33064     },
33065     
33066     storeState : function(){
33067         this.provider.set(this.layout.id+"-layout-state", this.state);
33068     },
33069     
33070     onRegionResized : function(region, newSize){
33071         this.state[region.getPosition()].size = newSize;
33072         this.storeState();
33073     },
33074     
33075     onRegionCollapsed : function(region){
33076         this.state[region.getPosition()].collapsed = true;
33077         this.storeState();
33078     },
33079     
33080     onRegionExpanded : function(region){
33081         this.state[region.getPosition()].collapsed = false;
33082         this.storeState();
33083     }
33084 };/*
33085  * Based on:
33086  * Ext JS Library 1.1.1
33087  * Copyright(c) 2006-2007, Ext JS, LLC.
33088  *
33089  * Originally Released Under LGPL - original licence link has changed is not relivant.
33090  *
33091  * Fork - LGPL
33092  * <script type="text/javascript">
33093  */
33094 /**
33095  * @class Roo.ContentPanel
33096  * @extends Roo.util.Observable
33097  * A basic ContentPanel element.
33098  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33099  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33100  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
33101  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33102  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33103  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33104  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33105  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33106  * @cfg {String} title          The title for this panel
33107  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33108  * @cfg {String} url            Calls {@link #setUrl} with this value
33109  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33110  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33111  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33112  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33113
33114  * @constructor
33115  * Create a new ContentPanel.
33116  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33117  * @param {String/Object} config A string to set only the title or a config object
33118  * @param {String} content (optional) Set the HTML content for this panel
33119  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33120  */
33121 Roo.ContentPanel = function(el, config, content){
33122     
33123      
33124     /*
33125     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33126         config = el;
33127         el = Roo.id();
33128     }
33129     if (config && config.parentLayout) { 
33130         el = config.parentLayout.el.createChild(); 
33131     }
33132     */
33133     if(el.autoCreate){ // xtype is available if this is called from factory
33134         config = el;
33135         el = Roo.id();
33136     }
33137     this.el = Roo.get(el);
33138     if(!this.el && config && config.autoCreate){
33139         if(typeof config.autoCreate == "object"){
33140             if(!config.autoCreate.id){
33141                 config.autoCreate.id = config.id||el;
33142             }
33143             this.el = Roo.DomHelper.append(document.body,
33144                         config.autoCreate, true);
33145         }else{
33146             this.el = Roo.DomHelper.append(document.body,
33147                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33148         }
33149     }
33150     this.closable = false;
33151     this.loaded = false;
33152     this.active = false;
33153     if(typeof config == "string"){
33154         this.title = config;
33155     }else{
33156         Roo.apply(this, config);
33157     }
33158     
33159     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33160         this.wrapEl = this.el.wrap();
33161         this.toolbar.container = this.el.insertSibling(false, 'before');
33162         this.toolbar = new Roo.Toolbar(this.toolbar);
33163     }
33164     
33165     // xtype created footer. - not sure if will work as we normally have to render first..
33166     if (this.footer && !this.footer.el && this.footer.xtype) {
33167         if (!this.wrapEl) {
33168             this.wrapEl = this.el.wrap();
33169         }
33170     
33171         this.footer.container = this.wrapEl.createChild();
33172          
33173         this.footer = Roo.factory(this.footer, Roo);
33174         
33175     }
33176     
33177     if(this.resizeEl){
33178         this.resizeEl = Roo.get(this.resizeEl, true);
33179     }else{
33180         this.resizeEl = this.el;
33181     }
33182     this.addEvents({
33183         /**
33184          * @event activate
33185          * Fires when this panel is activated. 
33186          * @param {Roo.ContentPanel} this
33187          */
33188         "activate" : true,
33189         /**
33190          * @event deactivate
33191          * Fires when this panel is activated. 
33192          * @param {Roo.ContentPanel} this
33193          */
33194         "deactivate" : true,
33195
33196         /**
33197          * @event resize
33198          * Fires when this panel is resized if fitToFrame is true.
33199          * @param {Roo.ContentPanel} this
33200          * @param {Number} width The width after any component adjustments
33201          * @param {Number} height The height after any component adjustments
33202          */
33203         "resize" : true,
33204         
33205          /**
33206          * @event render
33207          * Fires when this tab is created
33208          * @param {Roo.ContentPanel} this
33209          */
33210         "render" : true
33211         
33212         
33213         
33214     });
33215     if(this.autoScroll){
33216         this.resizeEl.setStyle("overflow", "auto");
33217     } else {
33218         // fix randome scrolling
33219         this.el.on('scroll', function() {
33220             Roo.log('fix random scolling');
33221             this.scrollTo('top',0); 
33222         });
33223     }
33224     content = content || this.content;
33225     if(content){
33226         this.setContent(content);
33227     }
33228     if(config && config.url){
33229         this.setUrl(this.url, this.params, this.loadOnce);
33230     }
33231     
33232     
33233     
33234     Roo.ContentPanel.superclass.constructor.call(this);
33235     
33236     this.fireEvent('render', this);
33237 };
33238
33239 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33240     tabTip:'',
33241     setRegion : function(region){
33242         this.region = region;
33243         if(region){
33244            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33245         }else{
33246            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33247         } 
33248     },
33249     
33250     /**
33251      * Returns the toolbar for this Panel if one was configured. 
33252      * @return {Roo.Toolbar} 
33253      */
33254     getToolbar : function(){
33255         return this.toolbar;
33256     },
33257     
33258     setActiveState : function(active){
33259         this.active = active;
33260         if(!active){
33261             this.fireEvent("deactivate", this);
33262         }else{
33263             this.fireEvent("activate", this);
33264         }
33265     },
33266     /**
33267      * Updates this panel's element
33268      * @param {String} content The new content
33269      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33270     */
33271     setContent : function(content, loadScripts){
33272         this.el.update(content, loadScripts);
33273     },
33274
33275     ignoreResize : function(w, h){
33276         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33277             return true;
33278         }else{
33279             this.lastSize = {width: w, height: h};
33280             return false;
33281         }
33282     },
33283     /**
33284      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33285      * @return {Roo.UpdateManager} The UpdateManager
33286      */
33287     getUpdateManager : function(){
33288         return this.el.getUpdateManager();
33289     },
33290      /**
33291      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33292      * @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:
33293 <pre><code>
33294 panel.load({
33295     url: "your-url.php",
33296     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33297     callback: yourFunction,
33298     scope: yourObject, //(optional scope)
33299     discardUrl: false,
33300     nocache: false,
33301     text: "Loading...",
33302     timeout: 30,
33303     scripts: false
33304 });
33305 </code></pre>
33306      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33307      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
33308      * @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}
33309      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33310      * @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.
33311      * @return {Roo.ContentPanel} this
33312      */
33313     load : function(){
33314         var um = this.el.getUpdateManager();
33315         um.update.apply(um, arguments);
33316         return this;
33317     },
33318
33319
33320     /**
33321      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
33322      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33323      * @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)
33324      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
33325      * @return {Roo.UpdateManager} The UpdateManager
33326      */
33327     setUrl : function(url, params, loadOnce){
33328         if(this.refreshDelegate){
33329             this.removeListener("activate", this.refreshDelegate);
33330         }
33331         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33332         this.on("activate", this.refreshDelegate);
33333         return this.el.getUpdateManager();
33334     },
33335     
33336     _handleRefresh : function(url, params, loadOnce){
33337         if(!loadOnce || !this.loaded){
33338             var updater = this.el.getUpdateManager();
33339             updater.update(url, params, this._setLoaded.createDelegate(this));
33340         }
33341     },
33342     
33343     _setLoaded : function(){
33344         this.loaded = true;
33345     }, 
33346     
33347     /**
33348      * Returns this panel's id
33349      * @return {String} 
33350      */
33351     getId : function(){
33352         return this.el.id;
33353     },
33354     
33355     /** 
33356      * Returns this panel's element - used by regiosn to add.
33357      * @return {Roo.Element} 
33358      */
33359     getEl : function(){
33360         return this.wrapEl || this.el;
33361     },
33362     
33363     adjustForComponents : function(width, height)
33364     {
33365         Roo.log('adjustForComponents ');
33366         if(this.resizeEl != this.el){
33367             width -= this.el.getFrameWidth('lr');
33368             height -= this.el.getFrameWidth('tb');
33369         }
33370         if(this.toolbar){
33371             var te = this.toolbar.getEl();
33372             height -= te.getHeight();
33373             te.setWidth(width);
33374         }
33375         if(this.footer){
33376             var te = this.footer.getEl();
33377             Roo.log("footer:" + te.getHeight());
33378             
33379             height -= te.getHeight();
33380             te.setWidth(width);
33381         }
33382         
33383         
33384         if(this.adjustments){
33385             width += this.adjustments[0];
33386             height += this.adjustments[1];
33387         }
33388         return {"width": width, "height": height};
33389     },
33390     
33391     setSize : function(width, height){
33392         if(this.fitToFrame && !this.ignoreResize(width, height)){
33393             if(this.fitContainer && this.resizeEl != this.el){
33394                 this.el.setSize(width, height);
33395             }
33396             var size = this.adjustForComponents(width, height);
33397             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33398             this.fireEvent('resize', this, size.width, size.height);
33399         }
33400     },
33401     
33402     /**
33403      * Returns this panel's title
33404      * @return {String} 
33405      */
33406     getTitle : function(){
33407         return this.title;
33408     },
33409     
33410     /**
33411      * Set this panel's title
33412      * @param {String} title
33413      */
33414     setTitle : function(title){
33415         this.title = title;
33416         if(this.region){
33417             this.region.updatePanelTitle(this, title);
33418         }
33419     },
33420     
33421     /**
33422      * Returns true is this panel was configured to be closable
33423      * @return {Boolean} 
33424      */
33425     isClosable : function(){
33426         return this.closable;
33427     },
33428     
33429     beforeSlide : function(){
33430         this.el.clip();
33431         this.resizeEl.clip();
33432     },
33433     
33434     afterSlide : function(){
33435         this.el.unclip();
33436         this.resizeEl.unclip();
33437     },
33438     
33439     /**
33440      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33441      *   Will fail silently if the {@link #setUrl} method has not been called.
33442      *   This does not activate the panel, just updates its content.
33443      */
33444     refresh : function(){
33445         if(this.refreshDelegate){
33446            this.loaded = false;
33447            this.refreshDelegate();
33448         }
33449     },
33450     
33451     /**
33452      * Destroys this panel
33453      */
33454     destroy : function(){
33455         this.el.removeAllListeners();
33456         var tempEl = document.createElement("span");
33457         tempEl.appendChild(this.el.dom);
33458         tempEl.innerHTML = "";
33459         this.el.remove();
33460         this.el = null;
33461     },
33462     
33463     /**
33464      * form - if the content panel contains a form - this is a reference to it.
33465      * @type {Roo.form.Form}
33466      */
33467     form : false,
33468     /**
33469      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33470      *    This contains a reference to it.
33471      * @type {Roo.View}
33472      */
33473     view : false,
33474     
33475       /**
33476      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33477      * <pre><code>
33478
33479 layout.addxtype({
33480        xtype : 'Form',
33481        items: [ .... ]
33482    }
33483 );
33484
33485 </code></pre>
33486      * @param {Object} cfg Xtype definition of item to add.
33487      */
33488     
33489     addxtype : function(cfg) {
33490         // add form..
33491         if (cfg.xtype.match(/^Form$/)) {
33492             
33493             var el;
33494             //if (this.footer) {
33495             //    el = this.footer.container.insertSibling(false, 'before');
33496             //} else {
33497                 el = this.el.createChild();
33498             //}
33499
33500             this.form = new  Roo.form.Form(cfg);
33501             
33502             
33503             if ( this.form.allItems.length) this.form.render(el.dom);
33504             return this.form;
33505         }
33506         // should only have one of theses..
33507         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33508             // views..
33509             cfg.el = this.el.appendChild(document.createElement("div"));
33510             // factory?
33511             
33512             var ret = new Roo.factory(cfg);
33513             ret.render && ret.render(false, ''); // render blank..
33514             this.view = ret;
33515             return ret;
33516         }
33517         return false;
33518     }
33519 });
33520
33521 /**
33522  * @class Roo.GridPanel
33523  * @extends Roo.ContentPanel
33524  * @constructor
33525  * Create a new GridPanel.
33526  * @param {Roo.grid.Grid} grid The grid for this panel
33527  * @param {String/Object} config A string to set only the panel's title, or a config object
33528  */
33529 Roo.GridPanel = function(grid, config){
33530     
33531   
33532     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33533         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33534         
33535     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33536     
33537     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33538     
33539     if(this.toolbar){
33540         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33541     }
33542     // xtype created footer. - not sure if will work as we normally have to render first..
33543     if (this.footer && !this.footer.el && this.footer.xtype) {
33544         
33545         this.footer.container = this.grid.getView().getFooterPanel(true);
33546         this.footer.dataSource = this.grid.dataSource;
33547         this.footer = Roo.factory(this.footer, Roo);
33548         
33549     }
33550     
33551     grid.monitorWindowResize = false; // turn off autosizing
33552     grid.autoHeight = false;
33553     grid.autoWidth = false;
33554     this.grid = grid;
33555     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33556 };
33557
33558 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33559     getId : function(){
33560         return this.grid.id;
33561     },
33562     
33563     /**
33564      * Returns the grid for this panel
33565      * @return {Roo.grid.Grid} 
33566      */
33567     getGrid : function(){
33568         return this.grid;    
33569     },
33570     
33571     setSize : function(width, height){
33572         if(!this.ignoreResize(width, height)){
33573             var grid = this.grid;
33574             var size = this.adjustForComponents(width, height);
33575             grid.getGridEl().setSize(size.width, size.height);
33576             grid.autoSize();
33577         }
33578     },
33579     
33580     beforeSlide : function(){
33581         this.grid.getView().scroller.clip();
33582     },
33583     
33584     afterSlide : function(){
33585         this.grid.getView().scroller.unclip();
33586     },
33587     
33588     destroy : function(){
33589         this.grid.destroy();
33590         delete this.grid;
33591         Roo.GridPanel.superclass.destroy.call(this); 
33592     }
33593 });
33594
33595
33596 /**
33597  * @class Roo.NestedLayoutPanel
33598  * @extends Roo.ContentPanel
33599  * @constructor
33600  * Create a new NestedLayoutPanel.
33601  * 
33602  * 
33603  * @param {Roo.BorderLayout} layout The layout for this panel
33604  * @param {String/Object} config A string to set only the title or a config object
33605  */
33606 Roo.NestedLayoutPanel = function(layout, config)
33607 {
33608     // construct with only one argument..
33609     /* FIXME - implement nicer consturctors
33610     if (layout.layout) {
33611         config = layout;
33612         layout = config.layout;
33613         delete config.layout;
33614     }
33615     if (layout.xtype && !layout.getEl) {
33616         // then layout needs constructing..
33617         layout = Roo.factory(layout, Roo);
33618     }
33619     */
33620     
33621     
33622     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33623     
33624     layout.monitorWindowResize = false; // turn off autosizing
33625     this.layout = layout;
33626     this.layout.getEl().addClass("x-layout-nested-layout");
33627     
33628     
33629     
33630     
33631 };
33632
33633 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33634
33635     setSize : function(width, height){
33636         if(!this.ignoreResize(width, height)){
33637             var size = this.adjustForComponents(width, height);
33638             var el = this.layout.getEl();
33639             el.setSize(size.width, size.height);
33640             var touch = el.dom.offsetWidth;
33641             this.layout.layout();
33642             // ie requires a double layout on the first pass
33643             if(Roo.isIE && !this.initialized){
33644                 this.initialized = true;
33645                 this.layout.layout();
33646             }
33647         }
33648     },
33649     
33650     // activate all subpanels if not currently active..
33651     
33652     setActiveState : function(active){
33653         this.active = active;
33654         if(!active){
33655             this.fireEvent("deactivate", this);
33656             return;
33657         }
33658         
33659         this.fireEvent("activate", this);
33660         // not sure if this should happen before or after..
33661         if (!this.layout) {
33662             return; // should not happen..
33663         }
33664         var reg = false;
33665         for (var r in this.layout.regions) {
33666             reg = this.layout.getRegion(r);
33667             if (reg.getActivePanel()) {
33668                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33669                 reg.setActivePanel(reg.getActivePanel());
33670                 continue;
33671             }
33672             if (!reg.panels.length) {
33673                 continue;
33674             }
33675             reg.showPanel(reg.getPanel(0));
33676         }
33677         
33678         
33679         
33680         
33681     },
33682     
33683     /**
33684      * Returns the nested BorderLayout for this panel
33685      * @return {Roo.BorderLayout} 
33686      */
33687     getLayout : function(){
33688         return this.layout;
33689     },
33690     
33691      /**
33692      * Adds a xtype elements to the layout of the nested panel
33693      * <pre><code>
33694
33695 panel.addxtype({
33696        xtype : 'ContentPanel',
33697        region: 'west',
33698        items: [ .... ]
33699    }
33700 );
33701
33702 panel.addxtype({
33703         xtype : 'NestedLayoutPanel',
33704         region: 'west',
33705         layout: {
33706            center: { },
33707            west: { }   
33708         },
33709         items : [ ... list of content panels or nested layout panels.. ]
33710    }
33711 );
33712 </code></pre>
33713      * @param {Object} cfg Xtype definition of item to add.
33714      */
33715     addxtype : function(cfg) {
33716         return this.layout.addxtype(cfg);
33717     
33718     }
33719 });
33720
33721 Roo.ScrollPanel = function(el, config, content){
33722     config = config || {};
33723     config.fitToFrame = true;
33724     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33725     
33726     this.el.dom.style.overflow = "hidden";
33727     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33728     this.el.removeClass("x-layout-inactive-content");
33729     this.el.on("mousewheel", this.onWheel, this);
33730
33731     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33732     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33733     up.unselectable(); down.unselectable();
33734     up.on("click", this.scrollUp, this);
33735     down.on("click", this.scrollDown, this);
33736     up.addClassOnOver("x-scroller-btn-over");
33737     down.addClassOnOver("x-scroller-btn-over");
33738     up.addClassOnClick("x-scroller-btn-click");
33739     down.addClassOnClick("x-scroller-btn-click");
33740     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33741
33742     this.resizeEl = this.el;
33743     this.el = wrap; this.up = up; this.down = down;
33744 };
33745
33746 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33747     increment : 100,
33748     wheelIncrement : 5,
33749     scrollUp : function(){
33750         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33751     },
33752
33753     scrollDown : function(){
33754         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33755     },
33756
33757     afterScroll : function(){
33758         var el = this.resizeEl;
33759         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33760         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33761         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33762     },
33763
33764     setSize : function(){
33765         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33766         this.afterScroll();
33767     },
33768
33769     onWheel : function(e){
33770         var d = e.getWheelDelta();
33771         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33772         this.afterScroll();
33773         e.stopEvent();
33774     },
33775
33776     setContent : function(content, loadScripts){
33777         this.resizeEl.update(content, loadScripts);
33778     }
33779
33780 });
33781
33782
33783
33784
33785
33786
33787
33788
33789
33790 /**
33791  * @class Roo.TreePanel
33792  * @extends Roo.ContentPanel
33793  * @constructor
33794  * Create a new TreePanel. - defaults to fit/scoll contents.
33795  * @param {String/Object} config A string to set only the panel's title, or a config object
33796  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33797  */
33798 Roo.TreePanel = function(config){
33799     var el = config.el;
33800     var tree = config.tree;
33801     delete config.tree; 
33802     delete config.el; // hopefull!
33803     
33804     // wrapper for IE7 strict & safari scroll issue
33805     
33806     var treeEl = el.createChild();
33807     config.resizeEl = treeEl;
33808     
33809     
33810     
33811     Roo.TreePanel.superclass.constructor.call(this, el, config);
33812  
33813  
33814     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33815     //console.log(tree);
33816     this.on('activate', function()
33817     {
33818         if (this.tree.rendered) {
33819             return;
33820         }
33821         //console.log('render tree');
33822         this.tree.render();
33823     });
33824     // this should not be needed.. - it's actually the 'el' that resizes?
33825     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33826     
33827     //this.on('resize',  function (cp, w, h) {
33828     //        this.tree.innerCt.setWidth(w);
33829     //        this.tree.innerCt.setHeight(h);
33830     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33831     //});
33832
33833         
33834     
33835 };
33836
33837 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33838     fitToFrame : true,
33839     autoScroll : true
33840 });
33841
33842
33843
33844
33845
33846
33847
33848
33849
33850
33851
33852 /*
33853  * Based on:
33854  * Ext JS Library 1.1.1
33855  * Copyright(c) 2006-2007, Ext JS, LLC.
33856  *
33857  * Originally Released Under LGPL - original licence link has changed is not relivant.
33858  *
33859  * Fork - LGPL
33860  * <script type="text/javascript">
33861  */
33862  
33863
33864 /**
33865  * @class Roo.ReaderLayout
33866  * @extends Roo.BorderLayout
33867  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33868  * center region containing two nested regions (a top one for a list view and one for item preview below),
33869  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33870  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33871  * expedites the setup of the overall layout and regions for this common application style.
33872  * Example:
33873  <pre><code>
33874 var reader = new Roo.ReaderLayout();
33875 var CP = Roo.ContentPanel;  // shortcut for adding
33876
33877 reader.beginUpdate();
33878 reader.add("north", new CP("north", "North"));
33879 reader.add("west", new CP("west", {title: "West"}));
33880 reader.add("east", new CP("east", {title: "East"}));
33881
33882 reader.regions.listView.add(new CP("listView", "List"));
33883 reader.regions.preview.add(new CP("preview", "Preview"));
33884 reader.endUpdate();
33885 </code></pre>
33886 * @constructor
33887 * Create a new ReaderLayout
33888 * @param {Object} config Configuration options
33889 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33890 * document.body if omitted)
33891 */
33892 Roo.ReaderLayout = function(config, renderTo){
33893     var c = config || {size:{}};
33894     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33895         north: c.north !== false ? Roo.apply({
33896             split:false,
33897             initialSize: 32,
33898             titlebar: false
33899         }, c.north) : false,
33900         west: c.west !== false ? Roo.apply({
33901             split:true,
33902             initialSize: 200,
33903             minSize: 175,
33904             maxSize: 400,
33905             titlebar: true,
33906             collapsible: true,
33907             animate: true,
33908             margins:{left:5,right:0,bottom:5,top:5},
33909             cmargins:{left:5,right:5,bottom:5,top:5}
33910         }, c.west) : false,
33911         east: c.east !== false ? Roo.apply({
33912             split:true,
33913             initialSize: 200,
33914             minSize: 175,
33915             maxSize: 400,
33916             titlebar: true,
33917             collapsible: true,
33918             animate: true,
33919             margins:{left:0,right:5,bottom:5,top:5},
33920             cmargins:{left:5,right:5,bottom:5,top:5}
33921         }, c.east) : false,
33922         center: Roo.apply({
33923             tabPosition: 'top',
33924             autoScroll:false,
33925             closeOnTab: true,
33926             titlebar:false,
33927             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33928         }, c.center)
33929     });
33930
33931     this.el.addClass('x-reader');
33932
33933     this.beginUpdate();
33934
33935     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33936         south: c.preview !== false ? Roo.apply({
33937             split:true,
33938             initialSize: 200,
33939             minSize: 100,
33940             autoScroll:true,
33941             collapsible:true,
33942             titlebar: true,
33943             cmargins:{top:5,left:0, right:0, bottom:0}
33944         }, c.preview) : false,
33945         center: Roo.apply({
33946             autoScroll:false,
33947             titlebar:false,
33948             minHeight:200
33949         }, c.listView)
33950     });
33951     this.add('center', new Roo.NestedLayoutPanel(inner,
33952             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33953
33954     this.endUpdate();
33955
33956     this.regions.preview = inner.getRegion('south');
33957     this.regions.listView = inner.getRegion('center');
33958 };
33959
33960 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33961  * Based on:
33962  * Ext JS Library 1.1.1
33963  * Copyright(c) 2006-2007, Ext JS, LLC.
33964  *
33965  * Originally Released Under LGPL - original licence link has changed is not relivant.
33966  *
33967  * Fork - LGPL
33968  * <script type="text/javascript">
33969  */
33970  
33971 /**
33972  * @class Roo.grid.Grid
33973  * @extends Roo.util.Observable
33974  * This class represents the primary interface of a component based grid control.
33975  * <br><br>Usage:<pre><code>
33976  var grid = new Roo.grid.Grid("my-container-id", {
33977      ds: myDataStore,
33978      cm: myColModel,
33979      selModel: mySelectionModel,
33980      autoSizeColumns: true,
33981      monitorWindowResize: false,
33982      trackMouseOver: true
33983  });
33984  // set any options
33985  grid.render();
33986  * </code></pre>
33987  * <b>Common Problems:</b><br/>
33988  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33989  * element will correct this<br/>
33990  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33991  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33992  * are unpredictable.<br/>
33993  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33994  * grid to calculate dimensions/offsets.<br/>
33995   * @constructor
33996  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33997  * The container MUST have some type of size defined for the grid to fill. The container will be
33998  * automatically set to position relative if it isn't already.
33999  * @param {Object} config A config object that sets properties on this grid.
34000  */
34001 Roo.grid.Grid = function(container, config){
34002         // initialize the container
34003         this.container = Roo.get(container);
34004         this.container.update("");
34005         this.container.setStyle("overflow", "hidden");
34006     this.container.addClass('x-grid-container');
34007
34008     this.id = this.container.id;
34009
34010     Roo.apply(this, config);
34011     // check and correct shorthanded configs
34012     if(this.ds){
34013         this.dataSource = this.ds;
34014         delete this.ds;
34015     }
34016     if(this.cm){
34017         this.colModel = this.cm;
34018         delete this.cm;
34019     }
34020     if(this.sm){
34021         this.selModel = this.sm;
34022         delete this.sm;
34023     }
34024
34025     if (this.selModel) {
34026         this.selModel = Roo.factory(this.selModel, Roo.grid);
34027         this.sm = this.selModel;
34028         this.sm.xmodule = this.xmodule || false;
34029     }
34030     if (typeof(this.colModel.config) == 'undefined') {
34031         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34032         this.cm = this.colModel;
34033         this.cm.xmodule = this.xmodule || false;
34034     }
34035     if (this.dataSource) {
34036         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34037         this.ds = this.dataSource;
34038         this.ds.xmodule = this.xmodule || false;
34039          
34040     }
34041     
34042     
34043     
34044     if(this.width){
34045         this.container.setWidth(this.width);
34046     }
34047
34048     if(this.height){
34049         this.container.setHeight(this.height);
34050     }
34051     /** @private */
34052         this.addEvents({
34053         // raw events
34054         /**
34055          * @event click
34056          * The raw click event for the entire grid.
34057          * @param {Roo.EventObject} e
34058          */
34059         "click" : true,
34060         /**
34061          * @event dblclick
34062          * The raw dblclick event for the entire grid.
34063          * @param {Roo.EventObject} e
34064          */
34065         "dblclick" : true,
34066         /**
34067          * @event contextmenu
34068          * The raw contextmenu event for the entire grid.
34069          * @param {Roo.EventObject} e
34070          */
34071         "contextmenu" : true,
34072         /**
34073          * @event mousedown
34074          * The raw mousedown event for the entire grid.
34075          * @param {Roo.EventObject} e
34076          */
34077         "mousedown" : true,
34078         /**
34079          * @event mouseup
34080          * The raw mouseup event for the entire grid.
34081          * @param {Roo.EventObject} e
34082          */
34083         "mouseup" : true,
34084         /**
34085          * @event mouseover
34086          * The raw mouseover event for the entire grid.
34087          * @param {Roo.EventObject} e
34088          */
34089         "mouseover" : true,
34090         /**
34091          * @event mouseout
34092          * The raw mouseout event for the entire grid.
34093          * @param {Roo.EventObject} e
34094          */
34095         "mouseout" : true,
34096         /**
34097          * @event keypress
34098          * The raw keypress event for the entire grid.
34099          * @param {Roo.EventObject} e
34100          */
34101         "keypress" : true,
34102         /**
34103          * @event keydown
34104          * The raw keydown event for the entire grid.
34105          * @param {Roo.EventObject} e
34106          */
34107         "keydown" : true,
34108
34109         // custom events
34110
34111         /**
34112          * @event cellclick
34113          * Fires when a cell is clicked
34114          * @param {Grid} this
34115          * @param {Number} rowIndex
34116          * @param {Number} columnIndex
34117          * @param {Roo.EventObject} e
34118          */
34119         "cellclick" : true,
34120         /**
34121          * @event celldblclick
34122          * Fires when a cell is double clicked
34123          * @param {Grid} this
34124          * @param {Number} rowIndex
34125          * @param {Number} columnIndex
34126          * @param {Roo.EventObject} e
34127          */
34128         "celldblclick" : true,
34129         /**
34130          * @event rowclick
34131          * Fires when a row is clicked
34132          * @param {Grid} this
34133          * @param {Number} rowIndex
34134          * @param {Roo.EventObject} e
34135          */
34136         "rowclick" : true,
34137         /**
34138          * @event rowdblclick
34139          * Fires when a row is double clicked
34140          * @param {Grid} this
34141          * @param {Number} rowIndex
34142          * @param {Roo.EventObject} e
34143          */
34144         "rowdblclick" : true,
34145         /**
34146          * @event headerclick
34147          * Fires when a header is clicked
34148          * @param {Grid} this
34149          * @param {Number} columnIndex
34150          * @param {Roo.EventObject} e
34151          */
34152         "headerclick" : true,
34153         /**
34154          * @event headerdblclick
34155          * Fires when a header cell is double clicked
34156          * @param {Grid} this
34157          * @param {Number} columnIndex
34158          * @param {Roo.EventObject} e
34159          */
34160         "headerdblclick" : true,
34161         /**
34162          * @event rowcontextmenu
34163          * Fires when a row is right clicked
34164          * @param {Grid} this
34165          * @param {Number} rowIndex
34166          * @param {Roo.EventObject} e
34167          */
34168         "rowcontextmenu" : true,
34169         /**
34170          * @event cellcontextmenu
34171          * Fires when a cell is right clicked
34172          * @param {Grid} this
34173          * @param {Number} rowIndex
34174          * @param {Number} cellIndex
34175          * @param {Roo.EventObject} e
34176          */
34177          "cellcontextmenu" : true,
34178         /**
34179          * @event headercontextmenu
34180          * Fires when a header is right clicked
34181          * @param {Grid} this
34182          * @param {Number} columnIndex
34183          * @param {Roo.EventObject} e
34184          */
34185         "headercontextmenu" : true,
34186         /**
34187          * @event bodyscroll
34188          * Fires when the body element is scrolled
34189          * @param {Number} scrollLeft
34190          * @param {Number} scrollTop
34191          */
34192         "bodyscroll" : true,
34193         /**
34194          * @event columnresize
34195          * Fires when the user resizes a column
34196          * @param {Number} columnIndex
34197          * @param {Number} newSize
34198          */
34199         "columnresize" : true,
34200         /**
34201          * @event columnmove
34202          * Fires when the user moves a column
34203          * @param {Number} oldIndex
34204          * @param {Number} newIndex
34205          */
34206         "columnmove" : true,
34207         /**
34208          * @event startdrag
34209          * Fires when row(s) start being dragged
34210          * @param {Grid} this
34211          * @param {Roo.GridDD} dd The drag drop object
34212          * @param {event} e The raw browser event
34213          */
34214         "startdrag" : true,
34215         /**
34216          * @event enddrag
34217          * Fires when a drag operation is complete
34218          * @param {Grid} this
34219          * @param {Roo.GridDD} dd The drag drop object
34220          * @param {event} e The raw browser event
34221          */
34222         "enddrag" : true,
34223         /**
34224          * @event dragdrop
34225          * Fires when dragged row(s) are dropped on a valid DD target
34226          * @param {Grid} this
34227          * @param {Roo.GridDD} dd The drag drop object
34228          * @param {String} targetId The target drag drop object
34229          * @param {event} e The raw browser event
34230          */
34231         "dragdrop" : true,
34232         /**
34233          * @event dragover
34234          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34235          * @param {Grid} this
34236          * @param {Roo.GridDD} dd The drag drop object
34237          * @param {String} targetId The target drag drop object
34238          * @param {event} e The raw browser event
34239          */
34240         "dragover" : true,
34241         /**
34242          * @event dragenter
34243          *  Fires when the dragged row(s) first cross another DD target while being dragged
34244          * @param {Grid} this
34245          * @param {Roo.GridDD} dd The drag drop object
34246          * @param {String} targetId The target drag drop object
34247          * @param {event} e The raw browser event
34248          */
34249         "dragenter" : true,
34250         /**
34251          * @event dragout
34252          * Fires when the dragged row(s) leave another DD target while being dragged
34253          * @param {Grid} this
34254          * @param {Roo.GridDD} dd The drag drop object
34255          * @param {String} targetId The target drag drop object
34256          * @param {event} e The raw browser event
34257          */
34258         "dragout" : true,
34259         /**
34260          * @event rowclass
34261          * Fires when a row is rendered, so you can change add a style to it.
34262          * @param {GridView} gridview   The grid view
34263          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34264          */
34265         'rowclass' : true,
34266
34267         /**
34268          * @event render
34269          * Fires when the grid is rendered
34270          * @param {Grid} grid
34271          */
34272         'render' : true
34273     });
34274
34275     Roo.grid.Grid.superclass.constructor.call(this);
34276 };
34277 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34278     
34279     /**
34280      * @cfg {String} ddGroup - drag drop group.
34281      */
34282
34283     /**
34284      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34285      */
34286     minColumnWidth : 25,
34287
34288     /**
34289      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34290      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34291      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34292      */
34293     autoSizeColumns : false,
34294
34295     /**
34296      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34297      */
34298     autoSizeHeaders : true,
34299
34300     /**
34301      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34302      */
34303     monitorWindowResize : true,
34304
34305     /**
34306      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34307      * rows measured to get a columns size. Default is 0 (all rows).
34308      */
34309     maxRowsToMeasure : 0,
34310
34311     /**
34312      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34313      */
34314     trackMouseOver : true,
34315
34316     /**
34317     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34318     */
34319     
34320     /**
34321     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34322     */
34323     enableDragDrop : false,
34324     
34325     /**
34326     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34327     */
34328     enableColumnMove : true,
34329     
34330     /**
34331     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34332     */
34333     enableColumnHide : true,
34334     
34335     /**
34336     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34337     */
34338     enableRowHeightSync : false,
34339     
34340     /**
34341     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34342     */
34343     stripeRows : true,
34344     
34345     /**
34346     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34347     */
34348     autoHeight : false,
34349
34350     /**
34351      * @cfg {String} autoExpandColumn The id (or dataIndex) of a column in this grid that should expand to fill unused space. This id can not be 0. Default is false.
34352      */
34353     autoExpandColumn : false,
34354
34355     /**
34356     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34357     * Default is 50.
34358     */
34359     autoExpandMin : 50,
34360
34361     /**
34362     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34363     */
34364     autoExpandMax : 1000,
34365
34366     /**
34367     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34368     */
34369     view : null,
34370
34371     /**
34372     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34373     */
34374     loadMask : false,
34375     /**
34376     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34377     */
34378     dropTarget: false,
34379     
34380    
34381     
34382     // private
34383     rendered : false,
34384
34385     /**
34386     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34387     * of a fixed width. Default is false.
34388     */
34389     /**
34390     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34391     */
34392     /**
34393      * Called once after all setup has been completed and the grid is ready to be rendered.
34394      * @return {Roo.grid.Grid} this
34395      */
34396     render : function()
34397     {
34398         var c = this.container;
34399         // try to detect autoHeight/width mode
34400         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34401             this.autoHeight = true;
34402         }
34403         var view = this.getView();
34404         view.init(this);
34405
34406         c.on("click", this.onClick, this);
34407         c.on("dblclick", this.onDblClick, this);
34408         c.on("contextmenu", this.onContextMenu, this);
34409         c.on("keydown", this.onKeyDown, this);
34410
34411         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34412
34413         this.getSelectionModel().init(this);
34414
34415         view.render();
34416
34417         if(this.loadMask){
34418             this.loadMask = new Roo.LoadMask(this.container,
34419                     Roo.apply({store:this.dataSource}, this.loadMask));
34420         }
34421         
34422         
34423         if (this.toolbar && this.toolbar.xtype) {
34424             this.toolbar.container = this.getView().getHeaderPanel(true);
34425             this.toolbar = new Roo.Toolbar(this.toolbar);
34426         }
34427         if (this.footer && this.footer.xtype) {
34428             this.footer.dataSource = this.getDataSource();
34429             this.footer.container = this.getView().getFooterPanel(true);
34430             this.footer = Roo.factory(this.footer, Roo);
34431         }
34432         if (this.dropTarget && this.dropTarget.xtype) {
34433             delete this.dropTarget.xtype;
34434             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34435         }
34436         
34437         
34438         this.rendered = true;
34439         this.fireEvent('render', this);
34440         return this;
34441     },
34442
34443         /**
34444          * Reconfigures the grid to use a different Store and Column Model.
34445          * The View will be bound to the new objects and refreshed.
34446          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34447          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34448          */
34449     reconfigure : function(dataSource, colModel){
34450         if(this.loadMask){
34451             this.loadMask.destroy();
34452             this.loadMask = new Roo.LoadMask(this.container,
34453                     Roo.apply({store:dataSource}, this.loadMask));
34454         }
34455         this.view.bind(dataSource, colModel);
34456         this.dataSource = dataSource;
34457         this.colModel = colModel;
34458         this.view.refresh(true);
34459     },
34460
34461     // private
34462     onKeyDown : function(e){
34463         this.fireEvent("keydown", e);
34464     },
34465
34466     /**
34467      * Destroy this grid.
34468      * @param {Boolean} removeEl True to remove the element
34469      */
34470     destroy : function(removeEl, keepListeners){
34471         if(this.loadMask){
34472             this.loadMask.destroy();
34473         }
34474         var c = this.container;
34475         c.removeAllListeners();
34476         this.view.destroy();
34477         this.colModel.purgeListeners();
34478         if(!keepListeners){
34479             this.purgeListeners();
34480         }
34481         c.update("");
34482         if(removeEl === true){
34483             c.remove();
34484         }
34485     },
34486
34487     // private
34488     processEvent : function(name, e){
34489         this.fireEvent(name, e);
34490         var t = e.getTarget();
34491         var v = this.view;
34492         var header = v.findHeaderIndex(t);
34493         if(header !== false){
34494             this.fireEvent("header" + name, this, header, e);
34495         }else{
34496             var row = v.findRowIndex(t);
34497             var cell = v.findCellIndex(t);
34498             if(row !== false){
34499                 this.fireEvent("row" + name, this, row, e);
34500                 if(cell !== false){
34501                     this.fireEvent("cell" + name, this, row, cell, e);
34502                 }
34503             }
34504         }
34505     },
34506
34507     // private
34508     onClick : function(e){
34509         this.processEvent("click", e);
34510     },
34511
34512     // private
34513     onContextMenu : function(e, t){
34514         this.processEvent("contextmenu", e);
34515     },
34516
34517     // private
34518     onDblClick : function(e){
34519         this.processEvent("dblclick", e);
34520     },
34521
34522     // private
34523     walkCells : function(row, col, step, fn, scope){
34524         var cm = this.colModel, clen = cm.getColumnCount();
34525         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34526         if(step < 0){
34527             if(col < 0){
34528                 row--;
34529                 first = false;
34530             }
34531             while(row >= 0){
34532                 if(!first){
34533                     col = clen-1;
34534                 }
34535                 first = false;
34536                 while(col >= 0){
34537                     if(fn.call(scope || this, row, col, cm) === true){
34538                         return [row, col];
34539                     }
34540                     col--;
34541                 }
34542                 row--;
34543             }
34544         } else {
34545             if(col >= clen){
34546                 row++;
34547                 first = false;
34548             }
34549             while(row < rlen){
34550                 if(!first){
34551                     col = 0;
34552                 }
34553                 first = false;
34554                 while(col < clen){
34555                     if(fn.call(scope || this, row, col, cm) === true){
34556                         return [row, col];
34557                     }
34558                     col++;
34559                 }
34560                 row++;
34561             }
34562         }
34563         return null;
34564     },
34565
34566     // private
34567     getSelections : function(){
34568         return this.selModel.getSelections();
34569     },
34570
34571     /**
34572      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34573      * but if manual update is required this method will initiate it.
34574      */
34575     autoSize : function(){
34576         if(this.rendered){
34577             this.view.layout();
34578             if(this.view.adjustForScroll){
34579                 this.view.adjustForScroll();
34580             }
34581         }
34582     },
34583
34584     /**
34585      * Returns the grid's underlying element.
34586      * @return {Element} The element
34587      */
34588     getGridEl : function(){
34589         return this.container;
34590     },
34591
34592     // private for compatibility, overridden by editor grid
34593     stopEditing : function(){},
34594
34595     /**
34596      * Returns the grid's SelectionModel.
34597      * @return {SelectionModel}
34598      */
34599     getSelectionModel : function(){
34600         if(!this.selModel){
34601             this.selModel = new Roo.grid.RowSelectionModel();
34602         }
34603         return this.selModel;
34604     },
34605
34606     /**
34607      * Returns the grid's DataSource.
34608      * @return {DataSource}
34609      */
34610     getDataSource : function(){
34611         return this.dataSource;
34612     },
34613
34614     /**
34615      * Returns the grid's ColumnModel.
34616      * @return {ColumnModel}
34617      */
34618     getColumnModel : function(){
34619         return this.colModel;
34620     },
34621
34622     /**
34623      * Returns the grid's GridView object.
34624      * @return {GridView}
34625      */
34626     getView : function(){
34627         if(!this.view){
34628             this.view = new Roo.grid.GridView(this.viewConfig);
34629         }
34630         return this.view;
34631     },
34632     /**
34633      * Called to get grid's drag proxy text, by default returns this.ddText.
34634      * @return {String}
34635      */
34636     getDragDropText : function(){
34637         var count = this.selModel.getCount();
34638         return String.format(this.ddText, count, count == 1 ? '' : 's');
34639     }
34640 });
34641 /**
34642  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34643  * %0 is replaced with the number of selected rows.
34644  * @type String
34645  */
34646 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34647  * Based on:
34648  * Ext JS Library 1.1.1
34649  * Copyright(c) 2006-2007, Ext JS, LLC.
34650  *
34651  * Originally Released Under LGPL - original licence link has changed is not relivant.
34652  *
34653  * Fork - LGPL
34654  * <script type="text/javascript">
34655  */
34656  
34657 Roo.grid.AbstractGridView = function(){
34658         this.grid = null;
34659         
34660         this.events = {
34661             "beforerowremoved" : true,
34662             "beforerowsinserted" : true,
34663             "beforerefresh" : true,
34664             "rowremoved" : true,
34665             "rowsinserted" : true,
34666             "rowupdated" : true,
34667             "refresh" : true
34668         };
34669     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34670 };
34671
34672 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34673     rowClass : "x-grid-row",
34674     cellClass : "x-grid-cell",
34675     tdClass : "x-grid-td",
34676     hdClass : "x-grid-hd",
34677     splitClass : "x-grid-hd-split",
34678     
34679         init: function(grid){
34680         this.grid = grid;
34681                 var cid = this.grid.getGridEl().id;
34682         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34683         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34684         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34685         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34686         },
34687         
34688         getColumnRenderers : function(){
34689         var renderers = [];
34690         var cm = this.grid.colModel;
34691         var colCount = cm.getColumnCount();
34692         for(var i = 0; i < colCount; i++){
34693             renderers[i] = cm.getRenderer(i);
34694         }
34695         return renderers;
34696     },
34697     
34698     getColumnIds : function(){
34699         var ids = [];
34700         var cm = this.grid.colModel;
34701         var colCount = cm.getColumnCount();
34702         for(var i = 0; i < colCount; i++){
34703             ids[i] = cm.getColumnId(i);
34704         }
34705         return ids;
34706     },
34707     
34708     getDataIndexes : function(){
34709         if(!this.indexMap){
34710             this.indexMap = this.buildIndexMap();
34711         }
34712         return this.indexMap.colToData;
34713     },
34714     
34715     getColumnIndexByDataIndex : function(dataIndex){
34716         if(!this.indexMap){
34717             this.indexMap = this.buildIndexMap();
34718         }
34719         return this.indexMap.dataToCol[dataIndex];
34720     },
34721     
34722     /**
34723      * Set a css style for a column dynamically. 
34724      * @param {Number} colIndex The index of the column
34725      * @param {String} name The css property name
34726      * @param {String} value The css value
34727      */
34728     setCSSStyle : function(colIndex, name, value){
34729         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34730         Roo.util.CSS.updateRule(selector, name, value);
34731     },
34732     
34733     generateRules : function(cm){
34734         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34735         Roo.util.CSS.removeStyleSheet(rulesId);
34736         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34737             var cid = cm.getColumnId(i);
34738             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34739                          this.tdSelector, cid, " {\n}\n",
34740                          this.hdSelector, cid, " {\n}\n",
34741                          this.splitSelector, cid, " {\n}\n");
34742         }
34743         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34744     }
34745 });/*
34746  * Based on:
34747  * Ext JS Library 1.1.1
34748  * Copyright(c) 2006-2007, Ext JS, LLC.
34749  *
34750  * Originally Released Under LGPL - original licence link has changed is not relivant.
34751  *
34752  * Fork - LGPL
34753  * <script type="text/javascript">
34754  */
34755
34756 // private
34757 // This is a support class used internally by the Grid components
34758 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34759     this.grid = grid;
34760     this.view = grid.getView();
34761     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34762     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34763     if(hd2){
34764         this.setHandleElId(Roo.id(hd));
34765         this.setOuterHandleElId(Roo.id(hd2));
34766     }
34767     this.scroll = false;
34768 };
34769 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34770     maxDragWidth: 120,
34771     getDragData : function(e){
34772         var t = Roo.lib.Event.getTarget(e);
34773         var h = this.view.findHeaderCell(t);
34774         if(h){
34775             return {ddel: h.firstChild, header:h};
34776         }
34777         return false;
34778     },
34779
34780     onInitDrag : function(e){
34781         this.view.headersDisabled = true;
34782         var clone = this.dragData.ddel.cloneNode(true);
34783         clone.id = Roo.id();
34784         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34785         this.proxy.update(clone);
34786         return true;
34787     },
34788
34789     afterValidDrop : function(){
34790         var v = this.view;
34791         setTimeout(function(){
34792             v.headersDisabled = false;
34793         }, 50);
34794     },
34795
34796     afterInvalidDrop : function(){
34797         var v = this.view;
34798         setTimeout(function(){
34799             v.headersDisabled = false;
34800         }, 50);
34801     }
34802 });
34803 /*
34804  * Based on:
34805  * Ext JS Library 1.1.1
34806  * Copyright(c) 2006-2007, Ext JS, LLC.
34807  *
34808  * Originally Released Under LGPL - original licence link has changed is not relivant.
34809  *
34810  * Fork - LGPL
34811  * <script type="text/javascript">
34812  */
34813 // private
34814 // This is a support class used internally by the Grid components
34815 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34816     this.grid = grid;
34817     this.view = grid.getView();
34818     // split the proxies so they don't interfere with mouse events
34819     this.proxyTop = Roo.DomHelper.append(document.body, {
34820         cls:"col-move-top", html:"&#160;"
34821     }, true);
34822     this.proxyBottom = Roo.DomHelper.append(document.body, {
34823         cls:"col-move-bottom", html:"&#160;"
34824     }, true);
34825     this.proxyTop.hide = this.proxyBottom.hide = function(){
34826         this.setLeftTop(-100,-100);
34827         this.setStyle("visibility", "hidden");
34828     };
34829     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34830     // temporarily disabled
34831     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34832     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34833 };
34834 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34835     proxyOffsets : [-4, -9],
34836     fly: Roo.Element.fly,
34837
34838     getTargetFromEvent : function(e){
34839         var t = Roo.lib.Event.getTarget(e);
34840         var cindex = this.view.findCellIndex(t);
34841         if(cindex !== false){
34842             return this.view.getHeaderCell(cindex);
34843         }
34844         return null;
34845     },
34846
34847     nextVisible : function(h){
34848         var v = this.view, cm = this.grid.colModel;
34849         h = h.nextSibling;
34850         while(h){
34851             if(!cm.isHidden(v.getCellIndex(h))){
34852                 return h;
34853             }
34854             h = h.nextSibling;
34855         }
34856         return null;
34857     },
34858
34859     prevVisible : function(h){
34860         var v = this.view, cm = this.grid.colModel;
34861         h = h.prevSibling;
34862         while(h){
34863             if(!cm.isHidden(v.getCellIndex(h))){
34864                 return h;
34865             }
34866             h = h.prevSibling;
34867         }
34868         return null;
34869     },
34870
34871     positionIndicator : function(h, n, e){
34872         var x = Roo.lib.Event.getPageX(e);
34873         var r = Roo.lib.Dom.getRegion(n.firstChild);
34874         var px, pt, py = r.top + this.proxyOffsets[1];
34875         if((r.right - x) <= (r.right-r.left)/2){
34876             px = r.right+this.view.borderWidth;
34877             pt = "after";
34878         }else{
34879             px = r.left;
34880             pt = "before";
34881         }
34882         var oldIndex = this.view.getCellIndex(h);
34883         var newIndex = this.view.getCellIndex(n);
34884
34885         if(this.grid.colModel.isFixed(newIndex)){
34886             return false;
34887         }
34888
34889         var locked = this.grid.colModel.isLocked(newIndex);
34890
34891         if(pt == "after"){
34892             newIndex++;
34893         }
34894         if(oldIndex < newIndex){
34895             newIndex--;
34896         }
34897         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34898             return false;
34899         }
34900         px +=  this.proxyOffsets[0];
34901         this.proxyTop.setLeftTop(px, py);
34902         this.proxyTop.show();
34903         if(!this.bottomOffset){
34904             this.bottomOffset = this.view.mainHd.getHeight();
34905         }
34906         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34907         this.proxyBottom.show();
34908         return pt;
34909     },
34910
34911     onNodeEnter : function(n, dd, e, data){
34912         if(data.header != n){
34913             this.positionIndicator(data.header, n, e);
34914         }
34915     },
34916
34917     onNodeOver : function(n, dd, e, data){
34918         var result = false;
34919         if(data.header != n){
34920             result = this.positionIndicator(data.header, n, e);
34921         }
34922         if(!result){
34923             this.proxyTop.hide();
34924             this.proxyBottom.hide();
34925         }
34926         return result ? this.dropAllowed : this.dropNotAllowed;
34927     },
34928
34929     onNodeOut : function(n, dd, e, data){
34930         this.proxyTop.hide();
34931         this.proxyBottom.hide();
34932     },
34933
34934     onNodeDrop : function(n, dd, e, data){
34935         var h = data.header;
34936         if(h != n){
34937             var cm = this.grid.colModel;
34938             var x = Roo.lib.Event.getPageX(e);
34939             var r = Roo.lib.Dom.getRegion(n.firstChild);
34940             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34941             var oldIndex = this.view.getCellIndex(h);
34942             var newIndex = this.view.getCellIndex(n);
34943             var locked = cm.isLocked(newIndex);
34944             if(pt == "after"){
34945                 newIndex++;
34946             }
34947             if(oldIndex < newIndex){
34948                 newIndex--;
34949             }
34950             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34951                 return false;
34952             }
34953             cm.setLocked(oldIndex, locked, true);
34954             cm.moveColumn(oldIndex, newIndex);
34955             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34956             return true;
34957         }
34958         return false;
34959     }
34960 });
34961 /*
34962  * Based on:
34963  * Ext JS Library 1.1.1
34964  * Copyright(c) 2006-2007, Ext JS, LLC.
34965  *
34966  * Originally Released Under LGPL - original licence link has changed is not relivant.
34967  *
34968  * Fork - LGPL
34969  * <script type="text/javascript">
34970  */
34971   
34972 /**
34973  * @class Roo.grid.GridView
34974  * @extends Roo.util.Observable
34975  *
34976  * @constructor
34977  * @param {Object} config
34978  */
34979 Roo.grid.GridView = function(config){
34980     Roo.grid.GridView.superclass.constructor.call(this);
34981     this.el = null;
34982
34983     Roo.apply(this, config);
34984 };
34985
34986 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34987
34988     
34989     rowClass : "x-grid-row",
34990
34991     cellClass : "x-grid-col",
34992
34993     tdClass : "x-grid-td",
34994
34995     hdClass : "x-grid-hd",
34996
34997     splitClass : "x-grid-split",
34998
34999     sortClasses : ["sort-asc", "sort-desc"],
35000
35001     enableMoveAnim : false,
35002
35003     hlColor: "C3DAF9",
35004
35005     dh : Roo.DomHelper,
35006
35007     fly : Roo.Element.fly,
35008
35009     css : Roo.util.CSS,
35010
35011     borderWidth: 1,
35012
35013     splitOffset: 3,
35014
35015     scrollIncrement : 22,
35016
35017     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
35018
35019     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
35020
35021     bind : function(ds, cm){
35022         if(this.ds){
35023             this.ds.un("load", this.onLoad, this);
35024             this.ds.un("datachanged", this.onDataChange, this);
35025             this.ds.un("add", this.onAdd, this);
35026             this.ds.un("remove", this.onRemove, this);
35027             this.ds.un("update", this.onUpdate, this);
35028             this.ds.un("clear", this.onClear, this);
35029         }
35030         if(ds){
35031             ds.on("load", this.onLoad, this);
35032             ds.on("datachanged", this.onDataChange, this);
35033             ds.on("add", this.onAdd, this);
35034             ds.on("remove", this.onRemove, this);
35035             ds.on("update", this.onUpdate, this);
35036             ds.on("clear", this.onClear, this);
35037         }
35038         this.ds = ds;
35039
35040         if(this.cm){
35041             this.cm.un("widthchange", this.onColWidthChange, this);
35042             this.cm.un("headerchange", this.onHeaderChange, this);
35043             this.cm.un("hiddenchange", this.onHiddenChange, this);
35044             this.cm.un("columnmoved", this.onColumnMove, this);
35045             this.cm.un("columnlockchange", this.onColumnLock, this);
35046         }
35047         if(cm){
35048             this.generateRules(cm);
35049             cm.on("widthchange", this.onColWidthChange, this);
35050             cm.on("headerchange", this.onHeaderChange, this);
35051             cm.on("hiddenchange", this.onHiddenChange, this);
35052             cm.on("columnmoved", this.onColumnMove, this);
35053             cm.on("columnlockchange", this.onColumnLock, this);
35054         }
35055         this.cm = cm;
35056     },
35057
35058     init: function(grid){
35059         Roo.grid.GridView.superclass.init.call(this, grid);
35060
35061         this.bind(grid.dataSource, grid.colModel);
35062
35063         grid.on("headerclick", this.handleHeaderClick, this);
35064
35065         if(grid.trackMouseOver){
35066             grid.on("mouseover", this.onRowOver, this);
35067             grid.on("mouseout", this.onRowOut, this);
35068         }
35069         grid.cancelTextSelection = function(){};
35070         this.gridId = grid.id;
35071
35072         var tpls = this.templates || {};
35073
35074         if(!tpls.master){
35075             tpls.master = new Roo.Template(
35076                '<div class="x-grid" hidefocus="true">',
35077                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35078                   '<div class="x-grid-topbar"></div>',
35079                   '<div class="x-grid-scroller"><div></div></div>',
35080                   '<div class="x-grid-locked">',
35081                       '<div class="x-grid-header">{lockedHeader}</div>',
35082                       '<div class="x-grid-body">{lockedBody}</div>',
35083                   "</div>",
35084                   '<div class="x-grid-viewport">',
35085                       '<div class="x-grid-header">{header}</div>',
35086                       '<div class="x-grid-body">{body}</div>',
35087                   "</div>",
35088                   '<div class="x-grid-bottombar"></div>',
35089                  
35090                   '<div class="x-grid-resize-proxy">&#160;</div>',
35091                "</div>"
35092             );
35093             tpls.master.disableformats = true;
35094         }
35095
35096         if(!tpls.header){
35097             tpls.header = new Roo.Template(
35098                '<table border="0" cellspacing="0" cellpadding="0">',
35099                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35100                "</table>{splits}"
35101             );
35102             tpls.header.disableformats = true;
35103         }
35104         tpls.header.compile();
35105
35106         if(!tpls.hcell){
35107             tpls.hcell = new Roo.Template(
35108                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35109                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35110                 "</div></td>"
35111              );
35112              tpls.hcell.disableFormats = true;
35113         }
35114         tpls.hcell.compile();
35115
35116         if(!tpls.hsplit){
35117             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
35118             tpls.hsplit.disableFormats = true;
35119         }
35120         tpls.hsplit.compile();
35121
35122         if(!tpls.body){
35123             tpls.body = new Roo.Template(
35124                '<table border="0" cellspacing="0" cellpadding="0">',
35125                "<tbody>{rows}</tbody>",
35126                "</table>"
35127             );
35128             tpls.body.disableFormats = true;
35129         }
35130         tpls.body.compile();
35131
35132         if(!tpls.row){
35133             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35134             tpls.row.disableFormats = true;
35135         }
35136         tpls.row.compile();
35137
35138         if(!tpls.cell){
35139             tpls.cell = new Roo.Template(
35140                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35141                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
35142                 "</td>"
35143             );
35144             tpls.cell.disableFormats = true;
35145         }
35146         tpls.cell.compile();
35147
35148         this.templates = tpls;
35149     },
35150
35151     // remap these for backwards compat
35152     onColWidthChange : function(){
35153         this.updateColumns.apply(this, arguments);
35154     },
35155     onHeaderChange : function(){
35156         this.updateHeaders.apply(this, arguments);
35157     }, 
35158     onHiddenChange : function(){
35159         this.handleHiddenChange.apply(this, arguments);
35160     },
35161     onColumnMove : function(){
35162         this.handleColumnMove.apply(this, arguments);
35163     },
35164     onColumnLock : function(){
35165         this.handleLockChange.apply(this, arguments);
35166     },
35167
35168     onDataChange : function(){
35169         this.refresh();
35170         this.updateHeaderSortState();
35171     },
35172
35173     onClear : function(){
35174         this.refresh();
35175     },
35176
35177     onUpdate : function(ds, record){
35178         this.refreshRow(record);
35179     },
35180
35181     refreshRow : function(record){
35182         var ds = this.ds, index;
35183         if(typeof record == 'number'){
35184             index = record;
35185             record = ds.getAt(index);
35186         }else{
35187             index = ds.indexOf(record);
35188         }
35189         this.insertRows(ds, index, index, true);
35190         this.onRemove(ds, record, index+1, true);
35191         this.syncRowHeights(index, index);
35192         this.layout();
35193         this.fireEvent("rowupdated", this, index, record);
35194     },
35195
35196     onAdd : function(ds, records, index){
35197         this.insertRows(ds, index, index + (records.length-1));
35198     },
35199
35200     onRemove : function(ds, record, index, isUpdate){
35201         if(isUpdate !== true){
35202             this.fireEvent("beforerowremoved", this, index, record);
35203         }
35204         var bt = this.getBodyTable(), lt = this.getLockedTable();
35205         if(bt.rows[index]){
35206             bt.firstChild.removeChild(bt.rows[index]);
35207         }
35208         if(lt.rows[index]){
35209             lt.firstChild.removeChild(lt.rows[index]);
35210         }
35211         if(isUpdate !== true){
35212             this.stripeRows(index);
35213             this.syncRowHeights(index, index);
35214             this.layout();
35215             this.fireEvent("rowremoved", this, index, record);
35216         }
35217     },
35218
35219     onLoad : function(){
35220         this.scrollToTop();
35221     },
35222
35223     /**
35224      * Scrolls the grid to the top
35225      */
35226     scrollToTop : function(){
35227         if(this.scroller){
35228             this.scroller.dom.scrollTop = 0;
35229             this.syncScroll();
35230         }
35231     },
35232
35233     /**
35234      * Gets a panel in the header of the grid that can be used for toolbars etc.
35235      * After modifying the contents of this panel a call to grid.autoSize() may be
35236      * required to register any changes in size.
35237      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35238      * @return Roo.Element
35239      */
35240     getHeaderPanel : function(doShow){
35241         if(doShow){
35242             this.headerPanel.show();
35243         }
35244         return this.headerPanel;
35245     },
35246
35247     /**
35248      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35249      * After modifying the contents of this panel a call to grid.autoSize() may be
35250      * required to register any changes in size.
35251      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35252      * @return Roo.Element
35253      */
35254     getFooterPanel : function(doShow){
35255         if(doShow){
35256             this.footerPanel.show();
35257         }
35258         return this.footerPanel;
35259     },
35260
35261     initElements : function(){
35262         var E = Roo.Element;
35263         var el = this.grid.getGridEl().dom.firstChild;
35264         var cs = el.childNodes;
35265
35266         this.el = new E(el);
35267         
35268          this.focusEl = new E(el.firstChild);
35269         this.focusEl.swallowEvent("click", true);
35270         
35271         this.headerPanel = new E(cs[1]);
35272         this.headerPanel.enableDisplayMode("block");
35273
35274         this.scroller = new E(cs[2]);
35275         this.scrollSizer = new E(this.scroller.dom.firstChild);
35276
35277         this.lockedWrap = new E(cs[3]);
35278         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35279         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35280
35281         this.mainWrap = new E(cs[4]);
35282         this.mainHd = new E(this.mainWrap.dom.firstChild);
35283         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35284
35285         this.footerPanel = new E(cs[5]);
35286         this.footerPanel.enableDisplayMode("block");
35287
35288         this.resizeProxy = new E(cs[6]);
35289
35290         this.headerSelector = String.format(
35291            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35292            this.lockedHd.id, this.mainHd.id
35293         );
35294
35295         this.splitterSelector = String.format(
35296            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35297            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35298         );
35299     },
35300     idToCssName : function(s)
35301     {
35302         return s.replace(/[^a-z0-9]+/ig, '-');
35303     },
35304
35305     getHeaderCell : function(index){
35306         return Roo.DomQuery.select(this.headerSelector)[index];
35307     },
35308
35309     getHeaderCellMeasure : function(index){
35310         return this.getHeaderCell(index).firstChild;
35311     },
35312
35313     getHeaderCellText : function(index){
35314         return this.getHeaderCell(index).firstChild.firstChild;
35315     },
35316
35317     getLockedTable : function(){
35318         return this.lockedBody.dom.firstChild;
35319     },
35320
35321     getBodyTable : function(){
35322         return this.mainBody.dom.firstChild;
35323     },
35324
35325     getLockedRow : function(index){
35326         return this.getLockedTable().rows[index];
35327     },
35328
35329     getRow : function(index){
35330         return this.getBodyTable().rows[index];
35331     },
35332
35333     getRowComposite : function(index){
35334         if(!this.rowEl){
35335             this.rowEl = new Roo.CompositeElementLite();
35336         }
35337         var els = [], lrow, mrow;
35338         if(lrow = this.getLockedRow(index)){
35339             els.push(lrow);
35340         }
35341         if(mrow = this.getRow(index)){
35342             els.push(mrow);
35343         }
35344         this.rowEl.elements = els;
35345         return this.rowEl;
35346     },
35347     /**
35348      * Gets the 'td' of the cell
35349      * 
35350      * @param {Integer} rowIndex row to select
35351      * @param {Integer} colIndex column to select
35352      * 
35353      * @return {Object} 
35354      */
35355     getCell : function(rowIndex, colIndex){
35356         var locked = this.cm.getLockedCount();
35357         var source;
35358         if(colIndex < locked){
35359             source = this.lockedBody.dom.firstChild;
35360         }else{
35361             source = this.mainBody.dom.firstChild;
35362             colIndex -= locked;
35363         }
35364         return source.rows[rowIndex].childNodes[colIndex];
35365     },
35366
35367     getCellText : function(rowIndex, colIndex){
35368         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35369     },
35370
35371     getCellBox : function(cell){
35372         var b = this.fly(cell).getBox();
35373         if(Roo.isOpera){ // opera fails to report the Y
35374             b.y = cell.offsetTop + this.mainBody.getY();
35375         }
35376         return b;
35377     },
35378
35379     getCellIndex : function(cell){
35380         var id = String(cell.className).match(this.cellRE);
35381         if(id){
35382             return parseInt(id[1], 10);
35383         }
35384         return 0;
35385     },
35386
35387     findHeaderIndex : function(n){
35388         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35389         return r ? this.getCellIndex(r) : false;
35390     },
35391
35392     findHeaderCell : function(n){
35393         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35394         return r ? r : false;
35395     },
35396
35397     findRowIndex : function(n){
35398         if(!n){
35399             return false;
35400         }
35401         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35402         return r ? r.rowIndex : false;
35403     },
35404
35405     findCellIndex : function(node){
35406         var stop = this.el.dom;
35407         while(node && node != stop){
35408             if(this.findRE.test(node.className)){
35409                 return this.getCellIndex(node);
35410             }
35411             node = node.parentNode;
35412         }
35413         return false;
35414     },
35415
35416     getColumnId : function(index){
35417         return this.cm.getColumnId(index);
35418     },
35419
35420     getSplitters : function()
35421     {
35422         if(this.splitterSelector){
35423            return Roo.DomQuery.select(this.splitterSelector);
35424         }else{
35425             return null;
35426       }
35427     },
35428
35429     getSplitter : function(index){
35430         return this.getSplitters()[index];
35431     },
35432
35433     onRowOver : function(e, t){
35434         var row;
35435         if((row = this.findRowIndex(t)) !== false){
35436             this.getRowComposite(row).addClass("x-grid-row-over");
35437         }
35438     },
35439
35440     onRowOut : function(e, t){
35441         var row;
35442         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35443             this.getRowComposite(row).removeClass("x-grid-row-over");
35444         }
35445     },
35446
35447     renderHeaders : function(){
35448         var cm = this.cm;
35449         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35450         var cb = [], lb = [], sb = [], lsb = [], p = {};
35451         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35452             p.cellId = "x-grid-hd-0-" + i;
35453             p.splitId = "x-grid-csplit-0-" + i;
35454             p.id = cm.getColumnId(i);
35455             p.title = cm.getColumnTooltip(i) || "";
35456             p.value = cm.getColumnHeader(i) || "";
35457             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35458             if(!cm.isLocked(i)){
35459                 cb[cb.length] = ct.apply(p);
35460                 sb[sb.length] = st.apply(p);
35461             }else{
35462                 lb[lb.length] = ct.apply(p);
35463                 lsb[lsb.length] = st.apply(p);
35464             }
35465         }
35466         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35467                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35468     },
35469
35470     updateHeaders : function(){
35471         var html = this.renderHeaders();
35472         this.lockedHd.update(html[0]);
35473         this.mainHd.update(html[1]);
35474     },
35475
35476     /**
35477      * Focuses the specified row.
35478      * @param {Number} row The row index
35479      */
35480     focusRow : function(row)
35481     {
35482         //Roo.log('GridView.focusRow');
35483         var x = this.scroller.dom.scrollLeft;
35484         this.focusCell(row, 0, false);
35485         this.scroller.dom.scrollLeft = x;
35486     },
35487
35488     /**
35489      * Focuses the specified cell.
35490      * @param {Number} row The row index
35491      * @param {Number} col The column index
35492      * @param {Boolean} hscroll false to disable horizontal scrolling
35493      */
35494     focusCell : function(row, col, hscroll)
35495     {
35496         //Roo.log('GridView.focusCell');
35497         var el = this.ensureVisible(row, col, hscroll);
35498         this.focusEl.alignTo(el, "tl-tl");
35499         if(Roo.isGecko){
35500             this.focusEl.focus();
35501         }else{
35502             this.focusEl.focus.defer(1, this.focusEl);
35503         }
35504     },
35505
35506     /**
35507      * Scrolls the specified cell into view
35508      * @param {Number} row The row index
35509      * @param {Number} col The column index
35510      * @param {Boolean} hscroll false to disable horizontal scrolling
35511      */
35512     ensureVisible : function(row, col, hscroll)
35513     {
35514         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35515         //return null; //disable for testing.
35516         if(typeof row != "number"){
35517             row = row.rowIndex;
35518         }
35519         if(row < 0 && row >= this.ds.getCount()){
35520             return  null;
35521         }
35522         col = (col !== undefined ? col : 0);
35523         var cm = this.grid.colModel;
35524         while(cm.isHidden(col)){
35525             col++;
35526         }
35527
35528         var el = this.getCell(row, col);
35529         if(!el){
35530             return null;
35531         }
35532         var c = this.scroller.dom;
35533
35534         var ctop = parseInt(el.offsetTop, 10);
35535         var cleft = parseInt(el.offsetLeft, 10);
35536         var cbot = ctop + el.offsetHeight;
35537         var cright = cleft + el.offsetWidth;
35538         
35539         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35540         var stop = parseInt(c.scrollTop, 10);
35541         var sleft = parseInt(c.scrollLeft, 10);
35542         var sbot = stop + ch;
35543         var sright = sleft + c.clientWidth;
35544         /*
35545         Roo.log('GridView.ensureVisible:' +
35546                 ' ctop:' + ctop +
35547                 ' c.clientHeight:' + c.clientHeight +
35548                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35549                 ' stop:' + stop +
35550                 ' cbot:' + cbot +
35551                 ' sbot:' + sbot +
35552                 ' ch:' + ch  
35553                 );
35554         */
35555         if(ctop < stop){
35556              c.scrollTop = ctop;
35557             //Roo.log("set scrolltop to ctop DISABLE?");
35558         }else if(cbot > sbot){
35559             //Roo.log("set scrolltop to cbot-ch");
35560             c.scrollTop = cbot-ch;
35561         }
35562         
35563         if(hscroll !== false){
35564             if(cleft < sleft){
35565                 c.scrollLeft = cleft;
35566             }else if(cright > sright){
35567                 c.scrollLeft = cright-c.clientWidth;
35568             }
35569         }
35570          
35571         return el;
35572     },
35573
35574     updateColumns : function(){
35575         this.grid.stopEditing();
35576         var cm = this.grid.colModel, colIds = this.getColumnIds();
35577         //var totalWidth = cm.getTotalWidth();
35578         var pos = 0;
35579         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35580             //if(cm.isHidden(i)) continue;
35581             var w = cm.getColumnWidth(i);
35582             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35583             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35584         }
35585         this.updateSplitters();
35586     },
35587
35588     generateRules : function(cm){
35589         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35590         Roo.util.CSS.removeStyleSheet(rulesId);
35591         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35592             var cid = cm.getColumnId(i);
35593             var align = '';
35594             if(cm.config[i].align){
35595                 align = 'text-align:'+cm.config[i].align+';';
35596             }
35597             var hidden = '';
35598             if(cm.isHidden(i)){
35599                 hidden = 'display:none;';
35600             }
35601             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35602             ruleBuf.push(
35603                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35604                     this.hdSelector, cid, " {\n", align, width, "}\n",
35605                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35606                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35607         }
35608         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35609     },
35610
35611     updateSplitters : function(){
35612         var cm = this.cm, s = this.getSplitters();
35613         if(s){ // splitters not created yet
35614             var pos = 0, locked = true;
35615             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35616                 if(cm.isHidden(i)) continue;
35617                 var w = cm.getColumnWidth(i); // make sure it's a number
35618                 if(!cm.isLocked(i) && locked){
35619                     pos = 0;
35620                     locked = false;
35621                 }
35622                 pos += w;
35623                 s[i].style.left = (pos-this.splitOffset) + "px";
35624             }
35625         }
35626     },
35627
35628     handleHiddenChange : function(colModel, colIndex, hidden){
35629         if(hidden){
35630             this.hideColumn(colIndex);
35631         }else{
35632             this.unhideColumn(colIndex);
35633         }
35634     },
35635
35636     hideColumn : function(colIndex){
35637         var cid = this.getColumnId(colIndex);
35638         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35639         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35640         if(Roo.isSafari){
35641             this.updateHeaders();
35642         }
35643         this.updateSplitters();
35644         this.layout();
35645     },
35646
35647     unhideColumn : function(colIndex){
35648         var cid = this.getColumnId(colIndex);
35649         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35650         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35651
35652         if(Roo.isSafari){
35653             this.updateHeaders();
35654         }
35655         this.updateSplitters();
35656         this.layout();
35657     },
35658
35659     insertRows : function(dm, firstRow, lastRow, isUpdate){
35660         if(firstRow == 0 && lastRow == dm.getCount()-1){
35661             this.refresh();
35662         }else{
35663             if(!isUpdate){
35664                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35665             }
35666             var s = this.getScrollState();
35667             var markup = this.renderRows(firstRow, lastRow);
35668             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35669             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35670             this.restoreScroll(s);
35671             if(!isUpdate){
35672                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35673                 this.syncRowHeights(firstRow, lastRow);
35674                 this.stripeRows(firstRow);
35675                 this.layout();
35676             }
35677         }
35678     },
35679
35680     bufferRows : function(markup, target, index){
35681         var before = null, trows = target.rows, tbody = target.tBodies[0];
35682         if(index < trows.length){
35683             before = trows[index];
35684         }
35685         var b = document.createElement("div");
35686         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35687         var rows = b.firstChild.rows;
35688         for(var i = 0, len = rows.length; i < len; i++){
35689             if(before){
35690                 tbody.insertBefore(rows[0], before);
35691             }else{
35692                 tbody.appendChild(rows[0]);
35693             }
35694         }
35695         b.innerHTML = "";
35696         b = null;
35697     },
35698
35699     deleteRows : function(dm, firstRow, lastRow){
35700         if(dm.getRowCount()<1){
35701             this.fireEvent("beforerefresh", this);
35702             this.mainBody.update("");
35703             this.lockedBody.update("");
35704             this.fireEvent("refresh", this);
35705         }else{
35706             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35707             var bt = this.getBodyTable();
35708             var tbody = bt.firstChild;
35709             var rows = bt.rows;
35710             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35711                 tbody.removeChild(rows[firstRow]);
35712             }
35713             this.stripeRows(firstRow);
35714             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35715         }
35716     },
35717
35718     updateRows : function(dataSource, firstRow, lastRow){
35719         var s = this.getScrollState();
35720         this.refresh();
35721         this.restoreScroll(s);
35722     },
35723
35724     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35725         if(!noRefresh){
35726            this.refresh();
35727         }
35728         this.updateHeaderSortState();
35729     },
35730
35731     getScrollState : function(){
35732         
35733         var sb = this.scroller.dom;
35734         return {left: sb.scrollLeft, top: sb.scrollTop};
35735     },
35736
35737     stripeRows : function(startRow){
35738         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35739             return;
35740         }
35741         startRow = startRow || 0;
35742         var rows = this.getBodyTable().rows;
35743         var lrows = this.getLockedTable().rows;
35744         var cls = ' x-grid-row-alt ';
35745         for(var i = startRow, len = rows.length; i < len; i++){
35746             var row = rows[i], lrow = lrows[i];
35747             var isAlt = ((i+1) % 2 == 0);
35748             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35749             if(isAlt == hasAlt){
35750                 continue;
35751             }
35752             if(isAlt){
35753                 row.className += " x-grid-row-alt";
35754             }else{
35755                 row.className = row.className.replace("x-grid-row-alt", "");
35756             }
35757             if(lrow){
35758                 lrow.className = row.className;
35759             }
35760         }
35761     },
35762
35763     restoreScroll : function(state){
35764         //Roo.log('GridView.restoreScroll');
35765         var sb = this.scroller.dom;
35766         sb.scrollLeft = state.left;
35767         sb.scrollTop = state.top;
35768         this.syncScroll();
35769     },
35770
35771     syncScroll : function(){
35772         //Roo.log('GridView.syncScroll');
35773         var sb = this.scroller.dom;
35774         var sh = this.mainHd.dom;
35775         var bs = this.mainBody.dom;
35776         var lv = this.lockedBody.dom;
35777         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35778         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35779     },
35780
35781     handleScroll : function(e){
35782         this.syncScroll();
35783         var sb = this.scroller.dom;
35784         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35785         e.stopEvent();
35786     },
35787
35788     handleWheel : function(e){
35789         var d = e.getWheelDelta();
35790         this.scroller.dom.scrollTop -= d*22;
35791         // set this here to prevent jumpy scrolling on large tables
35792         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35793         e.stopEvent();
35794     },
35795
35796     renderRows : function(startRow, endRow){
35797         // pull in all the crap needed to render rows
35798         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35799         var colCount = cm.getColumnCount();
35800
35801         if(ds.getCount() < 1){
35802             return ["", ""];
35803         }
35804
35805         // build a map for all the columns
35806         var cs = [];
35807         for(var i = 0; i < colCount; i++){
35808             var name = cm.getDataIndex(i);
35809             cs[i] = {
35810                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35811                 renderer : cm.getRenderer(i),
35812                 id : cm.getColumnId(i),
35813                 locked : cm.isLocked(i)
35814             };
35815         }
35816
35817         startRow = startRow || 0;
35818         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35819
35820         // records to render
35821         var rs = ds.getRange(startRow, endRow);
35822
35823         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35824     },
35825
35826     // As much as I hate to duplicate code, this was branched because FireFox really hates
35827     // [].join("") on strings. The performance difference was substantial enough to
35828     // branch this function
35829     doRender : Roo.isGecko ?
35830             function(cs, rs, ds, startRow, colCount, stripe){
35831                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35832                 // buffers
35833                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35834                 
35835                 var hasListener = this.grid.hasListener('rowclass');
35836                 var rowcfg = {};
35837                 for(var j = 0, len = rs.length; j < len; j++){
35838                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35839                     for(var i = 0; i < colCount; i++){
35840                         c = cs[i];
35841                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35842                         p.id = c.id;
35843                         p.css = p.attr = "";
35844                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35845                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35846                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35847                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35848                         }
35849                         var markup = ct.apply(p);
35850                         if(!c.locked){
35851                             cb+= markup;
35852                         }else{
35853                             lcb+= markup;
35854                         }
35855                     }
35856                     var alt = [];
35857                     if(stripe && ((rowIndex+1) % 2 == 0)){
35858                         alt.push("x-grid-row-alt")
35859                     }
35860                     if(r.dirty){
35861                         alt.push(  " x-grid-dirty-row");
35862                     }
35863                     rp.cells = lcb;
35864                     if(this.getRowClass){
35865                         alt.push(this.getRowClass(r, rowIndex));
35866                     }
35867                     if (hasListener) {
35868                         rowcfg = {
35869                              
35870                             record: r,
35871                             rowIndex : rowIndex,
35872                             rowClass : ''
35873                         }
35874                         this.grid.fireEvent('rowclass', this, rowcfg);
35875                         alt.push(rowcfg.rowClass);
35876                     }
35877                     rp.alt = alt.join(" ");
35878                     lbuf+= rt.apply(rp);
35879                     rp.cells = cb;
35880                     buf+=  rt.apply(rp);
35881                 }
35882                 return [lbuf, buf];
35883             } :
35884             function(cs, rs, ds, startRow, colCount, stripe){
35885                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35886                 // buffers
35887                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35888                 var hasListener = this.grid.hasListener('rowclass');
35889  
35890                 var rowcfg = {};
35891                 for(var j = 0, len = rs.length; j < len; j++){
35892                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35893                     for(var i = 0; i < colCount; i++){
35894                         c = cs[i];
35895                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35896                         p.id = c.id;
35897                         p.css = p.attr = "";
35898                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35899                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35900                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35901                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35902                         }
35903                         
35904                         var markup = ct.apply(p);
35905                         if(!c.locked){
35906                             cb[cb.length] = markup;
35907                         }else{
35908                             lcb[lcb.length] = markup;
35909                         }
35910                     }
35911                     var alt = [];
35912                     if(stripe && ((rowIndex+1) % 2 == 0)){
35913                         alt.push( "x-grid-row-alt");
35914                     }
35915                     if(r.dirty){
35916                         alt.push(" x-grid-dirty-row");
35917                     }
35918                     rp.cells = lcb;
35919                     if(this.getRowClass){
35920                         alt.push( this.getRowClass(r, rowIndex));
35921                     }
35922                     if (hasListener) {
35923                         rowcfg = {
35924                              
35925                             record: r,
35926                             rowIndex : rowIndex,
35927                             rowClass : ''
35928                         }
35929                         this.grid.fireEvent('rowclass', this, rowcfg);
35930                         alt.push(rowcfg.rowClass);
35931                     }
35932                     rp.alt = alt.join(" ");
35933                     rp.cells = lcb.join("");
35934                     lbuf[lbuf.length] = rt.apply(rp);
35935                     rp.cells = cb.join("");
35936                     buf[buf.length] =  rt.apply(rp);
35937                 }
35938                 return [lbuf.join(""), buf.join("")];
35939             },
35940
35941     renderBody : function(){
35942         var markup = this.renderRows();
35943         var bt = this.templates.body;
35944         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35945     },
35946
35947     /**
35948      * Refreshes the grid
35949      * @param {Boolean} headersToo
35950      */
35951     refresh : function(headersToo){
35952         this.fireEvent("beforerefresh", this);
35953         this.grid.stopEditing();
35954         var result = this.renderBody();
35955         this.lockedBody.update(result[0]);
35956         this.mainBody.update(result[1]);
35957         if(headersToo === true){
35958             this.updateHeaders();
35959             this.updateColumns();
35960             this.updateSplitters();
35961             this.updateHeaderSortState();
35962         }
35963         this.syncRowHeights();
35964         this.layout();
35965         this.fireEvent("refresh", this);
35966     },
35967
35968     handleColumnMove : function(cm, oldIndex, newIndex){
35969         this.indexMap = null;
35970         var s = this.getScrollState();
35971         this.refresh(true);
35972         this.restoreScroll(s);
35973         this.afterMove(newIndex);
35974     },
35975
35976     afterMove : function(colIndex){
35977         if(this.enableMoveAnim && Roo.enableFx){
35978             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35979         }
35980         // if multisort - fix sortOrder, and reload..
35981         if (this.grid.dataSource.multiSort) {
35982             // the we can call sort again..
35983             var dm = this.grid.dataSource;
35984             var cm = this.grid.colModel;
35985             var so = [];
35986             for(var i = 0; i < cm.config.length; i++ ) {
35987                 
35988                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35989                     continue; // dont' bother, it's not in sort list or being set.
35990                 }
35991                 
35992                 so.push(cm.config[i].dataIndex);
35993             };
35994             dm.sortOrder = so;
35995             dm.load(dm.lastOptions);
35996             
35997             
35998         }
35999         
36000     },
36001
36002     updateCell : function(dm, rowIndex, dataIndex){
36003         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
36004         if(typeof colIndex == "undefined"){ // not present in grid
36005             return;
36006         }
36007         var cm = this.grid.colModel;
36008         var cell = this.getCell(rowIndex, colIndex);
36009         var cellText = this.getCellText(rowIndex, colIndex);
36010
36011         var p = {
36012             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
36013             id : cm.getColumnId(colIndex),
36014             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
36015         };
36016         var renderer = cm.getRenderer(colIndex);
36017         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
36018         if(typeof val == "undefined" || val === "") val = "&#160;";
36019         cellText.innerHTML = val;
36020         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
36021         this.syncRowHeights(rowIndex, rowIndex);
36022     },
36023
36024     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36025         var maxWidth = 0;
36026         if(this.grid.autoSizeHeaders){
36027             var h = this.getHeaderCellMeasure(colIndex);
36028             maxWidth = Math.max(maxWidth, h.scrollWidth);
36029         }
36030         var tb, index;
36031         if(this.cm.isLocked(colIndex)){
36032             tb = this.getLockedTable();
36033             index = colIndex;
36034         }else{
36035             tb = this.getBodyTable();
36036             index = colIndex - this.cm.getLockedCount();
36037         }
36038         if(tb && tb.rows){
36039             var rows = tb.rows;
36040             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36041             for(var i = 0; i < stopIndex; i++){
36042                 var cell = rows[i].childNodes[index].firstChild;
36043                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36044             }
36045         }
36046         return maxWidth + /*margin for error in IE*/ 5;
36047     },
36048     /**
36049      * Autofit a column to its content.
36050      * @param {Number} colIndex
36051      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36052      */
36053      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36054          if(this.cm.isHidden(colIndex)){
36055              return; // can't calc a hidden column
36056          }
36057         if(forceMinSize){
36058             var cid = this.cm.getColumnId(colIndex);
36059             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36060            if(this.grid.autoSizeHeaders){
36061                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36062            }
36063         }
36064         var newWidth = this.calcColumnWidth(colIndex);
36065         this.cm.setColumnWidth(colIndex,
36066             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36067         if(!suppressEvent){
36068             this.grid.fireEvent("columnresize", colIndex, newWidth);
36069         }
36070     },
36071
36072     /**
36073      * Autofits all columns to their content and then expands to fit any extra space in the grid
36074      */
36075      autoSizeColumns : function(){
36076         var cm = this.grid.colModel;
36077         var colCount = cm.getColumnCount();
36078         for(var i = 0; i < colCount; i++){
36079             this.autoSizeColumn(i, true, true);
36080         }
36081         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36082             this.fitColumns();
36083         }else{
36084             this.updateColumns();
36085             this.layout();
36086         }
36087     },
36088
36089     /**
36090      * Autofits all columns to the grid's width proportionate with their current size
36091      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36092      */
36093     fitColumns : function(reserveScrollSpace){
36094         var cm = this.grid.colModel;
36095         var colCount = cm.getColumnCount();
36096         var cols = [];
36097         var width = 0;
36098         var i, w;
36099         for (i = 0; i < colCount; i++){
36100             if(!cm.isHidden(i) && !cm.isFixed(i)){
36101                 w = cm.getColumnWidth(i);
36102                 cols.push(i);
36103                 cols.push(w);
36104                 width += w;
36105             }
36106         }
36107         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36108         if(reserveScrollSpace){
36109             avail -= 17;
36110         }
36111         var frac = (avail - cm.getTotalWidth())/width;
36112         while (cols.length){
36113             w = cols.pop();
36114             i = cols.pop();
36115             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36116         }
36117         this.updateColumns();
36118         this.layout();
36119     },
36120
36121     onRowSelect : function(rowIndex){
36122         var row = this.getRowComposite(rowIndex);
36123         row.addClass("x-grid-row-selected");
36124     },
36125
36126     onRowDeselect : function(rowIndex){
36127         var row = this.getRowComposite(rowIndex);
36128         row.removeClass("x-grid-row-selected");
36129     },
36130
36131     onCellSelect : function(row, col){
36132         var cell = this.getCell(row, col);
36133         if(cell){
36134             Roo.fly(cell).addClass("x-grid-cell-selected");
36135         }
36136     },
36137
36138     onCellDeselect : function(row, col){
36139         var cell = this.getCell(row, col);
36140         if(cell){
36141             Roo.fly(cell).removeClass("x-grid-cell-selected");
36142         }
36143     },
36144
36145     updateHeaderSortState : function(){
36146         
36147         // sort state can be single { field: xxx, direction : yyy}
36148         // or   { xxx=>ASC , yyy : DESC ..... }
36149         
36150         var mstate = {};
36151         if (!this.ds.multiSort) { 
36152             var state = this.ds.getSortState();
36153             if(!state){
36154                 return;
36155             }
36156             mstate[state.field] = state.direction;
36157             // FIXME... - this is not used here.. but might be elsewhere..
36158             this.sortState = state;
36159             
36160         } else {
36161             mstate = this.ds.sortToggle;
36162         }
36163         //remove existing sort classes..
36164         
36165         var sc = this.sortClasses;
36166         var hds = this.el.select(this.headerSelector).removeClass(sc);
36167         
36168         for(var f in mstate) {
36169         
36170             var sortColumn = this.cm.findColumnIndex(f);
36171             
36172             if(sortColumn != -1){
36173                 var sortDir = mstate[f];        
36174                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36175             }
36176         }
36177         
36178          
36179         
36180     },
36181
36182
36183     handleHeaderClick : function(g, index){
36184         if(this.headersDisabled){
36185             return;
36186         }
36187         var dm = g.dataSource, cm = g.colModel;
36188         if(!cm.isSortable(index)){
36189             return;
36190         }
36191         g.stopEditing();
36192         
36193         if (dm.multiSort) {
36194             // update the sortOrder
36195             var so = [];
36196             for(var i = 0; i < cm.config.length; i++ ) {
36197                 
36198                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36199                     continue; // dont' bother, it's not in sort list or being set.
36200                 }
36201                 
36202                 so.push(cm.config[i].dataIndex);
36203             };
36204             dm.sortOrder = so;
36205         }
36206         
36207         
36208         dm.sort(cm.getDataIndex(index));
36209     },
36210
36211
36212     destroy : function(){
36213         if(this.colMenu){
36214             this.colMenu.removeAll();
36215             Roo.menu.MenuMgr.unregister(this.colMenu);
36216             this.colMenu.getEl().remove();
36217             delete this.colMenu;
36218         }
36219         if(this.hmenu){
36220             this.hmenu.removeAll();
36221             Roo.menu.MenuMgr.unregister(this.hmenu);
36222             this.hmenu.getEl().remove();
36223             delete this.hmenu;
36224         }
36225         if(this.grid.enableColumnMove){
36226             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36227             if(dds){
36228                 for(var dd in dds){
36229                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36230                         var elid = dds[dd].dragElId;
36231                         dds[dd].unreg();
36232                         Roo.get(elid).remove();
36233                     } else if(dds[dd].config.isTarget){
36234                         dds[dd].proxyTop.remove();
36235                         dds[dd].proxyBottom.remove();
36236                         dds[dd].unreg();
36237                     }
36238                     if(Roo.dd.DDM.locationCache[dd]){
36239                         delete Roo.dd.DDM.locationCache[dd];
36240                     }
36241                 }
36242                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36243             }
36244         }
36245         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36246         this.bind(null, null);
36247         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36248     },
36249
36250     handleLockChange : function(){
36251         this.refresh(true);
36252     },
36253
36254     onDenyColumnLock : function(){
36255
36256     },
36257
36258     onDenyColumnHide : function(){
36259
36260     },
36261
36262     handleHdMenuClick : function(item){
36263         var index = this.hdCtxIndex;
36264         var cm = this.cm, ds = this.ds;
36265         switch(item.id){
36266             case "asc":
36267                 ds.sort(cm.getDataIndex(index), "ASC");
36268                 break;
36269             case "desc":
36270                 ds.sort(cm.getDataIndex(index), "DESC");
36271                 break;
36272             case "lock":
36273                 var lc = cm.getLockedCount();
36274                 if(cm.getColumnCount(true) <= lc+1){
36275                     this.onDenyColumnLock();
36276                     return;
36277                 }
36278                 if(lc != index){
36279                     cm.setLocked(index, true, true);
36280                     cm.moveColumn(index, lc);
36281                     this.grid.fireEvent("columnmove", index, lc);
36282                 }else{
36283                     cm.setLocked(index, true);
36284                 }
36285             break;
36286             case "unlock":
36287                 var lc = cm.getLockedCount();
36288                 if((lc-1) != index){
36289                     cm.setLocked(index, false, true);
36290                     cm.moveColumn(index, lc-1);
36291                     this.grid.fireEvent("columnmove", index, lc-1);
36292                 }else{
36293                     cm.setLocked(index, false);
36294                 }
36295             break;
36296             default:
36297                 index = cm.getIndexById(item.id.substr(4));
36298                 if(index != -1){
36299                     if(item.checked && cm.getColumnCount(true) <= 1){
36300                         this.onDenyColumnHide();
36301                         return false;
36302                     }
36303                     cm.setHidden(index, item.checked);
36304                 }
36305         }
36306         return true;
36307     },
36308
36309     beforeColMenuShow : function(){
36310         var cm = this.cm,  colCount = cm.getColumnCount();
36311         this.colMenu.removeAll();
36312         for(var i = 0; i < colCount; i++){
36313             this.colMenu.add(new Roo.menu.CheckItem({
36314                 id: "col-"+cm.getColumnId(i),
36315                 text: cm.getColumnHeader(i),
36316                 checked: !cm.isHidden(i),
36317                 hideOnClick:false
36318             }));
36319         }
36320     },
36321
36322     handleHdCtx : function(g, index, e){
36323         e.stopEvent();
36324         var hd = this.getHeaderCell(index);
36325         this.hdCtxIndex = index;
36326         var ms = this.hmenu.items, cm = this.cm;
36327         ms.get("asc").setDisabled(!cm.isSortable(index));
36328         ms.get("desc").setDisabled(!cm.isSortable(index));
36329         if(this.grid.enableColLock !== false){
36330             ms.get("lock").setDisabled(cm.isLocked(index));
36331             ms.get("unlock").setDisabled(!cm.isLocked(index));
36332         }
36333         this.hmenu.show(hd, "tl-bl");
36334     },
36335
36336     handleHdOver : function(e){
36337         var hd = this.findHeaderCell(e.getTarget());
36338         if(hd && !this.headersDisabled){
36339             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36340                this.fly(hd).addClass("x-grid-hd-over");
36341             }
36342         }
36343     },
36344
36345     handleHdOut : function(e){
36346         var hd = this.findHeaderCell(e.getTarget());
36347         if(hd){
36348             this.fly(hd).removeClass("x-grid-hd-over");
36349         }
36350     },
36351
36352     handleSplitDblClick : function(e, t){
36353         var i = this.getCellIndex(t);
36354         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36355             this.autoSizeColumn(i, true);
36356             this.layout();
36357         }
36358     },
36359
36360     render : function(){
36361
36362         var cm = this.cm;
36363         var colCount = cm.getColumnCount();
36364
36365         if(this.grid.monitorWindowResize === true){
36366             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36367         }
36368         var header = this.renderHeaders();
36369         var body = this.templates.body.apply({rows:""});
36370         var html = this.templates.master.apply({
36371             lockedBody: body,
36372             body: body,
36373             lockedHeader: header[0],
36374             header: header[1]
36375         });
36376
36377         //this.updateColumns();
36378
36379         this.grid.getGridEl().dom.innerHTML = html;
36380
36381         this.initElements();
36382         
36383         // a kludge to fix the random scolling effect in webkit
36384         this.el.on("scroll", function() {
36385             this.el.dom.scrollTop=0; // hopefully not recursive..
36386         },this);
36387
36388         this.scroller.on("scroll", this.handleScroll, this);
36389         this.lockedBody.on("mousewheel", this.handleWheel, this);
36390         this.mainBody.on("mousewheel", this.handleWheel, this);
36391
36392         this.mainHd.on("mouseover", this.handleHdOver, this);
36393         this.mainHd.on("mouseout", this.handleHdOut, this);
36394         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36395                 {delegate: "."+this.splitClass});
36396
36397         this.lockedHd.on("mouseover", this.handleHdOver, this);
36398         this.lockedHd.on("mouseout", this.handleHdOut, this);
36399         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36400                 {delegate: "."+this.splitClass});
36401
36402         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36403             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36404         }
36405
36406         this.updateSplitters();
36407
36408         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36409             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36410             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36411         }
36412
36413         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36414             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36415             this.hmenu.add(
36416                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36417                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36418             );
36419             if(this.grid.enableColLock !== false){
36420                 this.hmenu.add('-',
36421                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36422                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36423                 );
36424             }
36425             if(this.grid.enableColumnHide !== false){
36426
36427                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36428                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36429                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36430
36431                 this.hmenu.add('-',
36432                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36433                 );
36434             }
36435             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36436
36437             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36438         }
36439
36440         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36441             this.dd = new Roo.grid.GridDragZone(this.grid, {
36442                 ddGroup : this.grid.ddGroup || 'GridDD'
36443             });
36444         }
36445
36446         /*
36447         for(var i = 0; i < colCount; i++){
36448             if(cm.isHidden(i)){
36449                 this.hideColumn(i);
36450             }
36451             if(cm.config[i].align){
36452                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36453                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36454             }
36455         }*/
36456         
36457         this.updateHeaderSortState();
36458
36459         this.beforeInitialResize();
36460         this.layout(true);
36461
36462         // two part rendering gives faster view to the user
36463         this.renderPhase2.defer(1, this);
36464     },
36465
36466     renderPhase2 : function(){
36467         // render the rows now
36468         this.refresh();
36469         if(this.grid.autoSizeColumns){
36470             this.autoSizeColumns();
36471         }
36472     },
36473
36474     beforeInitialResize : function(){
36475
36476     },
36477
36478     onColumnSplitterMoved : function(i, w){
36479         this.userResized = true;
36480         var cm = this.grid.colModel;
36481         cm.setColumnWidth(i, w, true);
36482         var cid = cm.getColumnId(i);
36483         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36484         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36485         this.updateSplitters();
36486         this.layout();
36487         this.grid.fireEvent("columnresize", i, w);
36488     },
36489
36490     syncRowHeights : function(startIndex, endIndex){
36491         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36492             startIndex = startIndex || 0;
36493             var mrows = this.getBodyTable().rows;
36494             var lrows = this.getLockedTable().rows;
36495             var len = mrows.length-1;
36496             endIndex = Math.min(endIndex || len, len);
36497             for(var i = startIndex; i <= endIndex; i++){
36498                 var m = mrows[i], l = lrows[i];
36499                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36500                 m.style.height = l.style.height = h + "px";
36501             }
36502         }
36503     },
36504
36505     layout : function(initialRender, is2ndPass){
36506         var g = this.grid;
36507         var auto = g.autoHeight;
36508         var scrollOffset = 16;
36509         var c = g.getGridEl(), cm = this.cm,
36510                 expandCol = g.autoExpandColumn,
36511                 gv = this;
36512         //c.beginMeasure();
36513
36514         if(!c.dom.offsetWidth){ // display:none?
36515             if(initialRender){
36516                 this.lockedWrap.show();
36517                 this.mainWrap.show();
36518             }
36519             return;
36520         }
36521
36522         var hasLock = this.cm.isLocked(0);
36523
36524         var tbh = this.headerPanel.getHeight();
36525         var bbh = this.footerPanel.getHeight();
36526
36527         if(auto){
36528             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36529             var newHeight = ch + c.getBorderWidth("tb");
36530             if(g.maxHeight){
36531                 newHeight = Math.min(g.maxHeight, newHeight);
36532             }
36533             c.setHeight(newHeight);
36534         }
36535
36536         if(g.autoWidth){
36537             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36538         }
36539
36540         var s = this.scroller;
36541
36542         var csize = c.getSize(true);
36543
36544         this.el.setSize(csize.width, csize.height);
36545
36546         this.headerPanel.setWidth(csize.width);
36547         this.footerPanel.setWidth(csize.width);
36548
36549         var hdHeight = this.mainHd.getHeight();
36550         var vw = csize.width;
36551         var vh = csize.height - (tbh + bbh);
36552
36553         s.setSize(vw, vh);
36554
36555         var bt = this.getBodyTable();
36556         var ltWidth = hasLock ?
36557                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36558
36559         var scrollHeight = bt.offsetHeight;
36560         var scrollWidth = ltWidth + bt.offsetWidth;
36561         var vscroll = false, hscroll = false;
36562
36563         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36564
36565         var lw = this.lockedWrap, mw = this.mainWrap;
36566         var lb = this.lockedBody, mb = this.mainBody;
36567
36568         setTimeout(function(){
36569             var t = s.dom.offsetTop;
36570             var w = s.dom.clientWidth,
36571                 h = s.dom.clientHeight;
36572
36573             lw.setTop(t);
36574             lw.setSize(ltWidth, h);
36575
36576             mw.setLeftTop(ltWidth, t);
36577             mw.setSize(w-ltWidth, h);
36578
36579             lb.setHeight(h-hdHeight);
36580             mb.setHeight(h-hdHeight);
36581
36582             if(is2ndPass !== true && !gv.userResized && expandCol){
36583                 // high speed resize without full column calculation
36584                 
36585                 var ci = cm.getIndexById(expandCol);
36586                 if (ci < 0) {
36587                     ci = cm.findColumnIndex(expandCol);
36588                 }
36589                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36590                 var expandId = cm.getColumnId(ci);
36591                 var  tw = cm.getTotalWidth(false);
36592                 var currentWidth = cm.getColumnWidth(ci);
36593                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36594                 if(currentWidth != cw){
36595                     cm.setColumnWidth(ci, cw, true);
36596                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36597                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36598                     gv.updateSplitters();
36599                     gv.layout(false, true);
36600                 }
36601             }
36602
36603             if(initialRender){
36604                 lw.show();
36605                 mw.show();
36606             }
36607             //c.endMeasure();
36608         }, 10);
36609     },
36610
36611     onWindowResize : function(){
36612         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36613             return;
36614         }
36615         this.layout();
36616     },
36617
36618     appendFooter : function(parentEl){
36619         return null;
36620     },
36621
36622     sortAscText : "Sort Ascending",
36623     sortDescText : "Sort Descending",
36624     lockText : "Lock Column",
36625     unlockText : "Unlock Column",
36626     columnsText : "Columns"
36627 });
36628
36629
36630 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36631     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36632     this.proxy.el.addClass('x-grid3-col-dd');
36633 };
36634
36635 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36636     handleMouseDown : function(e){
36637
36638     },
36639
36640     callHandleMouseDown : function(e){
36641         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36642     }
36643 });