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, 'Y-m-d');
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     },
22807
22808     // private
22809     parseDate : function(value){
22810         if(!value || value instanceof Date){
22811             return value;
22812         }
22813         var v = Date.parseDate(value, this.format);
22814         if(!v && this.altFormats){
22815             if(!this.altFormatsArray){
22816                 this.altFormatsArray = this.altFormats.split("|");
22817             }
22818             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22819                 v = Date.parseDate(value, this.altFormatsArray[i]);
22820             }
22821         }
22822         return v;
22823     },
22824
22825     // private
22826     formatDate : function(date, fmt){
22827         return (!date || !(date instanceof Date)) ?
22828                date : date.dateFormat(fmt || this.format);
22829     },
22830
22831     // private
22832     menuListeners : {
22833         select: function(m, d){
22834             this.setValue(d);
22835             this.fireEvent('select', this, d);
22836         },
22837         show : function(){ // retain focus styling
22838             this.onFocus();
22839         },
22840         hide : function(){
22841             this.focus.defer(10, this);
22842             var ml = this.menuListeners;
22843             this.menu.un("select", ml.select,  this);
22844             this.menu.un("show", ml.show,  this);
22845             this.menu.un("hide", ml.hide,  this);
22846         }
22847     },
22848
22849     // private
22850     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
22851     onTriggerClick : function(){
22852         if(this.disabled){
22853             return;
22854         }
22855         if(this.menu == null){
22856             this.menu = new Roo.menu.DateMenu();
22857         }
22858         Roo.apply(this.menu.picker,  {
22859             showClear: this.allowBlank,
22860             minDate : this.minValue,
22861             maxDate : this.maxValue,
22862             disabledDatesRE : this.ddMatch,
22863             disabledDatesText : this.disabledDatesText,
22864             disabledDays : this.disabledDays,
22865             disabledDaysText : this.disabledDaysText,
22866             format : this.format,
22867             minText : String.format(this.minText, this.formatDate(this.minValue)),
22868             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
22869         });
22870         this.menu.on(Roo.apply({}, this.menuListeners, {
22871             scope:this
22872         }));
22873         this.menu.picker.setValue(this.getValue() || new Date());
22874         this.menu.show(this.el, "tl-bl?");
22875     },
22876
22877     beforeBlur : function(){
22878         var v = this.parseDate(this.getRawValue());
22879         if(v){
22880             this.setValue(v);
22881         }
22882     }
22883
22884     /** @cfg {Boolean} grow @hide */
22885     /** @cfg {Number} growMin @hide */
22886     /** @cfg {Number} growMax @hide */
22887     /**
22888      * @hide
22889      * @method autoSize
22890      */
22891 });/*
22892  * Based on:
22893  * Ext JS Library 1.1.1
22894  * Copyright(c) 2006-2007, Ext JS, LLC.
22895  *
22896  * Originally Released Under LGPL - original licence link has changed is not relivant.
22897  *
22898  * Fork - LGPL
22899  * <script type="text/javascript">
22900  */
22901  
22902 /**
22903  * @class Roo.form.MonthField
22904  * @extends Roo.form.TriggerField
22905  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
22906 * @constructor
22907 * Create a new MonthField
22908 * @param {Object} config
22909  */
22910 Roo.form.MonthField = function(config){
22911     
22912     Roo.form.MonthField.superclass.constructor.call(this, config);
22913     
22914       this.addEvents({
22915          
22916         /**
22917          * @event select
22918          * Fires when a date is selected
22919              * @param {Roo.form.MonthFieeld} combo This combo box
22920              * @param {Date} date The date selected
22921              */
22922         'select' : true
22923          
22924     });
22925     
22926     
22927     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
22928     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
22929     this.ddMatch = null;
22930     if(this.disabledDates){
22931         var dd = this.disabledDates;
22932         var re = "(?:";
22933         for(var i = 0; i < dd.length; i++){
22934             re += dd[i];
22935             if(i != dd.length-1) re += "|";
22936         }
22937         this.ddMatch = new RegExp(re + ")");
22938     }
22939 };
22940
22941 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
22942     /**
22943      * @cfg {String} format
22944      * The default date format string which can be overriden for localization support.  The format must be
22945      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22946      */
22947     format : "M Y",
22948     /**
22949      * @cfg {String} altFormats
22950      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22951      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22952      */
22953     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
22954     /**
22955      * @cfg {Array} disabledDays
22956      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
22957      */
22958     disabledDays : [0,1,2,3,4,5,6],
22959     /**
22960      * @cfg {String} disabledDaysText
22961      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
22962      */
22963     disabledDaysText : "Disabled",
22964     /**
22965      * @cfg {Array} disabledDates
22966      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
22967      * expression so they are very powerful. Some examples:
22968      * <ul>
22969      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
22970      * <li>["03/08", "09/16"] would disable those days for every year</li>
22971      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
22972      * <li>["03/../2006"] would disable every day in March 2006</li>
22973      * <li>["^03"] would disable every day in every March</li>
22974      * </ul>
22975      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
22976      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
22977      */
22978     disabledDates : null,
22979     /**
22980      * @cfg {String} disabledDatesText
22981      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
22982      */
22983     disabledDatesText : "Disabled",
22984     /**
22985      * @cfg {Date/String} minValue
22986      * The minimum allowed date. Can be either a Javascript date object or a string date in a
22987      * valid format (defaults to null).
22988      */
22989     minValue : null,
22990     /**
22991      * @cfg {Date/String} maxValue
22992      * The maximum allowed date. Can be either a Javascript date object or a string date in a
22993      * valid format (defaults to null).
22994      */
22995     maxValue : null,
22996     /**
22997      * @cfg {String} minText
22998      * The error text to display when the date in the cell is before minValue (defaults to
22999      * 'The date in this field must be after {minValue}').
23000      */
23001     minText : "The date in this field must be equal to or after {0}",
23002     /**
23003      * @cfg {String} maxTextf
23004      * The error text to display when the date in the cell is after maxValue (defaults to
23005      * 'The date in this field must be before {maxValue}').
23006      */
23007     maxText : "The date in this field must be equal to or before {0}",
23008     /**
23009      * @cfg {String} invalidText
23010      * The error text to display when the date in the field is invalid (defaults to
23011      * '{value} is not a valid date - it must be in the format {format}').
23012      */
23013     invalidText : "{0} is not a valid date - it must be in the format {1}",
23014     /**
23015      * @cfg {String} triggerClass
23016      * An additional CSS class used to style the trigger button.  The trigger will always get the
23017      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
23018      * which displays a calendar icon).
23019      */
23020     triggerClass : 'x-form-date-trigger',
23021     
23022
23023     /**
23024      * @cfg {Boolean} useIso
23025      * if enabled, then the date field will use a hidden field to store the 
23026      * real value as iso formated date. default (true)
23027      */ 
23028     useIso : true,
23029     /**
23030      * @cfg {String/Object} autoCreate
23031      * A DomHelper element spec, or true for a default element spec (defaults to
23032      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
23033      */ 
23034     // private
23035     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
23036     
23037     // private
23038     hiddenField: false,
23039     
23040     hideMonthPicker : false,
23041     
23042     onRender : function(ct, position)
23043     {
23044         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
23045         if (this.useIso) {
23046             this.el.dom.removeAttribute('name'); 
23047             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
23048                     'before', true);
23049             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
23050             // prevent input submission
23051             this.hiddenName = this.name;
23052         }
23053             
23054             
23055     },
23056     
23057     // private
23058     validateValue : function(value)
23059     {
23060         value = this.formatDate(value);
23061         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
23062             return false;
23063         }
23064         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
23065              return true;
23066         }
23067         var svalue = value;
23068         value = this.parseDate(value);
23069         if(!value){
23070             this.markInvalid(String.format(this.invalidText, svalue, this.format));
23071             return false;
23072         }
23073         var time = value.getTime();
23074         if(this.minValue && time < this.minValue.getTime()){
23075             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
23076             return false;
23077         }
23078         if(this.maxValue && time > this.maxValue.getTime()){
23079             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
23080             return false;
23081         }
23082         /*if(this.disabledDays){
23083             var day = value.getDay();
23084             for(var i = 0; i < this.disabledDays.length; i++) {
23085                 if(day === this.disabledDays[i]){
23086                     this.markInvalid(this.disabledDaysText);
23087                     return false;
23088                 }
23089             }
23090         }
23091         */
23092         var fvalue = this.formatDate(value);
23093         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
23094             this.markInvalid(String.format(this.disabledDatesText, fvalue));
23095             return false;
23096         }
23097         */
23098         return true;
23099     },
23100
23101     // private
23102     // Provides logic to override the default TriggerField.validateBlur which just returns true
23103     validateBlur : function(){
23104         return !this.menu || !this.menu.isVisible();
23105     },
23106
23107     /**
23108      * Returns the current date value of the date field.
23109      * @return {Date} The date value
23110      */
23111     getValue : function(){
23112         
23113         return  this.hiddenField ?
23114                 this.hiddenField.value :
23115                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
23116     },
23117
23118     /**
23119      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
23120      * date, using monthField.format as the date format, according to the same rules as {@link Date#parseDate}
23121      * (the default format used is "m/d/y").
23122      * <br />Usage:
23123      * <pre><code>
23124 //All of these calls set the same date value (May 4, 2006)
23125
23126 //Pass a date object:
23127 var dt = new Date('5/4/06');
23128 monthField.setValue(dt);
23129
23130 //Pass a date string (default format):
23131 monthField.setValue('5/4/06');
23132
23133 //Pass a date string (custom format):
23134 monthField.format = 'Y-m-d';
23135 monthField.setValue('2006-5-4');
23136 </code></pre>
23137      * @param {String/Date} date The date or valid date string
23138      */
23139     setValue : function(date){
23140         if (this.hiddenField) {
23141             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
23142         }
23143         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
23144     },
23145
23146     // private
23147     parseDate : function(value){
23148         if(!value || value instanceof Date){
23149             return value;
23150         }
23151         var v = Date.parseDate(value, this.format);
23152         if(!v && this.altFormats){
23153             if(!this.altFormatsArray){
23154                 this.altFormatsArray = this.altFormats.split("|");
23155             }
23156             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23157                 v = Date.parseDate(value, this.altFormatsArray[i]);
23158             }
23159         }
23160         return v;
23161     },
23162
23163     // private
23164     formatDate : function(date, fmt){
23165         return (!date || !(date instanceof Date)) ?
23166                date : date.dateFormat(fmt || this.format);
23167     },
23168
23169     // private
23170     menuListeners : {
23171         select: function(m, d){
23172             this.setValue(d);
23173             this.fireEvent('select', this, d);
23174         },
23175         show : function(){ // retain focus styling
23176             this.onFocus();
23177         },
23178         hide : function(){
23179             this.focus.defer(10, this);
23180             var ml = this.menuListeners;
23181             this.menu.un("select", ml.select,  this);
23182             this.menu.un("show", ml.show,  this);
23183             this.menu.un("hide", ml.hide,  this);
23184         }
23185     },
23186     // private
23187     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
23188     onTriggerClick : function(){
23189         if(this.disabled){
23190             return;
23191         }
23192         if(this.menu == null){
23193             this.menu = new Roo.menu.DateMenu();
23194         }
23195         
23196         Roo.apply(this.menu.picker,  {
23197             
23198             showClear: this.allowBlank,
23199             minDate : this.minValue,
23200             maxDate : this.maxValue,
23201             disabledDatesRE : this.ddMatch,
23202             disabledDatesText : this.disabledDatesText,
23203             
23204             format : this.format,
23205             minText : String.format(this.minText, this.formatDate(this.minValue)),
23206             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
23207             
23208         });
23209         
23210         this.menu.on(Roo.apply({}, this.menuListeners, {
23211             scope:this
23212         }));
23213         
23214         var m = this.menu;
23215         var p = m.picker;
23216         p.setValue(this.getValue() || new Date());
23217         m.show(this.el, "tl-bl?");
23218         
23219         // hidden the day picker
23220         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
23221         
23222         (function() {
23223             p.showMonthPicker();
23224         }).defer(100);
23225         
23226         p.hideMonthPicker  = function(disableAnim){
23227             if(this.monthPicker){
23228                 if(disableAnim === true){
23229                     this.monthPicker.hide();
23230                 }else{
23231                     this.monthPicker.slideOut('t', {duration:.2});
23232                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth));
23233                     p.fireEvent("select", this, this.value);
23234                     m.hide();
23235                 }
23236             }
23237         }
23238     },
23239
23240     beforeBlur : function(){
23241         var v = this.parseDate(this.getRawValue());
23242         if(v){
23243             this.setValue(v);
23244         }
23245     }
23246
23247     /** @cfg {Boolean} grow @hide */
23248     /** @cfg {Number} growMin @hide */
23249     /** @cfg {Number} growMax @hide */
23250     /**
23251      * @hide
23252      * @method autoSize
23253      */
23254 });/*
23255  * Based on:
23256  * Ext JS Library 1.1.1
23257  * Copyright(c) 2006-2007, Ext JS, LLC.
23258  *
23259  * Originally Released Under LGPL - original licence link has changed is not relivant.
23260  *
23261  * Fork - LGPL
23262  * <script type="text/javascript">
23263  */
23264  
23265
23266 /**
23267  * @class Roo.form.ComboBox
23268  * @extends Roo.form.TriggerField
23269  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
23270  * @constructor
23271  * Create a new ComboBox.
23272  * @param {Object} config Configuration options
23273  */
23274 Roo.form.ComboBox = function(config){
23275     Roo.form.ComboBox.superclass.constructor.call(this, config);
23276     this.addEvents({
23277         /**
23278          * @event expand
23279          * Fires when the dropdown list is expanded
23280              * @param {Roo.form.ComboBox} combo This combo box
23281              */
23282         'expand' : true,
23283         /**
23284          * @event collapse
23285          * Fires when the dropdown list is collapsed
23286              * @param {Roo.form.ComboBox} combo This combo box
23287              */
23288         'collapse' : true,
23289         /**
23290          * @event beforeselect
23291          * Fires before a list item is selected. Return false to cancel the selection.
23292              * @param {Roo.form.ComboBox} combo This combo box
23293              * @param {Roo.data.Record} record The data record returned from the underlying store
23294              * @param {Number} index The index of the selected item in the dropdown list
23295              */
23296         'beforeselect' : true,
23297         /**
23298          * @event select
23299          * Fires when a list item is selected
23300              * @param {Roo.form.ComboBox} combo This combo box
23301              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
23302              * @param {Number} index The index of the selected item in the dropdown list
23303              */
23304         'select' : true,
23305         /**
23306          * @event beforequery
23307          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
23308          * The event object passed has these properties:
23309              * @param {Roo.form.ComboBox} combo This combo box
23310              * @param {String} query The query
23311              * @param {Boolean} forceAll true to force "all" query
23312              * @param {Boolean} cancel true to cancel the query
23313              * @param {Object} e The query event object
23314              */
23315         'beforequery': true,
23316          /**
23317          * @event add
23318          * Fires when the 'add' icon is pressed (add a listener to enable add button)
23319              * @param {Roo.form.ComboBox} combo This combo box
23320              */
23321         'add' : true,
23322         /**
23323          * @event edit
23324          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
23325              * @param {Roo.form.ComboBox} combo This combo box
23326              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
23327              */
23328         'edit' : true
23329         
23330         
23331     });
23332     if(this.transform){
23333         this.allowDomMove = false;
23334         var s = Roo.getDom(this.transform);
23335         if(!this.hiddenName){
23336             this.hiddenName = s.name;
23337         }
23338         if(!this.store){
23339             this.mode = 'local';
23340             var d = [], opts = s.options;
23341             for(var i = 0, len = opts.length;i < len; i++){
23342                 var o = opts[i];
23343                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
23344                 if(o.selected) {
23345                     this.value = value;
23346                 }
23347                 d.push([value, o.text]);
23348             }
23349             this.store = new Roo.data.SimpleStore({
23350                 'id': 0,
23351                 fields: ['value', 'text'],
23352                 data : d
23353             });
23354             this.valueField = 'value';
23355             this.displayField = 'text';
23356         }
23357         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
23358         if(!this.lazyRender){
23359             this.target = true;
23360             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
23361             s.parentNode.removeChild(s); // remove it
23362             this.render(this.el.parentNode);
23363         }else{
23364             s.parentNode.removeChild(s); // remove it
23365         }
23366
23367     }
23368     if (this.store) {
23369         this.store = Roo.factory(this.store, Roo.data);
23370     }
23371     
23372     this.selectedIndex = -1;
23373     if(this.mode == 'local'){
23374         if(config.queryDelay === undefined){
23375             this.queryDelay = 10;
23376         }
23377         if(config.minChars === undefined){
23378             this.minChars = 0;
23379         }
23380     }
23381 };
23382
23383 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
23384     /**
23385      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
23386      */
23387     /**
23388      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
23389      * rendering into an Roo.Editor, defaults to false)
23390      */
23391     /**
23392      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
23393      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
23394      */
23395     /**
23396      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
23397      */
23398     /**
23399      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
23400      * the dropdown list (defaults to undefined, with no header element)
23401      */
23402
23403      /**
23404      * @cfg {String/Roo.Template} tpl The template to use to render the output
23405      */
23406      
23407     // private
23408     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
23409     /**
23410      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
23411      */
23412     listWidth: undefined,
23413     /**
23414      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
23415      * mode = 'remote' or 'text' if mode = 'local')
23416      */
23417     displayField: undefined,
23418     /**
23419      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
23420      * mode = 'remote' or 'value' if mode = 'local'). 
23421      * Note: use of a valueField requires the user make a selection
23422      * in order for a value to be mapped.
23423      */
23424     valueField: undefined,
23425     
23426     
23427     /**
23428      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
23429      * field's data value (defaults to the underlying DOM element's name)
23430      */
23431     hiddenName: undefined,
23432     /**
23433      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
23434      */
23435     listClass: '',
23436     /**
23437      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
23438      */
23439     selectedClass: 'x-combo-selected',
23440     /**
23441      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
23442      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
23443      * which displays a downward arrow icon).
23444      */
23445     triggerClass : 'x-form-arrow-trigger',
23446     /**
23447      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
23448      */
23449     shadow:'sides',
23450     /**
23451      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
23452      * anchor positions (defaults to 'tl-bl')
23453      */
23454     listAlign: 'tl-bl?',
23455     /**
23456      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
23457      */
23458     maxHeight: 300,
23459     /**
23460      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
23461      * query specified by the allQuery config option (defaults to 'query')
23462      */
23463     triggerAction: 'query',
23464     /**
23465      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
23466      * (defaults to 4, does not apply if editable = false)
23467      */
23468     minChars : 4,
23469     /**
23470      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
23471      * delay (typeAheadDelay) if it matches a known value (defaults to false)
23472      */
23473     typeAhead: false,
23474     /**
23475      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
23476      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
23477      */
23478     queryDelay: 500,
23479     /**
23480      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
23481      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
23482      */
23483     pageSize: 0,
23484     /**
23485      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
23486      * when editable = true (defaults to false)
23487      */
23488     selectOnFocus:false,
23489     /**
23490      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
23491      */
23492     queryParam: 'query',
23493     /**
23494      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
23495      * when mode = 'remote' (defaults to 'Loading...')
23496      */
23497     loadingText: 'Loading...',
23498     /**
23499      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
23500      */
23501     resizable: false,
23502     /**
23503      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
23504      */
23505     handleHeight : 8,
23506     /**
23507      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
23508      * traditional select (defaults to true)
23509      */
23510     editable: true,
23511     /**
23512      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
23513      */
23514     allQuery: '',
23515     /**
23516      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
23517      */
23518     mode: 'remote',
23519     /**
23520      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
23521      * listWidth has a higher value)
23522      */
23523     minListWidth : 70,
23524     /**
23525      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
23526      * allow the user to set arbitrary text into the field (defaults to false)
23527      */
23528     forceSelection:false,
23529     /**
23530      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
23531      * if typeAhead = true (defaults to 250)
23532      */
23533     typeAheadDelay : 250,
23534     /**
23535      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
23536      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
23537      */
23538     valueNotFoundText : undefined,
23539     /**
23540      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
23541      */
23542     blockFocus : false,
23543     
23544     /**
23545      * @cfg {Boolean} disableClear Disable showing of clear button.
23546      */
23547     disableClear : false,
23548     /**
23549      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
23550      */
23551     alwaysQuery : false,
23552     
23553     //private
23554     addicon : false,
23555     editicon: false,
23556     
23557     // element that contains real text value.. (when hidden is used..)
23558      
23559     // private
23560     onRender : function(ct, position){
23561         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
23562         if(this.hiddenName){
23563             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
23564                     'before', true);
23565             this.hiddenField.value =
23566                 this.hiddenValue !== undefined ? this.hiddenValue :
23567                 this.value !== undefined ? this.value : '';
23568
23569             // prevent input submission
23570             this.el.dom.removeAttribute('name');
23571              
23572              
23573         }
23574         if(Roo.isGecko){
23575             this.el.dom.setAttribute('autocomplete', 'off');
23576         }
23577
23578         var cls = 'x-combo-list';
23579
23580         this.list = new Roo.Layer({
23581             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
23582         });
23583
23584         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
23585         this.list.setWidth(lw);
23586         this.list.swallowEvent('mousewheel');
23587         this.assetHeight = 0;
23588
23589         if(this.title){
23590             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
23591             this.assetHeight += this.header.getHeight();
23592         }
23593
23594         this.innerList = this.list.createChild({cls:cls+'-inner'});
23595         this.innerList.on('mouseover', this.onViewOver, this);
23596         this.innerList.on('mousemove', this.onViewMove, this);
23597         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23598         
23599         if(this.allowBlank && !this.pageSize && !this.disableClear){
23600             this.footer = this.list.createChild({cls:cls+'-ft'});
23601             this.pageTb = new Roo.Toolbar(this.footer);
23602            
23603         }
23604         if(this.pageSize){
23605             this.footer = this.list.createChild({cls:cls+'-ft'});
23606             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
23607                     {pageSize: this.pageSize});
23608             
23609         }
23610         
23611         if (this.pageTb && this.allowBlank && !this.disableClear) {
23612             var _this = this;
23613             this.pageTb.add(new Roo.Toolbar.Fill(), {
23614                 cls: 'x-btn-icon x-btn-clear',
23615                 text: '&#160;',
23616                 handler: function()
23617                 {
23618                     _this.collapse();
23619                     _this.clearValue();
23620                     _this.onSelect(false, -1);
23621                 }
23622             });
23623         }
23624         if (this.footer) {
23625             this.assetHeight += this.footer.getHeight();
23626         }
23627         
23628
23629         if(!this.tpl){
23630             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
23631         }
23632
23633         this.view = new Roo.View(this.innerList, this.tpl, {
23634             singleSelect:true, store: this.store, selectedClass: this.selectedClass
23635         });
23636
23637         this.view.on('click', this.onViewClick, this);
23638
23639         this.store.on('beforeload', this.onBeforeLoad, this);
23640         this.store.on('load', this.onLoad, this);
23641         this.store.on('loadexception', this.onLoadException, this);
23642
23643         if(this.resizable){
23644             this.resizer = new Roo.Resizable(this.list,  {
23645                pinned:true, handles:'se'
23646             });
23647             this.resizer.on('resize', function(r, w, h){
23648                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
23649                 this.listWidth = w;
23650                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
23651                 this.restrictHeight();
23652             }, this);
23653             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
23654         }
23655         if(!this.editable){
23656             this.editable = true;
23657             this.setEditable(false);
23658         }  
23659         
23660         
23661         if (typeof(this.events.add.listeners) != 'undefined') {
23662             
23663             this.addicon = this.wrap.createChild(
23664                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
23665        
23666             this.addicon.on('click', function(e) {
23667                 this.fireEvent('add', this);
23668             }, this);
23669         }
23670         if (typeof(this.events.edit.listeners) != 'undefined') {
23671             
23672             this.editicon = this.wrap.createChild(
23673                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
23674             if (this.addicon) {
23675                 this.editicon.setStyle('margin-left', '40px');
23676             }
23677             this.editicon.on('click', function(e) {
23678                 
23679                 // we fire even  if inothing is selected..
23680                 this.fireEvent('edit', this, this.lastData );
23681                 
23682             }, this);
23683         }
23684         
23685         
23686         
23687     },
23688
23689     // private
23690     initEvents : function(){
23691         Roo.form.ComboBox.superclass.initEvents.call(this);
23692
23693         this.keyNav = new Roo.KeyNav(this.el, {
23694             "up" : function(e){
23695                 this.inKeyMode = true;
23696                 this.selectPrev();
23697             },
23698
23699             "down" : function(e){
23700                 if(!this.isExpanded()){
23701                     this.onTriggerClick();
23702                 }else{
23703                     this.inKeyMode = true;
23704                     this.selectNext();
23705                 }
23706             },
23707
23708             "enter" : function(e){
23709                 this.onViewClick();
23710                 //return true;
23711             },
23712
23713             "esc" : function(e){
23714                 this.collapse();
23715             },
23716
23717             "tab" : function(e){
23718                 this.onViewClick(false);
23719                 this.fireEvent("specialkey", this, e);
23720                 return true;
23721             },
23722
23723             scope : this,
23724
23725             doRelay : function(foo, bar, hname){
23726                 if(hname == 'down' || this.scope.isExpanded()){
23727                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
23728                 }
23729                 return true;
23730             },
23731
23732             forceKeyDown: true
23733         });
23734         this.queryDelay = Math.max(this.queryDelay || 10,
23735                 this.mode == 'local' ? 10 : 250);
23736         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
23737         if(this.typeAhead){
23738             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
23739         }
23740         if(this.editable !== false){
23741             this.el.on("keyup", this.onKeyUp, this);
23742         }
23743         if(this.forceSelection){
23744             this.on('blur', this.doForce, this);
23745         }
23746     },
23747
23748     onDestroy : function(){
23749         if(this.view){
23750             this.view.setStore(null);
23751             this.view.el.removeAllListeners();
23752             this.view.el.remove();
23753             this.view.purgeListeners();
23754         }
23755         if(this.list){
23756             this.list.destroy();
23757         }
23758         if(this.store){
23759             this.store.un('beforeload', this.onBeforeLoad, this);
23760             this.store.un('load', this.onLoad, this);
23761             this.store.un('loadexception', this.onLoadException, this);
23762         }
23763         Roo.form.ComboBox.superclass.onDestroy.call(this);
23764     },
23765
23766     // private
23767     fireKey : function(e){
23768         if(e.isNavKeyPress() && !this.list.isVisible()){
23769             this.fireEvent("specialkey", this, e);
23770         }
23771     },
23772
23773     // private
23774     onResize: function(w, h){
23775         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
23776         
23777         if(typeof w != 'number'){
23778             // we do not handle it!?!?
23779             return;
23780         }
23781         var tw = this.trigger.getWidth();
23782         tw += this.addicon ? this.addicon.getWidth() : 0;
23783         tw += this.editicon ? this.editicon.getWidth() : 0;
23784         var x = w - tw;
23785         this.el.setWidth( this.adjustWidth('input', x));
23786             
23787         this.trigger.setStyle('left', x+'px');
23788         
23789         if(this.list && this.listWidth === undefined){
23790             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
23791             this.list.setWidth(lw);
23792             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
23793         }
23794         
23795     
23796         
23797     },
23798
23799     /**
23800      * Allow or prevent the user from directly editing the field text.  If false is passed,
23801      * the user will only be able to select from the items defined in the dropdown list.  This method
23802      * is the runtime equivalent of setting the 'editable' config option at config time.
23803      * @param {Boolean} value True to allow the user to directly edit the field text
23804      */
23805     setEditable : function(value){
23806         if(value == this.editable){
23807             return;
23808         }
23809         this.editable = value;
23810         if(!value){
23811             this.el.dom.setAttribute('readOnly', true);
23812             this.el.on('mousedown', this.onTriggerClick,  this);
23813             this.el.addClass('x-combo-noedit');
23814         }else{
23815             this.el.dom.setAttribute('readOnly', false);
23816             this.el.un('mousedown', this.onTriggerClick,  this);
23817             this.el.removeClass('x-combo-noedit');
23818         }
23819     },
23820
23821     // private
23822     onBeforeLoad : function(){
23823         if(!this.hasFocus){
23824             return;
23825         }
23826         this.innerList.update(this.loadingText ?
23827                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
23828         this.restrictHeight();
23829         this.selectedIndex = -1;
23830     },
23831
23832     // private
23833     onLoad : function(){
23834         if(!this.hasFocus){
23835             return;
23836         }
23837         if(this.store.getCount() > 0){
23838             this.expand();
23839             this.restrictHeight();
23840             if(this.lastQuery == this.allQuery){
23841                 if(this.editable){
23842                     this.el.dom.select();
23843                 }
23844                 if(!this.selectByValue(this.value, true)){
23845                     this.select(0, true);
23846                 }
23847             }else{
23848                 this.selectNext();
23849                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
23850                     this.taTask.delay(this.typeAheadDelay);
23851                 }
23852             }
23853         }else{
23854             this.onEmptyResults();
23855         }
23856         //this.el.focus();
23857     },
23858     // private
23859     onLoadException : function()
23860     {
23861         this.collapse();
23862         Roo.log(this.store.reader.jsonData);
23863         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
23864             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
23865         }
23866         
23867         
23868     },
23869     // private
23870     onTypeAhead : function(){
23871         if(this.store.getCount() > 0){
23872             var r = this.store.getAt(0);
23873             var newValue = r.data[this.displayField];
23874             var len = newValue.length;
23875             var selStart = this.getRawValue().length;
23876             if(selStart != len){
23877                 this.setRawValue(newValue);
23878                 this.selectText(selStart, newValue.length);
23879             }
23880         }
23881     },
23882
23883     // private
23884     onSelect : function(record, index){
23885         if(this.fireEvent('beforeselect', this, record, index) !== false){
23886             this.setFromData(index > -1 ? record.data : false);
23887             this.collapse();
23888             this.fireEvent('select', this, record, index);
23889         }
23890     },
23891
23892     /**
23893      * Returns the currently selected field value or empty string if no value is set.
23894      * @return {String} value The selected value
23895      */
23896     getValue : function(){
23897         if(this.valueField){
23898             return typeof this.value != 'undefined' ? this.value : '';
23899         }else{
23900             return Roo.form.ComboBox.superclass.getValue.call(this);
23901         }
23902     },
23903
23904     /**
23905      * Clears any text/value currently set in the field
23906      */
23907     clearValue : function(){
23908         if(this.hiddenField){
23909             this.hiddenField.value = '';
23910         }
23911         this.value = '';
23912         this.setRawValue('');
23913         this.lastSelectionText = '';
23914         this.applyEmptyText();
23915     },
23916
23917     /**
23918      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
23919      * will be displayed in the field.  If the value does not match the data value of an existing item,
23920      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
23921      * Otherwise the field will be blank (although the value will still be set).
23922      * @param {String} value The value to match
23923      */
23924     setValue : function(v){
23925         var text = v;
23926         if(this.valueField){
23927             var r = this.findRecord(this.valueField, v);
23928             if(r){
23929                 text = r.data[this.displayField];
23930             }else if(this.valueNotFoundText !== undefined){
23931                 text = this.valueNotFoundText;
23932             }
23933         }
23934         this.lastSelectionText = text;
23935         if(this.hiddenField){
23936             this.hiddenField.value = v;
23937         }
23938         Roo.form.ComboBox.superclass.setValue.call(this, text);
23939         this.value = v;
23940     },
23941     /**
23942      * @property {Object} the last set data for the element
23943      */
23944     
23945     lastData : false,
23946     /**
23947      * Sets the value of the field based on a object which is related to the record format for the store.
23948      * @param {Object} value the value to set as. or false on reset?
23949      */
23950     setFromData : function(o){
23951         var dv = ''; // display value
23952         var vv = ''; // value value..
23953         this.lastData = o;
23954         if (this.displayField) {
23955             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
23956         } else {
23957             // this is an error condition!!!
23958             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
23959         }
23960         
23961         if(this.valueField){
23962             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
23963         }
23964         if(this.hiddenField){
23965             this.hiddenField.value = vv;
23966             
23967             this.lastSelectionText = dv;
23968             Roo.form.ComboBox.superclass.setValue.call(this, dv);
23969             this.value = vv;
23970             return;
23971         }
23972         // no hidden field.. - we store the value in 'value', but still display
23973         // display field!!!!
23974         this.lastSelectionText = dv;
23975         Roo.form.ComboBox.superclass.setValue.call(this, dv);
23976         this.value = vv;
23977         
23978         
23979     },
23980     // private
23981     reset : function(){
23982         // overridden so that last data is reset..
23983         this.setValue(this.originalValue);
23984         this.clearInvalid();
23985         this.lastData = false;
23986     },
23987     // private
23988     findRecord : function(prop, value){
23989         var record;
23990         if(this.store.getCount() > 0){
23991             this.store.each(function(r){
23992                 if(r.data[prop] == value){
23993                     record = r;
23994                     return false;
23995                 }
23996                 return true;
23997             });
23998         }
23999         return record;
24000     },
24001     
24002     getName: function()
24003     {
24004         // returns hidden if it's set..
24005         if (!this.rendered) {return ''};
24006         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
24007         
24008     },
24009     // private
24010     onViewMove : function(e, t){
24011         this.inKeyMode = false;
24012     },
24013
24014     // private
24015     onViewOver : function(e, t){
24016         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
24017             return;
24018         }
24019         var item = this.view.findItemFromChild(t);
24020         if(item){
24021             var index = this.view.indexOf(item);
24022             this.select(index, false);
24023         }
24024     },
24025
24026     // private
24027     onViewClick : function(doFocus)
24028     {
24029         var index = this.view.getSelectedIndexes()[0];
24030         var r = this.store.getAt(index);
24031         if(r){
24032             this.onSelect(r, index);
24033         }
24034         if(doFocus !== false && !this.blockFocus){
24035             this.el.focus();
24036         }
24037     },
24038
24039     // private
24040     restrictHeight : function(){
24041         this.innerList.dom.style.height = '';
24042         var inner = this.innerList.dom;
24043         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
24044         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
24045         this.list.beginUpdate();
24046         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
24047         this.list.alignTo(this.el, this.listAlign);
24048         this.list.endUpdate();
24049     },
24050
24051     // private
24052     onEmptyResults : function(){
24053         this.collapse();
24054     },
24055
24056     /**
24057      * Returns true if the dropdown list is expanded, else false.
24058      */
24059     isExpanded : function(){
24060         return this.list.isVisible();
24061     },
24062
24063     /**
24064      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
24065      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24066      * @param {String} value The data value of the item to select
24067      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24068      * selected item if it is not currently in view (defaults to true)
24069      * @return {Boolean} True if the value matched an item in the list, else false
24070      */
24071     selectByValue : function(v, scrollIntoView){
24072         if(v !== undefined && v !== null){
24073             var r = this.findRecord(this.valueField || this.displayField, v);
24074             if(r){
24075                 this.select(this.store.indexOf(r), scrollIntoView);
24076                 return true;
24077             }
24078         }
24079         return false;
24080     },
24081
24082     /**
24083      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
24084      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
24085      * @param {Number} index The zero-based index of the list item to select
24086      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
24087      * selected item if it is not currently in view (defaults to true)
24088      */
24089     select : function(index, scrollIntoView){
24090         this.selectedIndex = index;
24091         this.view.select(index);
24092         if(scrollIntoView !== false){
24093             var el = this.view.getNode(index);
24094             if(el){
24095                 this.innerList.scrollChildIntoView(el, false);
24096             }
24097         }
24098     },
24099
24100     // private
24101     selectNext : function(){
24102         var ct = this.store.getCount();
24103         if(ct > 0){
24104             if(this.selectedIndex == -1){
24105                 this.select(0);
24106             }else if(this.selectedIndex < ct-1){
24107                 this.select(this.selectedIndex+1);
24108             }
24109         }
24110     },
24111
24112     // private
24113     selectPrev : function(){
24114         var ct = this.store.getCount();
24115         if(ct > 0){
24116             if(this.selectedIndex == -1){
24117                 this.select(0);
24118             }else if(this.selectedIndex != 0){
24119                 this.select(this.selectedIndex-1);
24120             }
24121         }
24122     },
24123
24124     // private
24125     onKeyUp : function(e){
24126         if(this.editable !== false && !e.isSpecialKey()){
24127             this.lastKey = e.getKey();
24128             this.dqTask.delay(this.queryDelay);
24129         }
24130     },
24131
24132     // private
24133     validateBlur : function(){
24134         return !this.list || !this.list.isVisible();   
24135     },
24136
24137     // private
24138     initQuery : function(){
24139         this.doQuery(this.getRawValue());
24140     },
24141
24142     // private
24143     doForce : function(){
24144         if(this.el.dom.value.length > 0){
24145             this.el.dom.value =
24146                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
24147             this.applyEmptyText();
24148         }
24149     },
24150
24151     /**
24152      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
24153      * query allowing the query action to be canceled if needed.
24154      * @param {String} query The SQL query to execute
24155      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
24156      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
24157      * saved in the current store (defaults to false)
24158      */
24159     doQuery : function(q, forceAll){
24160         if(q === undefined || q === null){
24161             q = '';
24162         }
24163         var qe = {
24164             query: q,
24165             forceAll: forceAll,
24166             combo: this,
24167             cancel:false
24168         };
24169         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
24170             return false;
24171         }
24172         q = qe.query;
24173         forceAll = qe.forceAll;
24174         if(forceAll === true || (q.length >= this.minChars)){
24175             if(this.lastQuery != q || this.alwaysQuery){
24176                 this.lastQuery = q;
24177                 if(this.mode == 'local'){
24178                     this.selectedIndex = -1;
24179                     if(forceAll){
24180                         this.store.clearFilter();
24181                     }else{
24182                         this.store.filter(this.displayField, q);
24183                     }
24184                     this.onLoad();
24185                 }else{
24186                     this.store.baseParams[this.queryParam] = q;
24187                     this.store.load({
24188                         params: this.getParams(q)
24189                     });
24190                     this.expand();
24191                 }
24192             }else{
24193                 this.selectedIndex = -1;
24194                 this.onLoad();   
24195             }
24196         }
24197     },
24198
24199     // private
24200     getParams : function(q){
24201         var p = {};
24202         //p[this.queryParam] = q;
24203         if(this.pageSize){
24204             p.start = 0;
24205             p.limit = this.pageSize;
24206         }
24207         return p;
24208     },
24209
24210     /**
24211      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
24212      */
24213     collapse : function(){
24214         if(!this.isExpanded()){
24215             return;
24216         }
24217         this.list.hide();
24218         Roo.get(document).un('mousedown', this.collapseIf, this);
24219         Roo.get(document).un('mousewheel', this.collapseIf, this);
24220         if (!this.editable) {
24221             Roo.get(document).un('keydown', this.listKeyPress, this);
24222         }
24223         this.fireEvent('collapse', this);
24224     },
24225
24226     // private
24227     collapseIf : function(e){
24228         if(!e.within(this.wrap) && !e.within(this.list)){
24229             this.collapse();
24230         }
24231     },
24232
24233     /**
24234      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
24235      */
24236     expand : function(){
24237         if(this.isExpanded() || !this.hasFocus){
24238             return;
24239         }
24240         this.list.alignTo(this.el, this.listAlign);
24241         this.list.show();
24242         Roo.get(document).on('mousedown', this.collapseIf, this);
24243         Roo.get(document).on('mousewheel', this.collapseIf, this);
24244         if (!this.editable) {
24245             Roo.get(document).on('keydown', this.listKeyPress, this);
24246         }
24247         
24248         this.fireEvent('expand', this);
24249     },
24250
24251     // private
24252     // Implements the default empty TriggerField.onTriggerClick function
24253     onTriggerClick : function(){
24254         if(this.disabled){
24255             return;
24256         }
24257         if(this.isExpanded()){
24258             this.collapse();
24259             if (!this.blockFocus) {
24260                 this.el.focus();
24261             }
24262             
24263         }else {
24264             this.hasFocus = true;
24265             if(this.triggerAction == 'all') {
24266                 this.doQuery(this.allQuery, true);
24267             } else {
24268                 this.doQuery(this.getRawValue());
24269             }
24270             if (!this.blockFocus) {
24271                 this.el.focus();
24272             }
24273         }
24274     },
24275     listKeyPress : function(e)
24276     {
24277         //Roo.log('listkeypress');
24278         // scroll to first matching element based on key pres..
24279         if (e.isSpecialKey()) {
24280             return false;
24281         }
24282         var k = String.fromCharCode(e.getKey()).toUpperCase();
24283         //Roo.log(k);
24284         var match  = false;
24285         var csel = this.view.getSelectedNodes();
24286         var cselitem = false;
24287         if (csel.length) {
24288             var ix = this.view.indexOf(csel[0]);
24289             cselitem  = this.store.getAt(ix);
24290             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
24291                 cselitem = false;
24292             }
24293             
24294         }
24295         
24296         this.store.each(function(v) { 
24297             if (cselitem) {
24298                 // start at existing selection.
24299                 if (cselitem.id == v.id) {
24300                     cselitem = false;
24301                 }
24302                 return;
24303             }
24304                 
24305             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
24306                 match = this.store.indexOf(v);
24307                 return false;
24308             }
24309         }, this);
24310         
24311         if (match === false) {
24312             return true; // no more action?
24313         }
24314         // scroll to?
24315         this.view.select(match);
24316         var sn = Roo.get(this.view.getSelectedNodes()[0])
24317         sn.scrollIntoView(sn.dom.parentNode, false);
24318     }
24319
24320     /** 
24321     * @cfg {Boolean} grow 
24322     * @hide 
24323     */
24324     /** 
24325     * @cfg {Number} growMin 
24326     * @hide 
24327     */
24328     /** 
24329     * @cfg {Number} growMax 
24330     * @hide 
24331     */
24332     /**
24333      * @hide
24334      * @method autoSize
24335      */
24336 });/*
24337  * Copyright(c) 2010-2012, Roo J Solutions Limited
24338  *
24339  * Licence LGPL
24340  *
24341  */
24342
24343 /**
24344  * @class Roo.form.ComboBoxArray
24345  * @extends Roo.form.TextField
24346  * A facebook style adder... for lists of email / people / countries  etc...
24347  * pick multiple items from a combo box, and shows each one.
24348  *
24349  *  Fred [x]  Brian [x]  [Pick another |v]
24350  *
24351  *
24352  *  For this to work: it needs various extra information
24353  *    - normal combo problay has
24354  *      name, hiddenName
24355  *    + displayField, valueField
24356  *
24357  *    For our purpose...
24358  *
24359  *
24360  *   If we change from 'extends' to wrapping...
24361  *   
24362  *  
24363  *
24364  
24365  
24366  * @constructor
24367  * Create a new ComboBoxArray.
24368  * @param {Object} config Configuration options
24369  */
24370  
24371
24372 Roo.form.ComboBoxArray = function(config)
24373 {
24374     
24375     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
24376     
24377     this.items = new Roo.util.MixedCollection(false);
24378     
24379     // construct the child combo...
24380     
24381     
24382     
24383     
24384    
24385     
24386 }
24387
24388  
24389 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
24390
24391     /**
24392      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
24393      */
24394     
24395     lastData : false,
24396     
24397     // behavies liek a hiddne field
24398     inputType:      'hidden',
24399     /**
24400      * @cfg {Number} width The width of the box that displays the selected element
24401      */ 
24402     width:          300,
24403
24404     
24405     
24406     /**
24407      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
24408      */
24409     name : false,
24410     /**
24411      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
24412      */
24413     hiddenName : false,
24414     
24415     
24416     // private the array of items that are displayed..
24417     items  : false,
24418     // private - the hidden field el.
24419     hiddenEl : false,
24420     // private - the filed el..
24421     el : false,
24422     
24423     //validateValue : function() { return true; }, // all values are ok!
24424     //onAddClick: function() { },
24425     
24426     onRender : function(ct, position) 
24427     {
24428         
24429         // create the standard hidden element
24430         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
24431         
24432         
24433         // give fake names to child combo;
24434         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
24435         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
24436         
24437         this.combo = Roo.factory(this.combo, Roo.form);
24438         this.combo.onRender(ct, position);
24439         
24440         // assigned so form know we need to do this..
24441         this.store          = this.combo.store;
24442         this.valueField     = this.combo.valueField;
24443         this.displayField   = this.combo.displayField ;
24444         
24445         
24446         this.combo.wrap.addClass('x-cbarray-grp');
24447         
24448         var cbwrap = this.combo.wrap.createChild(
24449             {tag: 'div', cls: 'x-cbarray-cb'},
24450             this.combo.el.dom
24451         );
24452         
24453              
24454         this.hiddenEl = this.combo.wrap.createChild({
24455             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
24456         });
24457         this.el = this.combo.wrap.createChild({
24458             tag: 'input',  type:'hidden' , name: this.name, value : ''
24459         });
24460          //   this.el.dom.removeAttribute("name");
24461         
24462         
24463         this.outerWrap = this.combo.wrap;
24464         this.wrap = cbwrap;
24465         
24466         this.outerWrap.setWidth(this.width);
24467         this.outerWrap.dom.removeChild(this.el.dom);
24468         
24469         this.wrap.dom.appendChild(this.el.dom);
24470         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
24471         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
24472         
24473         this.combo.trigger.setStyle('position','relative');
24474         this.combo.trigger.setStyle('left', '0px');
24475         this.combo.trigger.setStyle('top', '2px');
24476         
24477         this.combo.el.setStyle('vertical-align', 'text-bottom');
24478         
24479         //this.trigger.setStyle('vertical-align', 'top');
24480         
24481         // this should use the code from combo really... on('add' ....)
24482         if (this.adder) {
24483             
24484         
24485             this.adder = this.outerWrap.createChild(
24486                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
24487             var _t = this;
24488             this.adder.on('click', function(e) {
24489                 _t.fireEvent('adderclick', this, e);
24490             }, _t);
24491         }
24492         //var _t = this;
24493         //this.adder.on('click', this.onAddClick, _t);
24494         
24495         
24496         this.combo.on('select', function(cb, rec, ix) {
24497             this.addItem(rec.data);
24498             
24499             cb.setValue('');
24500             cb.el.dom.value = '';
24501             //cb.lastData = rec.data;
24502             // add to list
24503             
24504         }, this);
24505         
24506         
24507     },
24508     
24509     
24510     getName: function()
24511     {
24512         // returns hidden if it's set..
24513         if (!this.rendered) {return ''};
24514         return  this.hiddenName ? this.hiddenName : this.name;
24515         
24516     },
24517     
24518     
24519     onResize: function(w, h){
24520         
24521         return;
24522         // not sure if this is needed..
24523         //this.combo.onResize(w,h);
24524         
24525         if(typeof w != 'number'){
24526             // we do not handle it!?!?
24527             return;
24528         }
24529         var tw = this.combo.trigger.getWidth();
24530         tw += this.addicon ? this.addicon.getWidth() : 0;
24531         tw += this.editicon ? this.editicon.getWidth() : 0;
24532         var x = w - tw;
24533         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
24534             
24535         this.combo.trigger.setStyle('left', '0px');
24536         
24537         if(this.list && this.listWidth === undefined){
24538             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
24539             this.list.setWidth(lw);
24540             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
24541         }
24542         
24543     
24544         
24545     },
24546     
24547     addItem: function(rec)
24548     {
24549         var valueField = this.combo.valueField;
24550         var displayField = this.combo.displayField;
24551         if (this.items.indexOfKey(rec[valueField]) > -1) {
24552             //console.log("GOT " + rec.data.id);
24553             return;
24554         }
24555         
24556         var x = new Roo.form.ComboBoxArray.Item({
24557             //id : rec[this.idField],
24558             data : rec,
24559             displayField : displayField ,
24560             tipField : displayField ,
24561             cb : this
24562         });
24563         // use the 
24564         this.items.add(rec[valueField],x);
24565         // add it before the element..
24566         this.updateHiddenEl();
24567         x.render(this.outerWrap, this.wrap.dom);
24568         // add the image handler..
24569     },
24570     
24571     updateHiddenEl : function()
24572     {
24573         this.validate();
24574         if (!this.hiddenEl) {
24575             return;
24576         }
24577         var ar = [];
24578         var idField = this.combo.valueField;
24579         
24580         this.items.each(function(f) {
24581             ar.push(f.data[idField]);
24582            
24583         });
24584         this.hiddenEl.dom.value = ar.join(',');
24585         this.validate();
24586     },
24587     
24588     reset : function()
24589     {
24590         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
24591         this.items.each(function(f) {
24592            f.remove(); 
24593         });
24594         this.el.dom.value = '';
24595         if (this.hiddenEl) {
24596             this.hiddenEl.dom.value = '';
24597         }
24598         
24599     },
24600     getValue: function()
24601     {
24602         return this.hiddenEl ? this.hiddenEl.dom.value : '';
24603     },
24604     setValue: function(v) // not a valid action - must use addItems..
24605     {
24606          
24607         this.reset();
24608         
24609         
24610         
24611         if (this.store.isLocal && (typeof(v) == 'string')) {
24612             // then we can use the store to find the values..
24613             // comma seperated at present.. this needs to allow JSON based encoding..
24614             this.hiddenEl.value  = v;
24615             var v_ar = [];
24616             Roo.each(v.split(','), function(k) {
24617                 Roo.log("CHECK " + this.valueField + ',' + k);
24618                 var li = this.store.query(this.valueField, k);
24619                 if (!li.length) {
24620                     return;
24621                 }
24622                 add = {};
24623                 add[this.valueField] = k;
24624                 add[this.displayField] = li.item(0).data[this.displayField];
24625                 
24626                 this.addItem(add);
24627             }, this) 
24628              
24629         }
24630         if (typeof(v) == 'object') {
24631             // then let's assume it's an array of objects..
24632             Roo.each(v, function(l) {
24633                 this.addItem(l);
24634             }, this);
24635              
24636         }
24637         
24638         
24639     },
24640     setFromData: function(v)
24641     {
24642         // this recieves an object, if setValues is called.
24643         this.reset();
24644         this.el.dom.value = v[this.displayField];
24645         this.hiddenEl.dom.value = v[this.valueField];
24646         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
24647             return;
24648         }
24649         var kv = v[this.valueField];
24650         var dv = v[this.displayField];
24651         kv = typeof(kv) != 'string' ? '' : kv;
24652         dv = typeof(dv) != 'string' ? '' : dv;
24653         
24654         
24655         var keys = kv.split(',');
24656         var display = dv.split(',');
24657         for (var i = 0 ; i < keys.length; i++) {
24658             
24659             add = {};
24660             add[this.valueField] = keys[i];
24661             add[this.displayField] = display[i];
24662             this.addItem(add);
24663         }
24664       
24665         
24666     },
24667     
24668     
24669     validateValue : function(value){
24670         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
24671         
24672     }
24673     
24674 });
24675
24676
24677
24678 /**
24679  * @class Roo.form.ComboBoxArray.Item
24680  * @extends Roo.BoxComponent
24681  * A selected item in the list
24682  *  Fred [x]  Brian [x]  [Pick another |v]
24683  * 
24684  * @constructor
24685  * Create a new item.
24686  * @param {Object} config Configuration options
24687  */
24688  
24689 Roo.form.ComboBoxArray.Item = function(config) {
24690     config.id = Roo.id();
24691     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
24692 }
24693
24694 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
24695     data : {},
24696     cb: false,
24697     displayField : false,
24698     tipField : false,
24699     
24700     
24701     defaultAutoCreate : {
24702         tag: 'div',
24703         cls: 'x-cbarray-item',
24704         cn : [ 
24705             { tag: 'div' },
24706             {
24707                 tag: 'img',
24708                 width:16,
24709                 height : 16,
24710                 src : Roo.BLANK_IMAGE_URL ,
24711                 align: 'center'
24712             }
24713         ]
24714         
24715     },
24716     
24717  
24718     onRender : function(ct, position)
24719     {
24720         Roo.form.Field.superclass.onRender.call(this, ct, position);
24721         
24722         if(!this.el){
24723             var cfg = this.getAutoCreate();
24724             this.el = ct.createChild(cfg, position);
24725         }
24726         
24727         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
24728         
24729         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
24730             this.cb.renderer(this.data) :
24731             String.format('{0}',this.data[this.displayField]);
24732         
24733             
24734         this.el.child('div').dom.setAttribute('qtip',
24735                         String.format('{0}',this.data[this.tipField])
24736         );
24737         
24738         this.el.child('img').on('click', this.remove, this);
24739         
24740     },
24741    
24742     remove : function()
24743     {
24744         
24745         this.cb.items.remove(this);
24746         this.el.child('img').un('click', this.remove, this);
24747         this.el.remove();
24748         this.cb.updateHiddenEl();
24749     }
24750     
24751     
24752 });/*
24753  * Based on:
24754  * Ext JS Library 1.1.1
24755  * Copyright(c) 2006-2007, Ext JS, LLC.
24756  *
24757  * Originally Released Under LGPL - original licence link has changed is not relivant.
24758  *
24759  * Fork - LGPL
24760  * <script type="text/javascript">
24761  */
24762 /**
24763  * @class Roo.form.Checkbox
24764  * @extends Roo.form.Field
24765  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
24766  * @constructor
24767  * Creates a new Checkbox
24768  * @param {Object} config Configuration options
24769  */
24770 Roo.form.Checkbox = function(config){
24771     Roo.form.Checkbox.superclass.constructor.call(this, config);
24772     this.addEvents({
24773         /**
24774          * @event check
24775          * Fires when the checkbox is checked or unchecked.
24776              * @param {Roo.form.Checkbox} this This checkbox
24777              * @param {Boolean} checked The new checked value
24778              */
24779         check : true
24780     });
24781 };
24782
24783 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
24784     /**
24785      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
24786      */
24787     focusClass : undefined,
24788     /**
24789      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
24790      */
24791     fieldClass: "x-form-field",
24792     /**
24793      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
24794      */
24795     checked: false,
24796     /**
24797      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
24798      * {tag: "input", type: "checkbox", autocomplete: "off"})
24799      */
24800     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
24801     /**
24802      * @cfg {String} boxLabel The text that appears beside the checkbox
24803      */
24804     boxLabel : "",
24805     /**
24806      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
24807      */  
24808     inputValue : '1',
24809     /**
24810      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24811      */
24812      valueOff: '0', // value when not checked..
24813
24814     actionMode : 'viewEl', 
24815     //
24816     // private
24817     itemCls : 'x-menu-check-item x-form-item',
24818     groupClass : 'x-menu-group-item',
24819     inputType : 'hidden',
24820     
24821     
24822     inSetChecked: false, // check that we are not calling self...
24823     
24824     inputElement: false, // real input element?
24825     basedOn: false, // ????
24826     
24827     isFormField: true, // not sure where this is needed!!!!
24828
24829     onResize : function(){
24830         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
24831         if(!this.boxLabel){
24832             this.el.alignTo(this.wrap, 'c-c');
24833         }
24834     },
24835
24836     initEvents : function(){
24837         Roo.form.Checkbox.superclass.initEvents.call(this);
24838         this.el.on("click", this.onClick,  this);
24839         this.el.on("change", this.onClick,  this);
24840     },
24841
24842
24843     getResizeEl : function(){
24844         return this.wrap;
24845     },
24846
24847     getPositionEl : function(){
24848         return this.wrap;
24849     },
24850
24851     // private
24852     onRender : function(ct, position){
24853         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
24854         /*
24855         if(this.inputValue !== undefined){
24856             this.el.dom.value = this.inputValue;
24857         }
24858         */
24859         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
24860         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
24861         var viewEl = this.wrap.createChild({ 
24862             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
24863         this.viewEl = viewEl;   
24864         this.wrap.on('click', this.onClick,  this); 
24865         
24866         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
24867         this.el.on('propertychange', this.setFromHidden,  this);  //ie
24868         
24869         
24870         
24871         if(this.boxLabel){
24872             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
24873         //    viewEl.on('click', this.onClick,  this); 
24874         }
24875         //if(this.checked){
24876             this.setChecked(this.checked);
24877         //}else{
24878             //this.checked = this.el.dom;
24879         //}
24880
24881     },
24882
24883     // private
24884     initValue : Roo.emptyFn,
24885
24886     /**
24887      * Returns the checked state of the checkbox.
24888      * @return {Boolean} True if checked, else false
24889      */
24890     getValue : function(){
24891         if(this.el){
24892             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
24893         }
24894         return this.valueOff;
24895         
24896     },
24897
24898         // private
24899     onClick : function(){ 
24900         this.setChecked(!this.checked);
24901
24902         //if(this.el.dom.checked != this.checked){
24903         //    this.setValue(this.el.dom.checked);
24904        // }
24905     },
24906
24907     /**
24908      * Sets the checked state of the checkbox.
24909      * On is always based on a string comparison between inputValue and the param.
24910      * @param {Boolean/String} value - the value to set 
24911      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
24912      */
24913     setValue : function(v,suppressEvent){
24914         
24915         
24916         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
24917         //if(this.el && this.el.dom){
24918         //    this.el.dom.checked = this.checked;
24919         //    this.el.dom.defaultChecked = this.checked;
24920         //}
24921         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
24922         //this.fireEvent("check", this, this.checked);
24923     },
24924     // private..
24925     setChecked : function(state,suppressEvent)
24926     {
24927         if (this.inSetChecked) {
24928             this.checked = state;
24929             return;
24930         }
24931         
24932     
24933         if(this.wrap){
24934             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
24935         }
24936         this.checked = state;
24937         if(suppressEvent !== true){
24938             this.fireEvent('check', this, state);
24939         }
24940         this.inSetChecked = true;
24941         this.el.dom.value = state ? this.inputValue : this.valueOff;
24942         this.inSetChecked = false;
24943         
24944     },
24945     // handle setting of hidden value by some other method!!?!?
24946     setFromHidden: function()
24947     {
24948         if(!this.el){
24949             return;
24950         }
24951         //console.log("SET FROM HIDDEN");
24952         //alert('setFrom hidden');
24953         this.setValue(this.el.dom.value);
24954     },
24955     
24956     onDestroy : function()
24957     {
24958         if(this.viewEl){
24959             Roo.get(this.viewEl).remove();
24960         }
24961          
24962         Roo.form.Checkbox.superclass.onDestroy.call(this);
24963     }
24964
24965 });/*
24966  * Based on:
24967  * Ext JS Library 1.1.1
24968  * Copyright(c) 2006-2007, Ext JS, LLC.
24969  *
24970  * Originally Released Under LGPL - original licence link has changed is not relivant.
24971  *
24972  * Fork - LGPL
24973  * <script type="text/javascript">
24974  */
24975  
24976 /**
24977  * @class Roo.form.Radio
24978  * @extends Roo.form.Checkbox
24979  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
24980  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
24981  * @constructor
24982  * Creates a new Radio
24983  * @param {Object} config Configuration options
24984  */
24985 Roo.form.Radio = function(){
24986     Roo.form.Radio.superclass.constructor.apply(this, arguments);
24987 };
24988 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
24989     inputType: 'radio',
24990
24991     /**
24992      * If this radio is part of a group, it will return the selected value
24993      * @return {String}
24994      */
24995     getGroupValue : function(){
24996         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
24997     }
24998 });//<script type="text/javascript">
24999
25000 /*
25001  * Ext JS Library 1.1.1
25002  * Copyright(c) 2006-2007, Ext JS, LLC.
25003  * licensing@extjs.com
25004  * 
25005  * http://www.extjs.com/license
25006  */
25007  
25008  /*
25009   * 
25010   * Known bugs:
25011   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
25012   * - IE ? - no idea how much works there.
25013   * 
25014   * 
25015   * 
25016   */
25017  
25018
25019 /**
25020  * @class Ext.form.HtmlEditor
25021  * @extends Ext.form.Field
25022  * Provides a lightweight HTML Editor component.
25023  *
25024  * This has been tested on Fireforx / Chrome.. IE may not be so great..
25025  * 
25026  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
25027  * supported by this editor.</b><br/><br/>
25028  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
25029  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25030  */
25031 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
25032       /**
25033      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25034      */
25035     toolbars : false,
25036     /**
25037      * @cfg {String} createLinkText The default text for the create link prompt
25038      */
25039     createLinkText : 'Please enter the URL for the link:',
25040     /**
25041      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
25042      */
25043     defaultLinkValue : 'http:/'+'/',
25044    
25045      /**
25046      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25047      *                        Roo.resizable.
25048      */
25049     resizable : false,
25050      /**
25051      * @cfg {Number} height (in pixels)
25052      */   
25053     height: 300,
25054    /**
25055      * @cfg {Number} width (in pixels)
25056      */   
25057     width: 500,
25058     
25059     /**
25060      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25061      * 
25062      */
25063     stylesheets: false,
25064     
25065     // id of frame..
25066     frameId: false,
25067     
25068     // private properties
25069     validationEvent : false,
25070     deferHeight: true,
25071     initialized : false,
25072     activated : false,
25073     sourceEditMode : false,
25074     onFocus : Roo.emptyFn,
25075     iframePad:3,
25076     hideMode:'offsets',
25077     
25078     defaultAutoCreate : { // modified by initCompnoent..
25079         tag: "textarea",
25080         style:"width:500px;height:300px;",
25081         autocomplete: "off"
25082     },
25083
25084     // private
25085     initComponent : function(){
25086         this.addEvents({
25087             /**
25088              * @event initialize
25089              * Fires when the editor is fully initialized (including the iframe)
25090              * @param {HtmlEditor} this
25091              */
25092             initialize: true,
25093             /**
25094              * @event activate
25095              * Fires when the editor is first receives the focus. Any insertion must wait
25096              * until after this event.
25097              * @param {HtmlEditor} this
25098              */
25099             activate: true,
25100              /**
25101              * @event beforesync
25102              * Fires before the textarea is updated with content from the editor iframe. Return false
25103              * to cancel the sync.
25104              * @param {HtmlEditor} this
25105              * @param {String} html
25106              */
25107             beforesync: true,
25108              /**
25109              * @event beforepush
25110              * Fires before the iframe editor is updated with content from the textarea. Return false
25111              * to cancel the push.
25112              * @param {HtmlEditor} this
25113              * @param {String} html
25114              */
25115             beforepush: true,
25116              /**
25117              * @event sync
25118              * Fires when the textarea is updated with content from the editor iframe.
25119              * @param {HtmlEditor} this
25120              * @param {String} html
25121              */
25122             sync: true,
25123              /**
25124              * @event push
25125              * Fires when the iframe editor is updated with content from the textarea.
25126              * @param {HtmlEditor} this
25127              * @param {String} html
25128              */
25129             push: true,
25130              /**
25131              * @event editmodechange
25132              * Fires when the editor switches edit modes
25133              * @param {HtmlEditor} this
25134              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25135              */
25136             editmodechange: true,
25137             /**
25138              * @event editorevent
25139              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25140              * @param {HtmlEditor} this
25141              */
25142             editorevent: true
25143         });
25144         this.defaultAutoCreate =  {
25145             tag: "textarea",
25146             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
25147             autocomplete: "off"
25148         };
25149     },
25150
25151     /**
25152      * Protected method that will not generally be called directly. It
25153      * is called when the editor creates its toolbar. Override this method if you need to
25154      * add custom toolbar buttons.
25155      * @param {HtmlEditor} editor
25156      */
25157     createToolbar : function(editor){
25158         if (!editor.toolbars || !editor.toolbars.length) {
25159             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
25160         }
25161         
25162         for (var i =0 ; i < editor.toolbars.length;i++) {
25163             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
25164             editor.toolbars[i].init(editor);
25165         }
25166          
25167         
25168     },
25169
25170     /**
25171      * Protected method that will not generally be called directly. It
25172      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25173      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25174      */
25175     getDocMarkup : function(){
25176         // body styles..
25177         var st = '';
25178         if (this.stylesheets === false) {
25179             
25180             Roo.get(document.head).select('style').each(function(node) {
25181                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25182             });
25183             
25184             Roo.get(document.head).select('link').each(function(node) { 
25185                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25186             });
25187             
25188         } else if (!this.stylesheets.length) {
25189                 // simple..
25190                 st = '<style type="text/css">' +
25191                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25192                    '</style>';
25193         } else {
25194             Roo.each(this.stylesheets, function(s) {
25195                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
25196             });
25197             
25198         }
25199         
25200         st +=  '<style type="text/css">' +
25201             'IMG { cursor: pointer } ' +
25202         '</style>';
25203
25204         
25205         return '<html><head>' + st  +
25206             //<style type="text/css">' +
25207             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25208             //'</style>' +
25209             ' </head><body class="roo-htmleditor-body"></body></html>';
25210     },
25211
25212     // private
25213     onRender : function(ct, position)
25214     {
25215         var _t = this;
25216         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
25217         this.el.dom.style.border = '0 none';
25218         this.el.dom.setAttribute('tabIndex', -1);
25219         this.el.addClass('x-hidden');
25220         if(Roo.isIE){ // fix IE 1px bogus margin
25221             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25222         }
25223         this.wrap = this.el.wrap({
25224             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25225         });
25226         
25227         if (this.resizable) {
25228             this.resizeEl = new Roo.Resizable(this.wrap, {
25229                 pinned : true,
25230                 wrap: true,
25231                 dynamic : true,
25232                 minHeight : this.height,
25233                 height: this.height,
25234                 handles : this.resizable,
25235                 width: this.width,
25236                 listeners : {
25237                     resize : function(r, w, h) {
25238                         _t.onResize(w,h); // -something
25239                     }
25240                 }
25241             });
25242             
25243         }
25244
25245         this.frameId = Roo.id();
25246         
25247         this.createToolbar(this);
25248         
25249       
25250         
25251         var iframe = this.wrap.createChild({
25252             tag: 'iframe',
25253             id: this.frameId,
25254             name: this.frameId,
25255             frameBorder : 'no',
25256             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25257         }, this.el
25258         );
25259         
25260        // console.log(iframe);
25261         //this.wrap.dom.appendChild(iframe);
25262
25263         this.iframe = iframe.dom;
25264
25265          this.assignDocWin();
25266         
25267         this.doc.designMode = 'on';
25268        
25269         this.doc.open();
25270         this.doc.write(this.getDocMarkup());
25271         this.doc.close();
25272
25273         
25274         var task = { // must defer to wait for browser to be ready
25275             run : function(){
25276                 //console.log("run task?" + this.doc.readyState);
25277                 this.assignDocWin();
25278                 if(this.doc.body || this.doc.readyState == 'complete'){
25279                     try {
25280                         this.doc.designMode="on";
25281                     } catch (e) {
25282                         return;
25283                     }
25284                     Roo.TaskMgr.stop(task);
25285                     this.initEditor.defer(10, this);
25286                 }
25287             },
25288             interval : 10,
25289             duration:10000,
25290             scope: this
25291         };
25292         Roo.TaskMgr.start(task);
25293
25294         if(!this.width){
25295             this.setSize(this.wrap.getSize());
25296         }
25297         if (this.resizeEl) {
25298             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25299             // should trigger onReize..
25300         }
25301     },
25302
25303     // private
25304     onResize : function(w, h)
25305     {
25306         //Roo.log('resize: ' +w + ',' + h );
25307         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
25308         if(this.el && this.iframe){
25309             if(typeof w == 'number'){
25310                 var aw = w - this.wrap.getFrameWidth('lr');
25311                 this.el.setWidth(this.adjustWidth('textarea', aw));
25312                 this.iframe.style.width = aw + 'px';
25313             }
25314             if(typeof h == 'number'){
25315                 var tbh = 0;
25316                 for (var i =0; i < this.toolbars.length;i++) {
25317                     // fixme - ask toolbars for heights?
25318                     tbh += this.toolbars[i].tb.el.getHeight();
25319                     if (this.toolbars[i].footer) {
25320                         tbh += this.toolbars[i].footer.el.getHeight();
25321                     }
25322                 }
25323                 
25324                 
25325                 
25326                 
25327                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25328                 ah -= 5; // knock a few pixes off for look..
25329                 this.el.setHeight(this.adjustWidth('textarea', ah));
25330                 this.iframe.style.height = ah + 'px';
25331                 if(this.doc){
25332                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
25333                 }
25334             }
25335         }
25336     },
25337
25338     /**
25339      * Toggles the editor between standard and source edit mode.
25340      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25341      */
25342     toggleSourceEdit : function(sourceEditMode){
25343         
25344         this.sourceEditMode = sourceEditMode === true;
25345         
25346         if(this.sourceEditMode){
25347           
25348             this.syncValue();
25349             this.iframe.className = 'x-hidden';
25350             this.el.removeClass('x-hidden');
25351             this.el.dom.removeAttribute('tabIndex');
25352             this.el.focus();
25353         }else{
25354              
25355             this.pushValue();
25356             this.iframe.className = '';
25357             this.el.addClass('x-hidden');
25358             this.el.dom.setAttribute('tabIndex', -1);
25359             this.deferFocus();
25360         }
25361         this.setSize(this.wrap.getSize());
25362         this.fireEvent('editmodechange', this, this.sourceEditMode);
25363     },
25364
25365     // private used internally
25366     createLink : function(){
25367         var url = prompt(this.createLinkText, this.defaultLinkValue);
25368         if(url && url != 'http:/'+'/'){
25369             this.relayCmd('createlink', url);
25370         }
25371     },
25372
25373     // private (for BoxComponent)
25374     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25375
25376     // private (for BoxComponent)
25377     getResizeEl : function(){
25378         return this.wrap;
25379     },
25380
25381     // private (for BoxComponent)
25382     getPositionEl : function(){
25383         return this.wrap;
25384     },
25385
25386     // private
25387     initEvents : function(){
25388         this.originalValue = this.getValue();
25389     },
25390
25391     /**
25392      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25393      * @method
25394      */
25395     markInvalid : Roo.emptyFn,
25396     /**
25397      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25398      * @method
25399      */
25400     clearInvalid : Roo.emptyFn,
25401
25402     setValue : function(v){
25403         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
25404         this.pushValue();
25405     },
25406
25407     /**
25408      * Protected method that will not generally be called directly. If you need/want
25409      * custom HTML cleanup, this is the method you should override.
25410      * @param {String} html The HTML to be cleaned
25411      * return {String} The cleaned HTML
25412      */
25413     cleanHtml : function(html){
25414         html = String(html);
25415         if(html.length > 5){
25416             if(Roo.isSafari){ // strip safari nonsense
25417                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25418             }
25419         }
25420         if(html == '&nbsp;'){
25421             html = '';
25422         }
25423         return html;
25424     },
25425
25426     /**
25427      * Protected method that will not generally be called directly. Syncs the contents
25428      * of the editor iframe with the textarea.
25429      */
25430     syncValue : function(){
25431         if(this.initialized){
25432             var bd = (this.doc.body || this.doc.documentElement);
25433             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25434             var html = bd.innerHTML;
25435             if(Roo.isSafari){
25436                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25437                 var m = bs.match(/text-align:(.*?);/i);
25438                 if(m && m[1]){
25439                     html = '<div style="'+m[0]+'">' + html + '</div>';
25440                 }
25441             }
25442             html = this.cleanHtml(html);
25443             // fix up the special chars..
25444             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
25445                 return "&#"+b.charCodeAt()+";" 
25446             });
25447             if(this.fireEvent('beforesync', this, html) !== false){
25448                 this.el.dom.value = html;
25449                 this.fireEvent('sync', this, html);
25450             }
25451         }
25452     },
25453
25454     /**
25455      * Protected method that will not generally be called directly. Pushes the value of the textarea
25456      * into the iframe editor.
25457      */
25458     pushValue : function(){
25459         if(this.initialized){
25460             var v = this.el.dom.value;
25461             if(v.length < 1){
25462                 v = '&#160;';
25463             }
25464             
25465             if(this.fireEvent('beforepush', this, v) !== false){
25466                 var d = (this.doc.body || this.doc.documentElement);
25467                 d.innerHTML = v;
25468                 this.cleanUpPaste();
25469                 this.el.dom.value = d.innerHTML;
25470                 this.fireEvent('push', this, v);
25471             }
25472         }
25473     },
25474
25475     // private
25476     deferFocus : function(){
25477         this.focus.defer(10, this);
25478     },
25479
25480     // doc'ed in Field
25481     focus : function(){
25482         if(this.win && !this.sourceEditMode){
25483             this.win.focus();
25484         }else{
25485             this.el.focus();
25486         }
25487     },
25488     
25489     assignDocWin: function()
25490     {
25491         var iframe = this.iframe;
25492         
25493          if(Roo.isIE){
25494             this.doc = iframe.contentWindow.document;
25495             this.win = iframe.contentWindow;
25496         } else {
25497             if (!Roo.get(this.frameId)) {
25498                 return;
25499             }
25500             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25501             this.win = Roo.get(this.frameId).dom.contentWindow;
25502         }
25503     },
25504     
25505     // private
25506     initEditor : function(){
25507         //console.log("INIT EDITOR");
25508         this.assignDocWin();
25509         
25510         
25511         
25512         this.doc.designMode="on";
25513         this.doc.open();
25514         this.doc.write(this.getDocMarkup());
25515         this.doc.close();
25516         
25517         var dbody = (this.doc.body || this.doc.documentElement);
25518         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25519         // this copies styles from the containing element into thsi one..
25520         // not sure why we need all of this..
25521         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25522         ss['background-attachment'] = 'fixed'; // w3c
25523         dbody.bgProperties = 'fixed'; // ie
25524         Roo.DomHelper.applyStyles(dbody, ss);
25525         Roo.EventManager.on(this.doc, {
25526             //'mousedown': this.onEditorEvent,
25527             'mouseup': this.onEditorEvent,
25528             'dblclick': this.onEditorEvent,
25529             'click': this.onEditorEvent,
25530             'keyup': this.onEditorEvent,
25531             buffer:100,
25532             scope: this
25533         });
25534         if(Roo.isGecko){
25535             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25536         }
25537         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25538             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25539         }
25540         this.initialized = true;
25541
25542         this.fireEvent('initialize', this);
25543         this.pushValue();
25544     },
25545
25546     // private
25547     onDestroy : function(){
25548         
25549         
25550         
25551         if(this.rendered){
25552             
25553             for (var i =0; i < this.toolbars.length;i++) {
25554                 // fixme - ask toolbars for heights?
25555                 this.toolbars[i].onDestroy();
25556             }
25557             
25558             this.wrap.dom.innerHTML = '';
25559             this.wrap.remove();
25560         }
25561     },
25562
25563     // private
25564     onFirstFocus : function(){
25565         
25566         this.assignDocWin();
25567         
25568         
25569         this.activated = true;
25570         for (var i =0; i < this.toolbars.length;i++) {
25571             this.toolbars[i].onFirstFocus();
25572         }
25573        
25574         if(Roo.isGecko){ // prevent silly gecko errors
25575             this.win.focus();
25576             var s = this.win.getSelection();
25577             if(!s.focusNode || s.focusNode.nodeType != 3){
25578                 var r = s.getRangeAt(0);
25579                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25580                 r.collapse(true);
25581                 this.deferFocus();
25582             }
25583             try{
25584                 this.execCmd('useCSS', true);
25585                 this.execCmd('styleWithCSS', false);
25586             }catch(e){}
25587         }
25588         this.fireEvent('activate', this);
25589     },
25590
25591     // private
25592     adjustFont: function(btn){
25593         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25594         //if(Roo.isSafari){ // safari
25595         //    adjust *= 2;
25596        // }
25597         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25598         if(Roo.isSafari){ // safari
25599             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25600             v =  (v < 10) ? 10 : v;
25601             v =  (v > 48) ? 48 : v;
25602             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25603             
25604         }
25605         
25606         
25607         v = Math.max(1, v+adjust);
25608         
25609         this.execCmd('FontSize', v  );
25610     },
25611
25612     onEditorEvent : function(e){
25613         this.fireEvent('editorevent', this, e);
25614       //  this.updateToolbar();
25615         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25616     },
25617
25618     insertTag : function(tg)
25619     {
25620         // could be a bit smarter... -> wrap the current selected tRoo..
25621         
25622         this.execCmd("formatblock",   tg);
25623         
25624     },
25625     
25626     insertText : function(txt)
25627     {
25628         
25629         
25630         range = this.createRange();
25631         range.deleteContents();
25632                //alert(Sender.getAttribute('label'));
25633                
25634         range.insertNode(this.doc.createTextNode(txt));
25635     } ,
25636     
25637     // private
25638     relayBtnCmd : function(btn){
25639         this.relayCmd(btn.cmd);
25640     },
25641
25642     /**
25643      * Executes a Midas editor command on the editor document and performs necessary focus and
25644      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25645      * @param {String} cmd The Midas command
25646      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25647      */
25648     relayCmd : function(cmd, value){
25649         this.win.focus();
25650         this.execCmd(cmd, value);
25651         this.fireEvent('editorevent', this);
25652         //this.updateToolbar();
25653         this.deferFocus();
25654     },
25655
25656     /**
25657      * Executes a Midas editor command directly on the editor document.
25658      * For visual commands, you should use {@link #relayCmd} instead.
25659      * <b>This should only be called after the editor is initialized.</b>
25660      * @param {String} cmd The Midas command
25661      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25662      */
25663     execCmd : function(cmd, value){
25664         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25665         this.syncValue();
25666     },
25667  
25668  
25669    
25670     /**
25671      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25672      * to insert tRoo.
25673      * @param {String} text | dom node.. 
25674      */
25675     insertAtCursor : function(text)
25676     {
25677         
25678         
25679         
25680         if(!this.activated){
25681             return;
25682         }
25683         /*
25684         if(Roo.isIE){
25685             this.win.focus();
25686             var r = this.doc.selection.createRange();
25687             if(r){
25688                 r.collapse(true);
25689                 r.pasteHTML(text);
25690                 this.syncValue();
25691                 this.deferFocus();
25692             
25693             }
25694             return;
25695         }
25696         */
25697         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25698             this.win.focus();
25699             
25700             
25701             // from jquery ui (MIT licenced)
25702             var range, node;
25703             var win = this.win;
25704             
25705             if (win.getSelection && win.getSelection().getRangeAt) {
25706                 range = win.getSelection().getRangeAt(0);
25707                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25708                 range.insertNode(node);
25709             } else if (win.document.selection && win.document.selection.createRange) {
25710                 // no firefox support
25711                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25712                 win.document.selection.createRange().pasteHTML(txt);
25713             } else {
25714                 // no firefox support
25715                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25716                 this.execCmd('InsertHTML', txt);
25717             } 
25718             
25719             this.syncValue();
25720             
25721             this.deferFocus();
25722         }
25723     },
25724  // private
25725     mozKeyPress : function(e){
25726         if(e.ctrlKey){
25727             var c = e.getCharCode(), cmd;
25728           
25729             if(c > 0){
25730                 c = String.fromCharCode(c).toLowerCase();
25731                 switch(c){
25732                     case 'b':
25733                         cmd = 'bold';
25734                         break;
25735                     case 'i':
25736                         cmd = 'italic';
25737                         break;
25738                     
25739                     case 'u':
25740                         cmd = 'underline';
25741                         break;
25742                     
25743                     case 'v':
25744                         this.cleanUpPaste.defer(100, this);
25745                         return;
25746                         
25747                 }
25748                 if(cmd){
25749                     this.win.focus();
25750                     this.execCmd(cmd);
25751                     this.deferFocus();
25752                     e.preventDefault();
25753                 }
25754                 
25755             }
25756         }
25757     },
25758
25759     // private
25760     fixKeys : function(){ // load time branching for fastest keydown performance
25761         if(Roo.isIE){
25762             return function(e){
25763                 var k = e.getKey(), r;
25764                 if(k == e.TAB){
25765                     e.stopEvent();
25766                     r = this.doc.selection.createRange();
25767                     if(r){
25768                         r.collapse(true);
25769                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25770                         this.deferFocus();
25771                     }
25772                     return;
25773                 }
25774                 
25775                 if(k == e.ENTER){
25776                     r = this.doc.selection.createRange();
25777                     if(r){
25778                         var target = r.parentElement();
25779                         if(!target || target.tagName.toLowerCase() != 'li'){
25780                             e.stopEvent();
25781                             r.pasteHTML('<br />');
25782                             r.collapse(false);
25783                             r.select();
25784                         }
25785                     }
25786                 }
25787                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25788                     this.cleanUpPaste.defer(100, this);
25789                     return;
25790                 }
25791                 
25792                 
25793             };
25794         }else if(Roo.isOpera){
25795             return function(e){
25796                 var k = e.getKey();
25797                 if(k == e.TAB){
25798                     e.stopEvent();
25799                     this.win.focus();
25800                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25801                     this.deferFocus();
25802                 }
25803                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25804                     this.cleanUpPaste.defer(100, this);
25805                     return;
25806                 }
25807                 
25808             };
25809         }else if(Roo.isSafari){
25810             return function(e){
25811                 var k = e.getKey();
25812                 
25813                 if(k == e.TAB){
25814                     e.stopEvent();
25815                     this.execCmd('InsertText','\t');
25816                     this.deferFocus();
25817                     return;
25818                 }
25819                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25820                     this.cleanUpPaste.defer(100, this);
25821                     return;
25822                 }
25823                 
25824              };
25825         }
25826     }(),
25827     
25828     getAllAncestors: function()
25829     {
25830         var p = this.getSelectedNode();
25831         var a = [];
25832         if (!p) {
25833             a.push(p); // push blank onto stack..
25834             p = this.getParentElement();
25835         }
25836         
25837         
25838         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25839             a.push(p);
25840             p = p.parentNode;
25841         }
25842         a.push(this.doc.body);
25843         return a;
25844     },
25845     lastSel : false,
25846     lastSelNode : false,
25847     
25848     
25849     getSelection : function() 
25850     {
25851         this.assignDocWin();
25852         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25853     },
25854     
25855     getSelectedNode: function() 
25856     {
25857         // this may only work on Gecko!!!
25858         
25859         // should we cache this!!!!
25860         
25861         
25862         
25863          
25864         var range = this.createRange(this.getSelection()).cloneRange();
25865         
25866         if (Roo.isIE) {
25867             var parent = range.parentElement();
25868             while (true) {
25869                 var testRange = range.duplicate();
25870                 testRange.moveToElementText(parent);
25871                 if (testRange.inRange(range)) {
25872                     break;
25873                 }
25874                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25875                     break;
25876                 }
25877                 parent = parent.parentElement;
25878             }
25879             return parent;
25880         }
25881         
25882         // is ancestor a text element.
25883         var ac =  range.commonAncestorContainer;
25884         if (ac.nodeType == 3) {
25885             ac = ac.parentNode;
25886         }
25887         
25888         var ar = ac.childNodes;
25889          
25890         var nodes = [];
25891         var other_nodes = [];
25892         var has_other_nodes = false;
25893         for (var i=0;i<ar.length;i++) {
25894             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25895                 continue;
25896             }
25897             // fullly contained node.
25898             
25899             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25900                 nodes.push(ar[i]);
25901                 continue;
25902             }
25903             
25904             // probably selected..
25905             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25906                 other_nodes.push(ar[i]);
25907                 continue;
25908             }
25909             // outer..
25910             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25911                 continue;
25912             }
25913             
25914             
25915             has_other_nodes = true;
25916         }
25917         if (!nodes.length && other_nodes.length) {
25918             nodes= other_nodes;
25919         }
25920         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25921             return false;
25922         }
25923         
25924         return nodes[0];
25925     },
25926     createRange: function(sel)
25927     {
25928         // this has strange effects when using with 
25929         // top toolbar - not sure if it's a great idea.
25930         //this.editor.contentWindow.focus();
25931         if (typeof sel != "undefined") {
25932             try {
25933                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25934             } catch(e) {
25935                 return this.doc.createRange();
25936             }
25937         } else {
25938             return this.doc.createRange();
25939         }
25940     },
25941     getParentElement: function()
25942     {
25943         
25944         this.assignDocWin();
25945         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25946         
25947         var range = this.createRange(sel);
25948          
25949         try {
25950             var p = range.commonAncestorContainer;
25951             while (p.nodeType == 3) { // text node
25952                 p = p.parentNode;
25953             }
25954             return p;
25955         } catch (e) {
25956             return null;
25957         }
25958     
25959     },
25960     /***
25961      *
25962      * Range intersection.. the hard stuff...
25963      *  '-1' = before
25964      *  '0' = hits..
25965      *  '1' = after.
25966      *         [ -- selected range --- ]
25967      *   [fail]                        [fail]
25968      *
25969      *    basically..
25970      *      if end is before start or  hits it. fail.
25971      *      if start is after end or hits it fail.
25972      *
25973      *   if either hits (but other is outside. - then it's not 
25974      *   
25975      *    
25976      **/
25977     
25978     
25979     // @see http://www.thismuchiknow.co.uk/?p=64.
25980     rangeIntersectsNode : function(range, node)
25981     {
25982         var nodeRange = node.ownerDocument.createRange();
25983         try {
25984             nodeRange.selectNode(node);
25985         } catch (e) {
25986             nodeRange.selectNodeContents(node);
25987         }
25988     
25989         var rangeStartRange = range.cloneRange();
25990         rangeStartRange.collapse(true);
25991     
25992         var rangeEndRange = range.cloneRange();
25993         rangeEndRange.collapse(false);
25994     
25995         var nodeStartRange = nodeRange.cloneRange();
25996         nodeStartRange.collapse(true);
25997     
25998         var nodeEndRange = nodeRange.cloneRange();
25999         nodeEndRange.collapse(false);
26000     
26001         return rangeStartRange.compareBoundaryPoints(
26002                  Range.START_TO_START, nodeEndRange) == -1 &&
26003                rangeEndRange.compareBoundaryPoints(
26004                  Range.START_TO_START, nodeStartRange) == 1;
26005         
26006          
26007     },
26008     rangeCompareNode : function(range, node)
26009     {
26010         var nodeRange = node.ownerDocument.createRange();
26011         try {
26012             nodeRange.selectNode(node);
26013         } catch (e) {
26014             nodeRange.selectNodeContents(node);
26015         }
26016         
26017         
26018         range.collapse(true);
26019     
26020         nodeRange.collapse(true);
26021      
26022         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26023         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26024          
26025         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26026         
26027         var nodeIsBefore   =  ss == 1;
26028         var nodeIsAfter    = ee == -1;
26029         
26030         if (nodeIsBefore && nodeIsAfter)
26031             return 0; // outer
26032         if (!nodeIsBefore && nodeIsAfter)
26033             return 1; //right trailed.
26034         
26035         if (nodeIsBefore && !nodeIsAfter)
26036             return 2;  // left trailed.
26037         // fully contined.
26038         return 3;
26039     },
26040
26041     // private? - in a new class?
26042     cleanUpPaste :  function()
26043     {
26044         // cleans up the whole document..
26045          Roo.log('cleanuppaste');
26046         this.cleanUpChildren(this.doc.body);
26047         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26048         if (clean != this.doc.body.innerHTML) {
26049             this.doc.body.innerHTML = clean;
26050         }
26051         
26052     },
26053     
26054     cleanWordChars : function(input) {
26055         var he = Roo.form.HtmlEditor;
26056     
26057         var output = input;
26058         Roo.each(he.swapCodes, function(sw) { 
26059         
26060             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26061             output = output.replace(swapper, sw[1]);
26062         });
26063         return output;
26064     },
26065     
26066     
26067     cleanUpChildren : function (n)
26068     {
26069         if (!n.childNodes.length) {
26070             return;
26071         }
26072         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26073            this.cleanUpChild(n.childNodes[i]);
26074         }
26075     },
26076     
26077     
26078         
26079     
26080     cleanUpChild : function (node)
26081     {
26082         //console.log(node);
26083         if (node.nodeName == "#text") {
26084             // clean up silly Windows -- stuff?
26085             return; 
26086         }
26087         if (node.nodeName == "#comment") {
26088             node.parentNode.removeChild(node);
26089             // clean up silly Windows -- stuff?
26090             return; 
26091         }
26092         
26093         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
26094             // remove node.
26095             node.parentNode.removeChild(node);
26096             return;
26097             
26098         }
26099         
26100         var remove_keep_children= Roo.form.HtmlEditor.remove.indexOf(node.tagName.toLowerCase()) > -1;
26101         
26102         // remove <a name=....> as rendering on yahoo mailer is bored with this.
26103         
26104         if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26105             remove_keep_children = true;
26106         }
26107         
26108         if (remove_keep_children) {
26109             this.cleanUpChildren(node);
26110             // inserts everything just before this node...
26111             while (node.childNodes.length) {
26112                 var cn = node.childNodes[0];
26113                 node.removeChild(cn);
26114                 node.parentNode.insertBefore(cn, node);
26115             }
26116             node.parentNode.removeChild(node);
26117             return;
26118         }
26119         
26120         if (!node.attributes || !node.attributes.length) {
26121             this.cleanUpChildren(node);
26122             return;
26123         }
26124         
26125         function cleanAttr(n,v)
26126         {
26127             
26128             if (v.match(/^\./) || v.match(/^\//)) {
26129                 return;
26130             }
26131             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
26132                 return;
26133             }
26134             if (v.match(/^#/)) {
26135                 return;
26136             }
26137             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
26138             node.removeAttribute(n);
26139             
26140         }
26141         
26142         function cleanStyle(n,v)
26143         {
26144             if (v.match(/expression/)) { //XSS?? should we even bother..
26145                 node.removeAttribute(n);
26146                 return;
26147             }
26148             
26149             
26150             var parts = v.split(/;/);
26151             Roo.each(parts, function(p) {
26152                 p = p.replace(/\s+/g,'');
26153                 if (!p.length) {
26154                     return true;
26155                 }
26156                 var l = p.split(':').shift().replace(/\s+/g,'');
26157                 
26158                 // only allow 'c whitelisted system attributes'
26159                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
26160                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
26161                     node.removeAttribute(n);
26162                     return false;
26163                 }
26164                 return true;
26165             });
26166             
26167             
26168         }
26169         
26170         
26171         for (var i = node.attributes.length-1; i > -1 ; i--) {
26172             var a = node.attributes[i];
26173             //console.log(a);
26174             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
26175                 node.removeAttribute(a.name);
26176                 continue;
26177             }
26178             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
26179                 cleanAttr(a.name,a.value); // fixme..
26180                 continue;
26181             }
26182             if (a.name == 'style') {
26183                 cleanStyle(a.name,a.value);
26184                 continue;
26185             }
26186             /// clean up MS crap..
26187             // tecnically this should be a list of valid class'es..
26188             
26189             
26190             if (a.name == 'class') {
26191                 if (a.value.match(/^Mso/)) {
26192                     node.className = '';
26193                 }
26194                 
26195                 if (a.value.match(/body/)) {
26196                     node.className = '';
26197                 }
26198                 continue;
26199             }
26200             
26201             // style cleanup!?
26202             // class cleanup?
26203             
26204         }
26205         
26206         
26207         this.cleanUpChildren(node);
26208         
26209         
26210     }
26211     
26212     
26213     // hide stuff that is not compatible
26214     /**
26215      * @event blur
26216      * @hide
26217      */
26218     /**
26219      * @event change
26220      * @hide
26221      */
26222     /**
26223      * @event focus
26224      * @hide
26225      */
26226     /**
26227      * @event specialkey
26228      * @hide
26229      */
26230     /**
26231      * @cfg {String} fieldClass @hide
26232      */
26233     /**
26234      * @cfg {String} focusClass @hide
26235      */
26236     /**
26237      * @cfg {String} autoCreate @hide
26238      */
26239     /**
26240      * @cfg {String} inputType @hide
26241      */
26242     /**
26243      * @cfg {String} invalidClass @hide
26244      */
26245     /**
26246      * @cfg {String} invalidText @hide
26247      */
26248     /**
26249      * @cfg {String} msgFx @hide
26250      */
26251     /**
26252      * @cfg {String} validateOnBlur @hide
26253      */
26254 });
26255
26256 Roo.form.HtmlEditor.white = [
26257         'area', 'br', 'img', 'input', 'hr', 'wbr',
26258         
26259        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26260        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26261        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26262        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26263        'table',   'ul',         'xmp', 
26264        
26265        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26266       'thead',   'tr', 
26267      
26268       'dir', 'menu', 'ol', 'ul', 'dl',
26269        
26270       'embed',  'object'
26271 ];
26272
26273
26274 Roo.form.HtmlEditor.black = [
26275     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26276         'applet', // 
26277         'base',   'basefont', 'bgsound', 'blink',  'body', 
26278         'frame',  'frameset', 'head',    'html',   'ilayer', 
26279         'iframe', 'layer',  'link',     'meta',    'object',   
26280         'script', 'style' ,'title',  'xml' // clean later..
26281 ];
26282 Roo.form.HtmlEditor.clean = [
26283     'script', 'style', 'title', 'xml'
26284 ];
26285 Roo.form.HtmlEditor.remove = [
26286     'font'
26287 ];
26288 // attributes..
26289
26290 Roo.form.HtmlEditor.ablack = [
26291     'on'
26292 ];
26293     
26294 Roo.form.HtmlEditor.aclean = [ 
26295     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26296 ];
26297
26298 // protocols..
26299 Roo.form.HtmlEditor.pwhite= [
26300         'http',  'https',  'mailto'
26301 ];
26302
26303 // white listed style attributes.
26304 Roo.form.HtmlEditor.cwhite= [
26305         'text-align',
26306         'font-size'
26307 ];
26308
26309
26310 Roo.form.HtmlEditor.swapCodes   =[ 
26311     [    8211, "--" ], 
26312     [    8212, "--" ], 
26313     [    8216,  "'" ],  
26314     [    8217, "'" ],  
26315     [    8220, '"' ],  
26316     [    8221, '"' ],  
26317     [    8226, "*" ],  
26318     [    8230, "..." ]
26319 ]; 
26320
26321     // <script type="text/javascript">
26322 /*
26323  * Based on
26324  * Ext JS Library 1.1.1
26325  * Copyright(c) 2006-2007, Ext JS, LLC.
26326  *  
26327  
26328  */
26329
26330 /**
26331  * @class Roo.form.HtmlEditorToolbar1
26332  * Basic Toolbar
26333  * 
26334  * Usage:
26335  *
26336  new Roo.form.HtmlEditor({
26337     ....
26338     toolbars : [
26339         new Roo.form.HtmlEditorToolbar1({
26340             disable : { fonts: 1 , format: 1, ..., ... , ...],
26341             btns : [ .... ]
26342         })
26343     }
26344      
26345  * 
26346  * @cfg {Object} disable List of elements to disable..
26347  * @cfg {Array} btns List of additional buttons.
26348  * 
26349  * 
26350  * NEEDS Extra CSS? 
26351  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26352  */
26353  
26354 Roo.form.HtmlEditor.ToolbarStandard = function(config)
26355 {
26356     
26357     Roo.apply(this, config);
26358     
26359     // default disabled, based on 'good practice'..
26360     this.disable = this.disable || {};
26361     Roo.applyIf(this.disable, {
26362         fontSize : true,
26363         colors : true,
26364         specialElements : true
26365     });
26366     
26367     
26368     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26369     // dont call parent... till later.
26370 }
26371
26372 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
26373     
26374     tb: false,
26375     
26376     rendered: false,
26377     
26378     editor : false,
26379     /**
26380      * @cfg {Object} disable  List of toolbar elements to disable
26381          
26382      */
26383     disable : false,
26384       /**
26385      * @cfg {Array} fontFamilies An array of available font families
26386      */
26387     fontFamilies : [
26388         'Arial',
26389         'Courier New',
26390         'Tahoma',
26391         'Times New Roman',
26392         'Verdana'
26393     ],
26394     
26395     specialChars : [
26396            "&#169;",
26397           "&#174;",     
26398           "&#8482;",    
26399           "&#163;" ,    
26400          // "&#8212;",    
26401           "&#8230;",    
26402           "&#247;" ,    
26403         //  "&#225;" ,     ?? a acute?
26404            "&#8364;"    , //Euro
26405        //   "&#8220;"    ,
26406         //  "&#8221;"    ,
26407         //  "&#8226;"    ,
26408           "&#176;"  //   , // degrees
26409
26410          // "&#233;"     , // e ecute
26411          // "&#250;"     , // u ecute?
26412     ],
26413     
26414     specialElements : [
26415         {
26416             text: "Insert Table",
26417             xtype: 'MenuItem',
26418             xns : Roo.Menu,
26419             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
26420                 
26421         },
26422         {    
26423             text: "Insert Image",
26424             xtype: 'MenuItem',
26425             xns : Roo.Menu,
26426             ihtml : '<img src="about:blank"/>'
26427             
26428         }
26429         
26430          
26431     ],
26432     
26433     
26434     inputElements : [ 
26435             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
26436             "input:submit", "input:button", "select", "textarea", "label" ],
26437     formats : [
26438         ["p"] ,  
26439         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
26440         ["pre"],[ "code"], 
26441         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
26442     ],
26443      /**
26444      * @cfg {String} defaultFont default font to use.
26445      */
26446     defaultFont: 'tahoma',
26447    
26448     fontSelect : false,
26449     
26450     
26451     formatCombo : false,
26452     
26453     init : function(editor)
26454     {
26455         this.editor = editor;
26456         
26457         
26458         var fid = editor.frameId;
26459         var etb = this;
26460         function btn(id, toggle, handler){
26461             var xid = fid + '-'+ id ;
26462             return {
26463                 id : xid,
26464                 cmd : id,
26465                 cls : 'x-btn-icon x-edit-'+id,
26466                 enableToggle:toggle !== false,
26467                 scope: editor, // was editor...
26468                 handler:handler||editor.relayBtnCmd,
26469                 clickEvent:'mousedown',
26470                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
26471                 tabIndex:-1
26472             };
26473         }
26474         
26475         
26476         
26477         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
26478         this.tb = tb;
26479          // stop form submits
26480         tb.el.on('click', function(e){
26481             e.preventDefault(); // what does this do?
26482         });
26483
26484         if(!this.disable.font && !Roo.isSafari){
26485             /* why no safari for fonts
26486             editor.fontSelect = tb.el.createChild({
26487                 tag:'select',
26488                 tabIndex: -1,
26489                 cls:'x-font-select',
26490                 html: editor.createFontOptions()
26491             });
26492             editor.fontSelect.on('change', function(){
26493                 var font = editor.fontSelect.dom.value;
26494                 editor.relayCmd('fontname', font);
26495                 editor.deferFocus();
26496             }, editor);
26497             tb.add(
26498                 editor.fontSelect.dom,
26499                 '-'
26500             );
26501             */
26502         };
26503         if(!this.disable.formats){
26504             this.formatCombo = new Roo.form.ComboBox({
26505                 store: new Roo.data.SimpleStore({
26506                     id : 'tag',
26507                     fields: ['tag'],
26508                     data : this.formats // from states.js
26509                 }),
26510                 blockFocus : true,
26511                 //autoCreate : {tag: "div",  size: "20"},
26512                 displayField:'tag',
26513                 typeAhead: false,
26514                 mode: 'local',
26515                 editable : false,
26516                 triggerAction: 'all',
26517                 emptyText:'Add tag',
26518                 selectOnFocus:true,
26519                 width:135,
26520                 listeners : {
26521                     'select': function(c, r, i) {
26522                         editor.insertTag(r.get('tag'));
26523                         editor.focus();
26524                     }
26525                 }
26526
26527             });
26528             tb.addField(this.formatCombo);
26529             
26530         }
26531         
26532         if(!this.disable.format){
26533             tb.add(
26534                 btn('bold'),
26535                 btn('italic'),
26536                 btn('underline')
26537             );
26538         };
26539         if(!this.disable.fontSize){
26540             tb.add(
26541                 '-',
26542                 
26543                 
26544                 btn('increasefontsize', false, editor.adjustFont),
26545                 btn('decreasefontsize', false, editor.adjustFont)
26546             );
26547         };
26548         
26549         
26550         if(!this.disable.colors){
26551             tb.add(
26552                 '-', {
26553                     id:editor.frameId +'-forecolor',
26554                     cls:'x-btn-icon x-edit-forecolor',
26555                     clickEvent:'mousedown',
26556                     tooltip: this.buttonTips['forecolor'] || undefined,
26557                     tabIndex:-1,
26558                     menu : new Roo.menu.ColorMenu({
26559                         allowReselect: true,
26560                         focus: Roo.emptyFn,
26561                         value:'000000',
26562                         plain:true,
26563                         selectHandler: function(cp, color){
26564                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
26565                             editor.deferFocus();
26566                         },
26567                         scope: editor,
26568                         clickEvent:'mousedown'
26569                     })
26570                 }, {
26571                     id:editor.frameId +'backcolor',
26572                     cls:'x-btn-icon x-edit-backcolor',
26573                     clickEvent:'mousedown',
26574                     tooltip: this.buttonTips['backcolor'] || undefined,
26575                     tabIndex:-1,
26576                     menu : new Roo.menu.ColorMenu({
26577                         focus: Roo.emptyFn,
26578                         value:'FFFFFF',
26579                         plain:true,
26580                         allowReselect: true,
26581                         selectHandler: function(cp, color){
26582                             if(Roo.isGecko){
26583                                 editor.execCmd('useCSS', false);
26584                                 editor.execCmd('hilitecolor', color);
26585                                 editor.execCmd('useCSS', true);
26586                                 editor.deferFocus();
26587                             }else{
26588                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
26589                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
26590                                 editor.deferFocus();
26591                             }
26592                         },
26593                         scope:editor,
26594                         clickEvent:'mousedown'
26595                     })
26596                 }
26597             );
26598         };
26599         // now add all the items...
26600         
26601
26602         if(!this.disable.alignments){
26603             tb.add(
26604                 '-',
26605                 btn('justifyleft'),
26606                 btn('justifycenter'),
26607                 btn('justifyright')
26608             );
26609         };
26610
26611         //if(!Roo.isSafari){
26612             if(!this.disable.links){
26613                 tb.add(
26614                     '-',
26615                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
26616                 );
26617             };
26618
26619             if(!this.disable.lists){
26620                 tb.add(
26621                     '-',
26622                     btn('insertorderedlist'),
26623                     btn('insertunorderedlist')
26624                 );
26625             }
26626             if(!this.disable.sourceEdit){
26627                 tb.add(
26628                     '-',
26629                     btn('sourceedit', true, function(btn){
26630                         this.toggleSourceEdit(btn.pressed);
26631                     })
26632                 );
26633             }
26634         //}
26635         
26636         var smenu = { };
26637         // special menu.. - needs to be tidied up..
26638         if (!this.disable.special) {
26639             smenu = {
26640                 text: "&#169;",
26641                 cls: 'x-edit-none',
26642                 
26643                 menu : {
26644                     items : []
26645                 }
26646             };
26647             for (var i =0; i < this.specialChars.length; i++) {
26648                 smenu.menu.items.push({
26649                     
26650                     html: this.specialChars[i],
26651                     handler: function(a,b) {
26652                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
26653                         //editor.insertAtCursor(a.html);
26654                         
26655                     },
26656                     tabIndex:-1
26657                 });
26658             }
26659             
26660             
26661             tb.add(smenu);
26662             
26663             
26664         }
26665          
26666         if (!this.disable.specialElements) {
26667             var semenu = {
26668                 text: "Other;",
26669                 cls: 'x-edit-none',
26670                 menu : {
26671                     items : []
26672                 }
26673             };
26674             for (var i =0; i < this.specialElements.length; i++) {
26675                 semenu.menu.items.push(
26676                     Roo.apply({ 
26677                         handler: function(a,b) {
26678                             editor.insertAtCursor(this.ihtml);
26679                         }
26680                     }, this.specialElements[i])
26681                 );
26682                     
26683             }
26684             
26685             tb.add(semenu);
26686             
26687             
26688         }
26689          
26690         
26691         if (this.btns) {
26692             for(var i =0; i< this.btns.length;i++) {
26693                 var b = Roo.factory(this.btns[i],Roo.form);
26694                 b.cls =  'x-edit-none';
26695                 b.scope = editor;
26696                 tb.add(b);
26697             }
26698         
26699         }
26700         
26701         
26702         
26703         // disable everything...
26704         
26705         this.tb.items.each(function(item){
26706            if(item.id != editor.frameId+ '-sourceedit'){
26707                 item.disable();
26708             }
26709         });
26710         this.rendered = true;
26711         
26712         // the all the btns;
26713         editor.on('editorevent', this.updateToolbar, this);
26714         // other toolbars need to implement this..
26715         //editor.on('editmodechange', this.updateToolbar, this);
26716     },
26717     
26718     
26719     
26720     /**
26721      * Protected method that will not generally be called directly. It triggers
26722      * a toolbar update by reading the markup state of the current selection in the editor.
26723      */
26724     updateToolbar: function(){
26725
26726         if(!this.editor.activated){
26727             this.editor.onFirstFocus();
26728             return;
26729         }
26730
26731         var btns = this.tb.items.map, 
26732             doc = this.editor.doc,
26733             frameId = this.editor.frameId;
26734
26735         if(!this.disable.font && !Roo.isSafari){
26736             /*
26737             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
26738             if(name != this.fontSelect.dom.value){
26739                 this.fontSelect.dom.value = name;
26740             }
26741             */
26742         }
26743         if(!this.disable.format){
26744             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
26745             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
26746             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
26747         }
26748         if(!this.disable.alignments){
26749             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
26750             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
26751             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
26752         }
26753         if(!Roo.isSafari && !this.disable.lists){
26754             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
26755             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
26756         }
26757         
26758         var ans = this.editor.getAllAncestors();
26759         if (this.formatCombo) {
26760             
26761             
26762             var store = this.formatCombo.store;
26763             this.formatCombo.setValue("");
26764             for (var i =0; i < ans.length;i++) {
26765                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26766                     // select it..
26767                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26768                     break;
26769                 }
26770             }
26771         }
26772         
26773         
26774         
26775         // hides menus... - so this cant be on a menu...
26776         Roo.menu.MenuMgr.hideAll();
26777
26778         //this.editorsyncValue();
26779     },
26780    
26781     
26782     createFontOptions : function(){
26783         var buf = [], fs = this.fontFamilies, ff, lc;
26784         for(var i = 0, len = fs.length; i< len; i++){
26785             ff = fs[i];
26786             lc = ff.toLowerCase();
26787             buf.push(
26788                 '<option value="',lc,'" style="font-family:',ff,';"',
26789                     (this.defaultFont == lc ? ' selected="true">' : '>'),
26790                     ff,
26791                 '</option>'
26792             );
26793         }
26794         return buf.join('');
26795     },
26796     
26797     toggleSourceEdit : function(sourceEditMode){
26798         if(sourceEditMode === undefined){
26799             sourceEditMode = !this.sourceEditMode;
26800         }
26801         this.sourceEditMode = sourceEditMode === true;
26802         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
26803         // just toggle the button?
26804         if(btn.pressed !== this.editor.sourceEditMode){
26805             btn.toggle(this.editor.sourceEditMode);
26806             return;
26807         }
26808         
26809         if(this.sourceEditMode){
26810             this.tb.items.each(function(item){
26811                 if(item.cmd != 'sourceedit'){
26812                     item.disable();
26813                 }
26814             });
26815           
26816         }else{
26817             if(this.initialized){
26818                 this.tb.items.each(function(item){
26819                     item.enable();
26820                 });
26821             }
26822             
26823         }
26824         // tell the editor that it's been pressed..
26825         this.editor.toggleSourceEdit(sourceEditMode);
26826        
26827     },
26828      /**
26829      * Object collection of toolbar tooltips for the buttons in the editor. The key
26830      * is the command id associated with that button and the value is a valid QuickTips object.
26831      * For example:
26832 <pre><code>
26833 {
26834     bold : {
26835         title: 'Bold (Ctrl+B)',
26836         text: 'Make the selected text bold.',
26837         cls: 'x-html-editor-tip'
26838     },
26839     italic : {
26840         title: 'Italic (Ctrl+I)',
26841         text: 'Make the selected text italic.',
26842         cls: 'x-html-editor-tip'
26843     },
26844     ...
26845 </code></pre>
26846     * @type Object
26847      */
26848     buttonTips : {
26849         bold : {
26850             title: 'Bold (Ctrl+B)',
26851             text: 'Make the selected text bold.',
26852             cls: 'x-html-editor-tip'
26853         },
26854         italic : {
26855             title: 'Italic (Ctrl+I)',
26856             text: 'Make the selected text italic.',
26857             cls: 'x-html-editor-tip'
26858         },
26859         underline : {
26860             title: 'Underline (Ctrl+U)',
26861             text: 'Underline the selected text.',
26862             cls: 'x-html-editor-tip'
26863         },
26864         increasefontsize : {
26865             title: 'Grow Text',
26866             text: 'Increase the font size.',
26867             cls: 'x-html-editor-tip'
26868         },
26869         decreasefontsize : {
26870             title: 'Shrink Text',
26871             text: 'Decrease the font size.',
26872             cls: 'x-html-editor-tip'
26873         },
26874         backcolor : {
26875             title: 'Text Highlight Color',
26876             text: 'Change the background color of the selected text.',
26877             cls: 'x-html-editor-tip'
26878         },
26879         forecolor : {
26880             title: 'Font Color',
26881             text: 'Change the color of the selected text.',
26882             cls: 'x-html-editor-tip'
26883         },
26884         justifyleft : {
26885             title: 'Align Text Left',
26886             text: 'Align text to the left.',
26887             cls: 'x-html-editor-tip'
26888         },
26889         justifycenter : {
26890             title: 'Center Text',
26891             text: 'Center text in the editor.',
26892             cls: 'x-html-editor-tip'
26893         },
26894         justifyright : {
26895             title: 'Align Text Right',
26896             text: 'Align text to the right.',
26897             cls: 'x-html-editor-tip'
26898         },
26899         insertunorderedlist : {
26900             title: 'Bullet List',
26901             text: 'Start a bulleted list.',
26902             cls: 'x-html-editor-tip'
26903         },
26904         insertorderedlist : {
26905             title: 'Numbered List',
26906             text: 'Start a numbered list.',
26907             cls: 'x-html-editor-tip'
26908         },
26909         createlink : {
26910             title: 'Hyperlink',
26911             text: 'Make the selected text a hyperlink.',
26912             cls: 'x-html-editor-tip'
26913         },
26914         sourceedit : {
26915             title: 'Source Edit',
26916             text: 'Switch to source editing mode.',
26917             cls: 'x-html-editor-tip'
26918         }
26919     },
26920     // private
26921     onDestroy : function(){
26922         if(this.rendered){
26923             
26924             this.tb.items.each(function(item){
26925                 if(item.menu){
26926                     item.menu.removeAll();
26927                     if(item.menu.el){
26928                         item.menu.el.destroy();
26929                     }
26930                 }
26931                 item.destroy();
26932             });
26933              
26934         }
26935     },
26936     onFirstFocus: function() {
26937         this.tb.items.each(function(item){
26938            item.enable();
26939         });
26940     }
26941 });
26942
26943
26944
26945
26946 // <script type="text/javascript">
26947 /*
26948  * Based on
26949  * Ext JS Library 1.1.1
26950  * Copyright(c) 2006-2007, Ext JS, LLC.
26951  *  
26952  
26953  */
26954
26955  
26956 /**
26957  * @class Roo.form.HtmlEditor.ToolbarContext
26958  * Context Toolbar
26959  * 
26960  * Usage:
26961  *
26962  new Roo.form.HtmlEditor({
26963     ....
26964     toolbars : [
26965         { xtype: 'ToolbarStandard', styles : {} }
26966         { xtype: 'ToolbarContext', disable : {} }
26967     ]
26968 })
26969
26970      
26971  * 
26972  * @config : {Object} disable List of elements to disable.. (not done yet.)
26973  * @config : {Object} styles  Map of styles available.
26974  * 
26975  */
26976
26977 Roo.form.HtmlEditor.ToolbarContext = function(config)
26978 {
26979     
26980     Roo.apply(this, config);
26981     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26982     // dont call parent... till later.
26983     this.styles = this.styles || {};
26984 }
26985 Roo.form.HtmlEditor.ToolbarContext.types = {
26986     'IMG' : {
26987         width : {
26988             title: "Width",
26989             width: 40
26990         },
26991         height:  {
26992             title: "Height",
26993             width: 40
26994         },
26995         align: {
26996             title: "Align",
26997             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
26998             width : 80
26999             
27000         },
27001         border: {
27002             title: "Border",
27003             width: 40
27004         },
27005         alt: {
27006             title: "Alt",
27007             width: 120
27008         },
27009         src : {
27010             title: "Src",
27011             width: 220
27012         }
27013         
27014     },
27015     'A' : {
27016         name : {
27017             title: "Name",
27018             width: 50
27019         },
27020         href:  {
27021             title: "Href",
27022             width: 220
27023         } // border?
27024         
27025     },
27026     'TABLE' : {
27027         rows : {
27028             title: "Rows",
27029             width: 20
27030         },
27031         cols : {
27032             title: "Cols",
27033             width: 20
27034         },
27035         width : {
27036             title: "Width",
27037             width: 40
27038         },
27039         height : {
27040             title: "Height",
27041             width: 40
27042         },
27043         border : {
27044             title: "Border",
27045             width: 20
27046         }
27047     },
27048     'TD' : {
27049         width : {
27050             title: "Width",
27051             width: 40
27052         },
27053         height : {
27054             title: "Height",
27055             width: 40
27056         },   
27057         align: {
27058             title: "Align",
27059             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
27060             width: 80
27061         },
27062         valign: {
27063             title: "Valign",
27064             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
27065             width: 80
27066         },
27067         colspan: {
27068             title: "Colspan",
27069             width: 20
27070             
27071         }
27072     },
27073     'INPUT' : {
27074         name : {
27075             title: "name",
27076             width: 120
27077         },
27078         value : {
27079             title: "Value",
27080             width: 120
27081         },
27082         width : {
27083             title: "Width",
27084             width: 40
27085         }
27086     },
27087     'LABEL' : {
27088         'for' : {
27089             title: "For",
27090             width: 120
27091         }
27092     },
27093     'TEXTAREA' : {
27094           name : {
27095             title: "name",
27096             width: 120
27097         },
27098         rows : {
27099             title: "Rows",
27100             width: 20
27101         },
27102         cols : {
27103             title: "Cols",
27104             width: 20
27105         }
27106     },
27107     'SELECT' : {
27108         name : {
27109             title: "name",
27110             width: 120
27111         },
27112         selectoptions : {
27113             title: "Options",
27114             width: 200
27115         }
27116     },
27117     
27118     // should we really allow this??
27119     // should this just be 
27120     'BODY' : {
27121         title : {
27122             title: "title",
27123             width: 200,
27124             disabled : true
27125         }
27126     },
27127     '*' : {
27128         // empty..
27129     }
27130 };
27131
27132
27133
27134 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
27135     
27136     tb: false,
27137     
27138     rendered: false,
27139     
27140     editor : false,
27141     /**
27142      * @cfg {Object} disable  List of toolbar elements to disable
27143          
27144      */
27145     disable : false,
27146     /**
27147      * @cfg {Object} styles List of styles 
27148      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
27149      *
27150      * These must be defined in the page, so they get rendered correctly..
27151      * .headline { }
27152      * TD.underline { }
27153      * 
27154      */
27155     styles : false,
27156     
27157     
27158     
27159     toolbars : false,
27160     
27161     init : function(editor)
27162     {
27163         this.editor = editor;
27164         
27165         
27166         var fid = editor.frameId;
27167         var etb = this;
27168         function btn(id, toggle, handler){
27169             var xid = fid + '-'+ id ;
27170             return {
27171                 id : xid,
27172                 cmd : id,
27173                 cls : 'x-btn-icon x-edit-'+id,
27174                 enableToggle:toggle !== false,
27175                 scope: editor, // was editor...
27176                 handler:handler||editor.relayBtnCmd,
27177                 clickEvent:'mousedown',
27178                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27179                 tabIndex:-1
27180             };
27181         }
27182         // create a new element.
27183         var wdiv = editor.wrap.createChild({
27184                 tag: 'div'
27185             }, editor.wrap.dom.firstChild.nextSibling, true);
27186         
27187         // can we do this more than once??
27188         
27189          // stop form submits
27190       
27191  
27192         // disable everything...
27193         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27194         this.toolbars = {};
27195            
27196         for (var i in  ty) {
27197           
27198             this.toolbars[i] = this.buildToolbar(ty[i],i);
27199         }
27200         this.tb = this.toolbars.BODY;
27201         this.tb.el.show();
27202         this.buildFooter();
27203         this.footer.show();
27204         editor.on('hide', function( ) { this.footer.hide() }, this);
27205         editor.on('show', function( ) { this.footer.show() }, this);
27206         
27207          
27208         this.rendered = true;
27209         
27210         // the all the btns;
27211         editor.on('editorevent', this.updateToolbar, this);
27212         // other toolbars need to implement this..
27213         //editor.on('editmodechange', this.updateToolbar, this);
27214     },
27215     
27216     
27217     
27218     /**
27219      * Protected method that will not generally be called directly. It triggers
27220      * a toolbar update by reading the markup state of the current selection in the editor.
27221      */
27222     updateToolbar: function(editor,ev,sel){
27223
27224         //Roo.log(ev);
27225         // capture mouse up - this is handy for selecting images..
27226         // perhaps should go somewhere else...
27227         if(!this.editor.activated){
27228              this.editor.onFirstFocus();
27229             return;
27230         }
27231         
27232         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
27233         // selectNode - might want to handle IE?
27234         if (ev &&
27235             (ev.type == 'mouseup' || ev.type == 'click' ) &&
27236             ev.target && ev.target.tagName == 'IMG') {
27237             // they have click on an image...
27238             // let's see if we can change the selection...
27239             sel = ev.target;
27240          
27241               var nodeRange = sel.ownerDocument.createRange();
27242             try {
27243                 nodeRange.selectNode(sel);
27244             } catch (e) {
27245                 nodeRange.selectNodeContents(sel);
27246             }
27247             //nodeRange.collapse(true);
27248             var s = editor.win.getSelection();
27249             s.removeAllRanges();
27250             s.addRange(nodeRange);
27251         }  
27252         
27253       
27254         var updateFooter = sel ? false : true;
27255         
27256         
27257         var ans = this.editor.getAllAncestors();
27258         
27259         // pick
27260         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
27261         
27262         if (!sel) { 
27263             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
27264             sel = sel ? sel : this.editor.doc.body;
27265             sel = sel.tagName.length ? sel : this.editor.doc.body;
27266             
27267         }
27268         // pick a menu that exists..
27269         var tn = sel.tagName.toUpperCase();
27270         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
27271         
27272         tn = sel.tagName.toUpperCase();
27273         
27274         var lastSel = this.tb.selectedNode
27275         
27276         this.tb.selectedNode = sel;
27277         
27278         // if current menu does not match..
27279         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
27280                 
27281             this.tb.el.hide();
27282             ///console.log("show: " + tn);
27283             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
27284             this.tb.el.show();
27285             // update name
27286             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
27287             
27288             
27289             // update attributes
27290             if (this.tb.fields) {
27291                 this.tb.fields.each(function(e) {
27292                    e.setValue(sel.getAttribute(e.attrname));
27293                 });
27294             }
27295             
27296             var hasStyles = false;
27297             for(var i in this.styles) {
27298                 hasStyles = true;
27299                 break;
27300             }
27301             
27302             // update styles
27303             if (hasStyles) { 
27304                 var st = this.tb.fields.item(0);
27305                 
27306                 st.store.removeAll();
27307                
27308                 
27309                 var cn = sel.className.split(/\s+/);
27310                 
27311                 var avs = [];
27312                 if (this.styles['*']) {
27313                     
27314                     Roo.each(this.styles['*'], function(v) {
27315                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27316                     });
27317                 }
27318                 if (this.styles[tn]) { 
27319                     Roo.each(this.styles[tn], function(v) {
27320                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
27321                     });
27322                 }
27323                 
27324                 st.store.loadData(avs);
27325                 st.collapse();
27326                 st.setValue(cn);
27327             }
27328             // flag our selected Node.
27329             this.tb.selectedNode = sel;
27330            
27331            
27332             Roo.menu.MenuMgr.hideAll();
27333
27334         }
27335         
27336         if (!updateFooter) {
27337             return;
27338         }
27339         // update the footer
27340         //
27341         var html = '';
27342         
27343         this.footerEls = ans.reverse();
27344         Roo.each(this.footerEls, function(a,i) {
27345             if (!a) { return; }
27346             html += html.length ? ' &gt; '  :  '';
27347             
27348             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
27349             
27350         });
27351        
27352         // 
27353         var sz = this.footDisp.up('td').getSize();
27354         this.footDisp.dom.style.width = (sz.width -10) + 'px';
27355         this.footDisp.dom.style.marginLeft = '5px';
27356         
27357         this.footDisp.dom.style.overflow = 'hidden';
27358         
27359         this.footDisp.dom.innerHTML = html;
27360             
27361         //this.editorsyncValue();
27362     },
27363    
27364        
27365     // private
27366     onDestroy : function(){
27367         if(this.rendered){
27368             
27369             this.tb.items.each(function(item){
27370                 if(item.menu){
27371                     item.menu.removeAll();
27372                     if(item.menu.el){
27373                         item.menu.el.destroy();
27374                     }
27375                 }
27376                 item.destroy();
27377             });
27378              
27379         }
27380     },
27381     onFirstFocus: function() {
27382         // need to do this for all the toolbars..
27383         this.tb.items.each(function(item){
27384            item.enable();
27385         });
27386     },
27387     buildToolbar: function(tlist, nm)
27388     {
27389         var editor = this.editor;
27390          // create a new element.
27391         var wdiv = editor.wrap.createChild({
27392                 tag: 'div'
27393             }, editor.wrap.dom.firstChild.nextSibling, true);
27394         
27395        
27396         var tb = new Roo.Toolbar(wdiv);
27397         // add the name..
27398         
27399         tb.add(nm+ ":&nbsp;");
27400         
27401         var styles = [];
27402         for(var i in this.styles) {
27403             styles.push(i);
27404         }
27405         
27406         // styles...
27407         if (styles && styles.length) {
27408             
27409             // this needs a multi-select checkbox...
27410             tb.addField( new Roo.form.ComboBox({
27411                 store: new Roo.data.SimpleStore({
27412                     id : 'val',
27413                     fields: ['val', 'selected'],
27414                     data : [] 
27415                 }),
27416                 name : '-roo-edit-className',
27417                 attrname : 'className',
27418                 displayField:'val',
27419                 typeAhead: false,
27420                 mode: 'local',
27421                 editable : false,
27422                 triggerAction: 'all',
27423                 emptyText:'Select Style',
27424                 selectOnFocus:true,
27425                 width: 130,
27426                 listeners : {
27427                     'select': function(c, r, i) {
27428                         // initial support only for on class per el..
27429                         tb.selectedNode.className =  r ? r.get('val') : '';
27430                         editor.syncValue();
27431                     }
27432                 }
27433     
27434             }));
27435         }
27436             
27437         
27438         
27439         for (var i in tlist) {
27440             
27441             var item = tlist[i];
27442             tb.add(item.title + ":&nbsp;");
27443             
27444             
27445             
27446             
27447             if (item.opts) {
27448                 // opts == pulldown..
27449                 tb.addField( new Roo.form.ComboBox({
27450                     store: new Roo.data.SimpleStore({
27451                         id : 'val',
27452                         fields: ['val'],
27453                         data : item.opts  
27454                     }),
27455                     name : '-roo-edit-' + i,
27456                     attrname : i,
27457                     displayField:'val',
27458                     typeAhead: false,
27459                     mode: 'local',
27460                     editable : false,
27461                     triggerAction: 'all',
27462                     emptyText:'Select',
27463                     selectOnFocus:true,
27464                     width: item.width ? item.width  : 130,
27465                     listeners : {
27466                         'select': function(c, r, i) {
27467                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
27468                         }
27469                     }
27470
27471                 }));
27472                 continue;
27473                     
27474                  
27475                 
27476                 tb.addField( new Roo.form.TextField({
27477                     name: i,
27478                     width: 100,
27479                     //allowBlank:false,
27480                     value: ''
27481                 }));
27482                 continue;
27483             }
27484             tb.addField( new Roo.form.TextField({
27485                 name: '-roo-edit-' + i,
27486                 attrname : i,
27487                 
27488                 width: item.width,
27489                 //allowBlank:true,
27490                 value: '',
27491                 listeners: {
27492                     'change' : function(f, nv, ov) {
27493                         tb.selectedNode.setAttribute(f.attrname, nv);
27494                     }
27495                 }
27496             }));
27497              
27498         }
27499         tb.el.on('click', function(e){
27500             e.preventDefault(); // what does this do?
27501         });
27502         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
27503         tb.el.hide();
27504         tb.name = nm;
27505         // dont need to disable them... as they will get hidden
27506         return tb;
27507          
27508         
27509     },
27510     buildFooter : function()
27511     {
27512         
27513         var fel = this.editor.wrap.createChild();
27514         this.footer = new Roo.Toolbar(fel);
27515         // toolbar has scrolly on left / right?
27516         var footDisp= new Roo.Toolbar.Fill();
27517         var _t = this;
27518         this.footer.add(
27519             {
27520                 text : '&lt;',
27521                 xtype: 'Button',
27522                 handler : function() {
27523                     _t.footDisp.scrollTo('left',0,true)
27524                 }
27525             }
27526         );
27527         this.footer.add( footDisp );
27528         this.footer.add( 
27529             {
27530                 text : '&gt;',
27531                 xtype: 'Button',
27532                 handler : function() {
27533                     // no animation..
27534                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
27535                 }
27536             }
27537         );
27538         var fel = Roo.get(footDisp.el);
27539         fel.addClass('x-editor-context');
27540         this.footDispWrap = fel; 
27541         this.footDispWrap.overflow  = 'hidden';
27542         
27543         this.footDisp = fel.createChild();
27544         this.footDispWrap.on('click', this.onContextClick, this)
27545         
27546         
27547     },
27548     onContextClick : function (ev,dom)
27549     {
27550         ev.preventDefault();
27551         var  cn = dom.className;
27552         Roo.log(cn);
27553         if (!cn.match(/x-ed-loc-/)) {
27554             return;
27555         }
27556         var n = cn.split('-').pop();
27557         var ans = this.footerEls;
27558         var sel = ans[n];
27559         
27560          // pick
27561         var range = this.editor.createRange();
27562         
27563         range.selectNodeContents(sel);
27564         //range.selectNode(sel);
27565         
27566         
27567         var selection = this.editor.getSelection();
27568         selection.removeAllRanges();
27569         selection.addRange(range);
27570         
27571         
27572         
27573         this.updateToolbar(null, null, sel);
27574         
27575         
27576     }
27577     
27578     
27579     
27580     
27581     
27582 });
27583
27584
27585
27586
27587
27588 /*
27589  * Based on:
27590  * Ext JS Library 1.1.1
27591  * Copyright(c) 2006-2007, Ext JS, LLC.
27592  *
27593  * Originally Released Under LGPL - original licence link has changed is not relivant.
27594  *
27595  * Fork - LGPL
27596  * <script type="text/javascript">
27597  */
27598  
27599 /**
27600  * @class Roo.form.BasicForm
27601  * @extends Roo.util.Observable
27602  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
27603  * @constructor
27604  * @param {String/HTMLElement/Roo.Element} el The form element or its id
27605  * @param {Object} config Configuration options
27606  */
27607 Roo.form.BasicForm = function(el, config){
27608     this.allItems = [];
27609     this.childForms = [];
27610     Roo.apply(this, config);
27611     /*
27612      * The Roo.form.Field items in this form.
27613      * @type MixedCollection
27614      */
27615      
27616      
27617     this.items = new Roo.util.MixedCollection(false, function(o){
27618         return o.id || (o.id = Roo.id());
27619     });
27620     this.addEvents({
27621         /**
27622          * @event beforeaction
27623          * Fires before any action is performed. Return false to cancel the action.
27624          * @param {Form} this
27625          * @param {Action} action The action to be performed
27626          */
27627         beforeaction: true,
27628         /**
27629          * @event actionfailed
27630          * Fires when an action fails.
27631          * @param {Form} this
27632          * @param {Action} action The action that failed
27633          */
27634         actionfailed : true,
27635         /**
27636          * @event actioncomplete
27637          * Fires when an action is completed.
27638          * @param {Form} this
27639          * @param {Action} action The action that completed
27640          */
27641         actioncomplete : true
27642     });
27643     if(el){
27644         this.initEl(el);
27645     }
27646     Roo.form.BasicForm.superclass.constructor.call(this);
27647 };
27648
27649 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
27650     /**
27651      * @cfg {String} method
27652      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
27653      */
27654     /**
27655      * @cfg {DataReader} reader
27656      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
27657      * This is optional as there is built-in support for processing JSON.
27658      */
27659     /**
27660      * @cfg {DataReader} errorReader
27661      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
27662      * This is completely optional as there is built-in support for processing JSON.
27663      */
27664     /**
27665      * @cfg {String} url
27666      * The URL to use for form actions if one isn't supplied in the action options.
27667      */
27668     /**
27669      * @cfg {Boolean} fileUpload
27670      * Set to true if this form is a file upload.
27671      */
27672      
27673     /**
27674      * @cfg {Object} baseParams
27675      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
27676      */
27677      /**
27678      
27679     /**
27680      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
27681      */
27682     timeout: 30,
27683
27684     // private
27685     activeAction : null,
27686
27687     /**
27688      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
27689      * or setValues() data instead of when the form was first created.
27690      */
27691     trackResetOnLoad : false,
27692     
27693     
27694     /**
27695      * childForms - used for multi-tab forms
27696      * @type {Array}
27697      */
27698     childForms : false,
27699     
27700     /**
27701      * allItems - full list of fields.
27702      * @type {Array}
27703      */
27704     allItems : false,
27705     
27706     /**
27707      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
27708      * element by passing it or its id or mask the form itself by passing in true.
27709      * @type Mixed
27710      */
27711     waitMsgTarget : false,
27712
27713     // private
27714     initEl : function(el){
27715         this.el = Roo.get(el);
27716         this.id = this.el.id || Roo.id();
27717         this.el.on('submit', this.onSubmit, this);
27718         this.el.addClass('x-form');
27719     },
27720
27721     // private
27722     onSubmit : function(e){
27723         e.stopEvent();
27724     },
27725
27726     /**
27727      * Returns true if client-side validation on the form is successful.
27728      * @return Boolean
27729      */
27730     isValid : function(){
27731         var valid = true;
27732         this.items.each(function(f){
27733            if(!f.validate()){
27734                valid = false;
27735            }
27736         });
27737         return valid;
27738     },
27739
27740     /**
27741      * Returns true if any fields in this form have changed since their original load.
27742      * @return Boolean
27743      */
27744     isDirty : function(){
27745         var dirty = false;
27746         this.items.each(function(f){
27747            if(f.isDirty()){
27748                dirty = true;
27749                return false;
27750            }
27751         });
27752         return dirty;
27753     },
27754
27755     /**
27756      * Performs a predefined action (submit or load) or custom actions you define on this form.
27757      * @param {String} actionName The name of the action type
27758      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
27759      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
27760      * accept other config options):
27761      * <pre>
27762 Property          Type             Description
27763 ----------------  ---------------  ----------------------------------------------------------------------------------
27764 url               String           The url for the action (defaults to the form's url)
27765 method            String           The form method to use (defaults to the form's method, or POST if not defined)
27766 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
27767 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
27768                                    validate the form on the client (defaults to false)
27769      * </pre>
27770      * @return {BasicForm} this
27771      */
27772     doAction : function(action, options){
27773         if(typeof action == 'string'){
27774             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
27775         }
27776         if(this.fireEvent('beforeaction', this, action) !== false){
27777             this.beforeAction(action);
27778             action.run.defer(100, action);
27779         }
27780         return this;
27781     },
27782
27783     /**
27784      * Shortcut to do a submit action.
27785      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27786      * @return {BasicForm} this
27787      */
27788     submit : function(options){
27789         this.doAction('submit', options);
27790         return this;
27791     },
27792
27793     /**
27794      * Shortcut to do a load action.
27795      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
27796      * @return {BasicForm} this
27797      */
27798     load : function(options){
27799         this.doAction('load', options);
27800         return this;
27801     },
27802
27803     /**
27804      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
27805      * @param {Record} record The record to edit
27806      * @return {BasicForm} this
27807      */
27808     updateRecord : function(record){
27809         record.beginEdit();
27810         var fs = record.fields;
27811         fs.each(function(f){
27812             var field = this.findField(f.name);
27813             if(field){
27814                 record.set(f.name, field.getValue());
27815             }
27816         }, this);
27817         record.endEdit();
27818         return this;
27819     },
27820
27821     /**
27822      * Loads an Roo.data.Record into this form.
27823      * @param {Record} record The record to load
27824      * @return {BasicForm} this
27825      */
27826     loadRecord : function(record){
27827         this.setValues(record.data);
27828         return this;
27829     },
27830
27831     // private
27832     beforeAction : function(action){
27833         var o = action.options;
27834         
27835        
27836         if(this.waitMsgTarget === true){
27837             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
27838         }else if(this.waitMsgTarget){
27839             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
27840             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
27841         }else {
27842             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
27843         }
27844          
27845     },
27846
27847     // private
27848     afterAction : function(action, success){
27849         this.activeAction = null;
27850         var o = action.options;
27851         
27852         if(this.waitMsgTarget === true){
27853             this.el.unmask();
27854         }else if(this.waitMsgTarget){
27855             this.waitMsgTarget.unmask();
27856         }else{
27857             Roo.MessageBox.updateProgress(1);
27858             Roo.MessageBox.hide();
27859         }
27860          
27861         if(success){
27862             if(o.reset){
27863                 this.reset();
27864             }
27865             Roo.callback(o.success, o.scope, [this, action]);
27866             this.fireEvent('actioncomplete', this, action);
27867             
27868         }else{
27869             
27870             // failure condition..
27871             // we have a scenario where updates need confirming.
27872             // eg. if a locking scenario exists..
27873             // we look for { errors : { needs_confirm : true }} in the response.
27874             if (
27875                 (typeof(action.result) != 'undefined')  &&
27876                 (typeof(action.result.errors) != 'undefined')  &&
27877                 (typeof(action.result.errors.needs_confirm) != 'undefined')
27878            ){
27879                 var _t = this;
27880                 Roo.MessageBox.confirm(
27881                     "Change requires confirmation",
27882                     action.result.errorMsg,
27883                     function(r) {
27884                         if (r != 'yes') {
27885                             return;
27886                         }
27887                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
27888                     }
27889                     
27890                 );
27891                 
27892                 
27893                 
27894                 return;
27895             }
27896             
27897             Roo.callback(o.failure, o.scope, [this, action]);
27898             // show an error message if no failed handler is set..
27899             if (!this.hasListener('actionfailed')) {
27900                 Roo.MessageBox.alert("Error",
27901                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
27902                         action.result.errorMsg :
27903                         "Saving Failed, please check your entries or try again"
27904                 );
27905             }
27906             
27907             this.fireEvent('actionfailed', this, action);
27908         }
27909         
27910     },
27911
27912     /**
27913      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
27914      * @param {String} id The value to search for
27915      * @return Field
27916      */
27917     findField : function(id){
27918         var field = this.items.get(id);
27919         if(!field){
27920             this.items.each(function(f){
27921                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
27922                     field = f;
27923                     return false;
27924                 }
27925             });
27926         }
27927         return field || null;
27928     },
27929
27930     /**
27931      * Add a secondary form to this one, 
27932      * Used to provide tabbed forms. One form is primary, with hidden values 
27933      * which mirror the elements from the other forms.
27934      * 
27935      * @param {Roo.form.Form} form to add.
27936      * 
27937      */
27938     addForm : function(form)
27939     {
27940        
27941         if (this.childForms.indexOf(form) > -1) {
27942             // already added..
27943             return;
27944         }
27945         this.childForms.push(form);
27946         var n = '';
27947         Roo.each(form.allItems, function (fe) {
27948             
27949             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
27950             if (this.findField(n)) { // already added..
27951                 return;
27952             }
27953             var add = new Roo.form.Hidden({
27954                 name : n
27955             });
27956             add.render(this.el);
27957             
27958             this.add( add );
27959         }, this);
27960         
27961     },
27962     /**
27963      * Mark fields in this form invalid in bulk.
27964      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
27965      * @return {BasicForm} this
27966      */
27967     markInvalid : function(errors){
27968         if(errors instanceof Array){
27969             for(var i = 0, len = errors.length; i < len; i++){
27970                 var fieldError = errors[i];
27971                 var f = this.findField(fieldError.id);
27972                 if(f){
27973                     f.markInvalid(fieldError.msg);
27974                 }
27975             }
27976         }else{
27977             var field, id;
27978             for(id in errors){
27979                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
27980                     field.markInvalid(errors[id]);
27981                 }
27982             }
27983         }
27984         Roo.each(this.childForms || [], function (f) {
27985             f.markInvalid(errors);
27986         });
27987         
27988         return this;
27989     },
27990
27991     /**
27992      * Set values for fields in this form in bulk.
27993      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
27994      * @return {BasicForm} this
27995      */
27996     setValues : function(values){
27997         if(values instanceof Array){ // array of objects
27998             for(var i = 0, len = values.length; i < len; i++){
27999                 var v = values[i];
28000                 var f = this.findField(v.id);
28001                 if(f){
28002                     f.setValue(v.value);
28003                     if(this.trackResetOnLoad){
28004                         f.originalValue = f.getValue();
28005                     }
28006                 }
28007             }
28008         }else{ // object hash
28009             var field, id;
28010             for(id in values){
28011                 if(typeof values[id] != 'function' && (field = this.findField(id))){
28012                     
28013                     if (field.setFromData && 
28014                         field.valueField && 
28015                         field.displayField &&
28016                         // combos' with local stores can 
28017                         // be queried via setValue()
28018                         // to set their value..
28019                         (field.store && !field.store.isLocal)
28020                         ) {
28021                         // it's a combo
28022                         var sd = { };
28023                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
28024                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
28025                         field.setFromData(sd);
28026                         
28027                     } else {
28028                         field.setValue(values[id]);
28029                     }
28030                     
28031                     
28032                     if(this.trackResetOnLoad){
28033                         field.originalValue = field.getValue();
28034                     }
28035                 }
28036             }
28037         }
28038          
28039         Roo.each(this.childForms || [], function (f) {
28040             f.setValues(values);
28041         });
28042                 
28043         return this;
28044     },
28045
28046     /**
28047      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
28048      * they are returned as an array.
28049      * @param {Boolean} asString
28050      * @return {Object}
28051      */
28052     getValues : function(asString){
28053         if (this.childForms) {
28054             // copy values from the child forms
28055             Roo.each(this.childForms, function (f) {
28056                 this.setValues(f.getValues());
28057             }, this);
28058         }
28059         
28060         
28061         
28062         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
28063         if(asString === true){
28064             return fs;
28065         }
28066         return Roo.urlDecode(fs);
28067     },
28068     
28069     /**
28070      * Returns the fields in this form as an object with key/value pairs. 
28071      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
28072      * @return {Object}
28073      */
28074     getFieldValues : function(with_hidden)
28075     {
28076         if (this.childForms) {
28077             // copy values from the child forms
28078             // should this call getFieldValues - probably not as we do not currently copy
28079             // hidden fields when we generate..
28080             Roo.each(this.childForms, function (f) {
28081                 this.setValues(f.getValues());
28082             }, this);
28083         }
28084         
28085         var ret = {};
28086         this.items.each(function(f){
28087             if (!f.getName()) {
28088                 return;
28089             }
28090             var v = f.getValue();
28091             // not sure if this supported any more..
28092             if ((typeof(v) == 'object') && f.getRawValue) {
28093                 v = f.getRawValue() ; // dates..
28094             }
28095             // combo boxes where name != hiddenName...
28096             if (f.name != f.getName()) {
28097                 ret[f.name] = f.getRawValue();
28098             }
28099             ret[f.getName()] = v;
28100         });
28101         
28102         return ret;
28103     },
28104
28105     /**
28106      * Clears all invalid messages in this form.
28107      * @return {BasicForm} this
28108      */
28109     clearInvalid : function(){
28110         this.items.each(function(f){
28111            f.clearInvalid();
28112         });
28113         
28114         Roo.each(this.childForms || [], function (f) {
28115             f.clearInvalid();
28116         });
28117         
28118         
28119         return this;
28120     },
28121
28122     /**
28123      * Resets this form.
28124      * @return {BasicForm} this
28125      */
28126     reset : function(){
28127         this.items.each(function(f){
28128             f.reset();
28129         });
28130         
28131         Roo.each(this.childForms || [], function (f) {
28132             f.reset();
28133         });
28134        
28135         
28136         return this;
28137     },
28138
28139     /**
28140      * Add Roo.form components to this form.
28141      * @param {Field} field1
28142      * @param {Field} field2 (optional)
28143      * @param {Field} etc (optional)
28144      * @return {BasicForm} this
28145      */
28146     add : function(){
28147         this.items.addAll(Array.prototype.slice.call(arguments, 0));
28148         return this;
28149     },
28150
28151
28152     /**
28153      * Removes a field from the items collection (does NOT remove its markup).
28154      * @param {Field} field
28155      * @return {BasicForm} this
28156      */
28157     remove : function(field){
28158         this.items.remove(field);
28159         return this;
28160     },
28161
28162     /**
28163      * Looks at the fields in this form, checks them for an id attribute,
28164      * and calls applyTo on the existing dom element with that id.
28165      * @return {BasicForm} this
28166      */
28167     render : function(){
28168         this.items.each(function(f){
28169             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
28170                 f.applyTo(f.id);
28171             }
28172         });
28173         return this;
28174     },
28175
28176     /**
28177      * Calls {@link Ext#apply} for all fields in this form with the passed object.
28178      * @param {Object} values
28179      * @return {BasicForm} this
28180      */
28181     applyToFields : function(o){
28182         this.items.each(function(f){
28183            Roo.apply(f, o);
28184         });
28185         return this;
28186     },
28187
28188     /**
28189      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
28190      * @param {Object} values
28191      * @return {BasicForm} this
28192      */
28193     applyIfToFields : function(o){
28194         this.items.each(function(f){
28195            Roo.applyIf(f, o);
28196         });
28197         return this;
28198     }
28199 });
28200
28201 // back compat
28202 Roo.BasicForm = Roo.form.BasicForm;/*
28203  * Based on:
28204  * Ext JS Library 1.1.1
28205  * Copyright(c) 2006-2007, Ext JS, LLC.
28206  *
28207  * Originally Released Under LGPL - original licence link has changed is not relivant.
28208  *
28209  * Fork - LGPL
28210  * <script type="text/javascript">
28211  */
28212
28213 /**
28214  * @class Roo.form.Form
28215  * @extends Roo.form.BasicForm
28216  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
28217  * @constructor
28218  * @param {Object} config Configuration options
28219  */
28220 Roo.form.Form = function(config){
28221     var xitems =  [];
28222     if (config.items) {
28223         xitems = config.items;
28224         delete config.items;
28225     }
28226    
28227     
28228     Roo.form.Form.superclass.constructor.call(this, null, config);
28229     this.url = this.url || this.action;
28230     if(!this.root){
28231         this.root = new Roo.form.Layout(Roo.applyIf({
28232             id: Roo.id()
28233         }, config));
28234     }
28235     this.active = this.root;
28236     /**
28237      * Array of all the buttons that have been added to this form via {@link addButton}
28238      * @type Array
28239      */
28240     this.buttons = [];
28241     this.allItems = [];
28242     this.addEvents({
28243         /**
28244          * @event clientvalidation
28245          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
28246          * @param {Form} this
28247          * @param {Boolean} valid true if the form has passed client-side validation
28248          */
28249         clientvalidation: true,
28250         /**
28251          * @event rendered
28252          * Fires when the form is rendered
28253          * @param {Roo.form.Form} form
28254          */
28255         rendered : true
28256     });
28257     
28258     if (this.progressUrl) {
28259             // push a hidden field onto the list of fields..
28260             this.addxtype( {
28261                     xns: Roo.form, 
28262                     xtype : 'Hidden', 
28263                     name : 'UPLOAD_IDENTIFIER' 
28264             });
28265         }
28266         
28267     
28268     Roo.each(xitems, this.addxtype, this);
28269     
28270     
28271     
28272 };
28273
28274 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
28275     /**
28276      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
28277      */
28278     /**
28279      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
28280      */
28281     /**
28282      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
28283      */
28284     buttonAlign:'center',
28285
28286     /**
28287      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
28288      */
28289     minButtonWidth:75,
28290
28291     /**
28292      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
28293      * This property cascades to child containers if not set.
28294      */
28295     labelAlign:'left',
28296
28297     /**
28298      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
28299      * fires a looping event with that state. This is required to bind buttons to the valid
28300      * state using the config value formBind:true on the button.
28301      */
28302     monitorValid : false,
28303
28304     /**
28305      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
28306      */
28307     monitorPoll : 200,
28308     
28309     /**
28310      * @cfg {String} progressUrl - Url to return progress data 
28311      */
28312     
28313     progressUrl : false,
28314   
28315     /**
28316      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
28317      * fields are added and the column is closed. If no fields are passed the column remains open
28318      * until end() is called.
28319      * @param {Object} config The config to pass to the column
28320      * @param {Field} field1 (optional)
28321      * @param {Field} field2 (optional)
28322      * @param {Field} etc (optional)
28323      * @return Column The column container object
28324      */
28325     column : function(c){
28326         var col = new Roo.form.Column(c);
28327         this.start(col);
28328         if(arguments.length > 1){ // duplicate code required because of Opera
28329             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28330             this.end();
28331         }
28332         return col;
28333     },
28334
28335     /**
28336      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
28337      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
28338      * until end() is called.
28339      * @param {Object} config The config to pass to the fieldset
28340      * @param {Field} field1 (optional)
28341      * @param {Field} field2 (optional)
28342      * @param {Field} etc (optional)
28343      * @return FieldSet The fieldset container object
28344      */
28345     fieldset : function(c){
28346         var fs = new Roo.form.FieldSet(c);
28347         this.start(fs);
28348         if(arguments.length > 1){ // duplicate code required because of Opera
28349             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28350             this.end();
28351         }
28352         return fs;
28353     },
28354
28355     /**
28356      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
28357      * fields are added and the container is closed. If no fields are passed the container remains open
28358      * until end() is called.
28359      * @param {Object} config The config to pass to the Layout
28360      * @param {Field} field1 (optional)
28361      * @param {Field} field2 (optional)
28362      * @param {Field} etc (optional)
28363      * @return Layout The container object
28364      */
28365     container : function(c){
28366         var l = new Roo.form.Layout(c);
28367         this.start(l);
28368         if(arguments.length > 1){ // duplicate code required because of Opera
28369             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
28370             this.end();
28371         }
28372         return l;
28373     },
28374
28375     /**
28376      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
28377      * @param {Object} container A Roo.form.Layout or subclass of Layout
28378      * @return {Form} this
28379      */
28380     start : function(c){
28381         // cascade label info
28382         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
28383         this.active.stack.push(c);
28384         c.ownerCt = this.active;
28385         this.active = c;
28386         return this;
28387     },
28388
28389     /**
28390      * Closes the current open container
28391      * @return {Form} this
28392      */
28393     end : function(){
28394         if(this.active == this.root){
28395             return this;
28396         }
28397         this.active = this.active.ownerCt;
28398         return this;
28399     },
28400
28401     /**
28402      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
28403      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
28404      * as the label of the field.
28405      * @param {Field} field1
28406      * @param {Field} field2 (optional)
28407      * @param {Field} etc. (optional)
28408      * @return {Form} this
28409      */
28410     add : function(){
28411         this.active.stack.push.apply(this.active.stack, arguments);
28412         this.allItems.push.apply(this.allItems,arguments);
28413         var r = [];
28414         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
28415             if(a[i].isFormField){
28416                 r.push(a[i]);
28417             }
28418         }
28419         if(r.length > 0){
28420             Roo.form.Form.superclass.add.apply(this, r);
28421         }
28422         return this;
28423     },
28424     
28425
28426     
28427     
28428     
28429      /**
28430      * Find any element that has been added to a form, using it's ID or name
28431      * This can include framesets, columns etc. along with regular fields..
28432      * @param {String} id - id or name to find.
28433      
28434      * @return {Element} e - or false if nothing found.
28435      */
28436     findbyId : function(id)
28437     {
28438         var ret = false;
28439         if (!id) {
28440             return ret;
28441         }
28442         Roo.each(this.allItems, function(f){
28443             if (f.id == id || f.name == id ){
28444                 ret = f;
28445                 return false;
28446             }
28447         });
28448         return ret;
28449     },
28450
28451     
28452     
28453     /**
28454      * Render this form into the passed container. This should only be called once!
28455      * @param {String/HTMLElement/Element} container The element this component should be rendered into
28456      * @return {Form} this
28457      */
28458     render : function(ct)
28459     {
28460         
28461         
28462         
28463         ct = Roo.get(ct);
28464         var o = this.autoCreate || {
28465             tag: 'form',
28466             method : this.method || 'POST',
28467             id : this.id || Roo.id()
28468         };
28469         this.initEl(ct.createChild(o));
28470
28471         this.root.render(this.el);
28472         
28473        
28474              
28475         this.items.each(function(f){
28476             f.render('x-form-el-'+f.id);
28477         });
28478
28479         if(this.buttons.length > 0){
28480             // tables are required to maintain order and for correct IE layout
28481             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
28482                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
28483                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28484             }}, null, true);
28485             var tr = tb.getElementsByTagName('tr')[0];
28486             for(var i = 0, len = this.buttons.length; i < len; i++) {
28487                 var b = this.buttons[i];
28488                 var td = document.createElement('td');
28489                 td.className = 'x-form-btn-td';
28490                 b.render(tr.appendChild(td));
28491             }
28492         }
28493         if(this.monitorValid){ // initialize after render
28494             this.startMonitoring();
28495         }
28496         this.fireEvent('rendered', this);
28497         return this;
28498     },
28499
28500     /**
28501      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
28502      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28503      * object or a valid Roo.DomHelper element config
28504      * @param {Function} handler The function called when the button is clicked
28505      * @param {Object} scope (optional) The scope of the handler function
28506      * @return {Roo.Button}
28507      */
28508     addButton : function(config, handler, scope){
28509         var bc = {
28510             handler: handler,
28511             scope: scope,
28512             minWidth: this.minButtonWidth,
28513             hideParent:true
28514         };
28515         if(typeof config == "string"){
28516             bc.text = config;
28517         }else{
28518             Roo.apply(bc, config);
28519         }
28520         var btn = new Roo.Button(null, bc);
28521         this.buttons.push(btn);
28522         return btn;
28523     },
28524
28525      /**
28526      * Adds a series of form elements (using the xtype property as the factory method.
28527      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
28528      * @param {Object} config 
28529      */
28530     
28531     addxtype : function()
28532     {
28533         var ar = Array.prototype.slice.call(arguments, 0);
28534         var ret = false;
28535         for(var i = 0; i < ar.length; i++) {
28536             if (!ar[i]) {
28537                 continue; // skip -- if this happends something invalid got sent, we 
28538                 // should ignore it, as basically that interface element will not show up
28539                 // and that should be pretty obvious!!
28540             }
28541             
28542             if (Roo.form[ar[i].xtype]) {
28543                 ar[i].form = this;
28544                 var fe = Roo.factory(ar[i], Roo.form);
28545                 if (!ret) {
28546                     ret = fe;
28547                 }
28548                 fe.form = this;
28549                 if (fe.store) {
28550                     fe.store.form = this;
28551                 }
28552                 if (fe.isLayout) {  
28553                          
28554                     this.start(fe);
28555                     this.allItems.push(fe);
28556                     if (fe.items && fe.addxtype) {
28557                         fe.addxtype.apply(fe, fe.items);
28558                         delete fe.items;
28559                     }
28560                      this.end();
28561                     continue;
28562                 }
28563                 
28564                 
28565                  
28566                 this.add(fe);
28567               //  console.log('adding ' + ar[i].xtype);
28568             }
28569             if (ar[i].xtype == 'Button') {  
28570                 //console.log('adding button');
28571                 //console.log(ar[i]);
28572                 this.addButton(ar[i]);
28573                 this.allItems.push(fe);
28574                 continue;
28575             }
28576             
28577             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
28578                 alert('end is not supported on xtype any more, use items');
28579             //    this.end();
28580             //    //console.log('adding end');
28581             }
28582             
28583         }
28584         return ret;
28585     },
28586     
28587     /**
28588      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
28589      * option "monitorValid"
28590      */
28591     startMonitoring : function(){
28592         if(!this.bound){
28593             this.bound = true;
28594             Roo.TaskMgr.start({
28595                 run : this.bindHandler,
28596                 interval : this.monitorPoll || 200,
28597                 scope: this
28598             });
28599         }
28600     },
28601
28602     /**
28603      * Stops monitoring of the valid state of this form
28604      */
28605     stopMonitoring : function(){
28606         this.bound = false;
28607     },
28608
28609     // private
28610     bindHandler : function(){
28611         if(!this.bound){
28612             return false; // stops binding
28613         }
28614         var valid = true;
28615         this.items.each(function(f){
28616             if(!f.isValid(true)){
28617                 valid = false;
28618                 return false;
28619             }
28620         });
28621         for(var i = 0, len = this.buttons.length; i < len; i++){
28622             var btn = this.buttons[i];
28623             if(btn.formBind === true && btn.disabled === valid){
28624                 btn.setDisabled(!valid);
28625             }
28626         }
28627         this.fireEvent('clientvalidation', this, valid);
28628     }
28629     
28630     
28631     
28632     
28633     
28634     
28635     
28636     
28637 });
28638
28639
28640 // back compat
28641 Roo.Form = Roo.form.Form;
28642 /*
28643  * Based on:
28644  * Ext JS Library 1.1.1
28645  * Copyright(c) 2006-2007, Ext JS, LLC.
28646  *
28647  * Originally Released Under LGPL - original licence link has changed is not relivant.
28648  *
28649  * Fork - LGPL
28650  * <script type="text/javascript">
28651  */
28652  
28653  /**
28654  * @class Roo.form.Action
28655  * Internal Class used to handle form actions
28656  * @constructor
28657  * @param {Roo.form.BasicForm} el The form element or its id
28658  * @param {Object} config Configuration options
28659  */
28660  
28661  
28662 // define the action interface
28663 Roo.form.Action = function(form, options){
28664     this.form = form;
28665     this.options = options || {};
28666 };
28667 /**
28668  * Client Validation Failed
28669  * @const 
28670  */
28671 Roo.form.Action.CLIENT_INVALID = 'client';
28672 /**
28673  * Server Validation Failed
28674  * @const 
28675  */
28676  Roo.form.Action.SERVER_INVALID = 'server';
28677  /**
28678  * Connect to Server Failed
28679  * @const 
28680  */
28681 Roo.form.Action.CONNECT_FAILURE = 'connect';
28682 /**
28683  * Reading Data from Server Failed
28684  * @const 
28685  */
28686 Roo.form.Action.LOAD_FAILURE = 'load';
28687
28688 Roo.form.Action.prototype = {
28689     type : 'default',
28690     failureType : undefined,
28691     response : undefined,
28692     result : undefined,
28693
28694     // interface method
28695     run : function(options){
28696
28697     },
28698
28699     // interface method
28700     success : function(response){
28701
28702     },
28703
28704     // interface method
28705     handleResponse : function(response){
28706
28707     },
28708
28709     // default connection failure
28710     failure : function(response){
28711         
28712         this.response = response;
28713         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28714         this.form.afterAction(this, false);
28715     },
28716
28717     processResponse : function(response){
28718         this.response = response;
28719         if(!response.responseText){
28720             return true;
28721         }
28722         this.result = this.handleResponse(response);
28723         return this.result;
28724     },
28725
28726     // utility functions used internally
28727     getUrl : function(appendParams){
28728         var url = this.options.url || this.form.url || this.form.el.dom.action;
28729         if(appendParams){
28730             var p = this.getParams();
28731             if(p){
28732                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
28733             }
28734         }
28735         return url;
28736     },
28737
28738     getMethod : function(){
28739         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
28740     },
28741
28742     getParams : function(){
28743         var bp = this.form.baseParams;
28744         var p = this.options.params;
28745         if(p){
28746             if(typeof p == "object"){
28747                 p = Roo.urlEncode(Roo.applyIf(p, bp));
28748             }else if(typeof p == 'string' && bp){
28749                 p += '&' + Roo.urlEncode(bp);
28750             }
28751         }else if(bp){
28752             p = Roo.urlEncode(bp);
28753         }
28754         return p;
28755     },
28756
28757     createCallback : function(){
28758         return {
28759             success: this.success,
28760             failure: this.failure,
28761             scope: this,
28762             timeout: (this.form.timeout*1000),
28763             upload: this.form.fileUpload ? this.success : undefined
28764         };
28765     }
28766 };
28767
28768 Roo.form.Action.Submit = function(form, options){
28769     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
28770 };
28771
28772 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
28773     type : 'submit',
28774
28775     haveProgress : false,
28776     uploadComplete : false,
28777     
28778     // uploadProgress indicator.
28779     uploadProgress : function()
28780     {
28781         if (!this.form.progressUrl) {
28782             return;
28783         }
28784         
28785         if (!this.haveProgress) {
28786             Roo.MessageBox.progress("Uploading", "Uploading");
28787         }
28788         if (this.uploadComplete) {
28789            Roo.MessageBox.hide();
28790            return;
28791         }
28792         
28793         this.haveProgress = true;
28794    
28795         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
28796         
28797         var c = new Roo.data.Connection();
28798         c.request({
28799             url : this.form.progressUrl,
28800             params: {
28801                 id : uid
28802             },
28803             method: 'GET',
28804             success : function(req){
28805                //console.log(data);
28806                 var rdata = false;
28807                 var edata;
28808                 try  {
28809                    rdata = Roo.decode(req.responseText)
28810                 } catch (e) {
28811                     Roo.log("Invalid data from server..");
28812                     Roo.log(edata);
28813                     return;
28814                 }
28815                 if (!rdata || !rdata.success) {
28816                     Roo.log(rdata);
28817                     Roo.MessageBox.alert(Roo.encode(rdata));
28818                     return;
28819                 }
28820                 var data = rdata.data;
28821                 
28822                 if (this.uploadComplete) {
28823                    Roo.MessageBox.hide();
28824                    return;
28825                 }
28826                    
28827                 if (data){
28828                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
28829                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
28830                     );
28831                 }
28832                 this.uploadProgress.defer(2000,this);
28833             },
28834        
28835             failure: function(data) {
28836                 Roo.log('progress url failed ');
28837                 Roo.log(data);
28838             },
28839             scope : this
28840         });
28841            
28842     },
28843     
28844     
28845     run : function()
28846     {
28847         // run get Values on the form, so it syncs any secondary forms.
28848         this.form.getValues();
28849         
28850         var o = this.options;
28851         var method = this.getMethod();
28852         var isPost = method == 'POST';
28853         if(o.clientValidation === false || this.form.isValid()){
28854             
28855             if (this.form.progressUrl) {
28856                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
28857                     (new Date() * 1) + '' + Math.random());
28858                     
28859             } 
28860             
28861             
28862             Roo.Ajax.request(Roo.apply(this.createCallback(), {
28863                 form:this.form.el.dom,
28864                 url:this.getUrl(!isPost),
28865                 method: method,
28866                 params:isPost ? this.getParams() : null,
28867                 isUpload: this.form.fileUpload
28868             }));
28869             
28870             this.uploadProgress();
28871
28872         }else if (o.clientValidation !== false){ // client validation failed
28873             this.failureType = Roo.form.Action.CLIENT_INVALID;
28874             this.form.afterAction(this, false);
28875         }
28876     },
28877
28878     success : function(response)
28879     {
28880         this.uploadComplete= true;
28881         if (this.haveProgress) {
28882             Roo.MessageBox.hide();
28883         }
28884         
28885         
28886         var result = this.processResponse(response);
28887         if(result === true || result.success){
28888             this.form.afterAction(this, true);
28889             return;
28890         }
28891         if(result.errors){
28892             this.form.markInvalid(result.errors);
28893             this.failureType = Roo.form.Action.SERVER_INVALID;
28894         }
28895         this.form.afterAction(this, false);
28896     },
28897     failure : function(response)
28898     {
28899         this.uploadComplete= true;
28900         if (this.haveProgress) {
28901             Roo.MessageBox.hide();
28902         }
28903         
28904         this.response = response;
28905         this.failureType = Roo.form.Action.CONNECT_FAILURE;
28906         this.form.afterAction(this, false);
28907     },
28908     
28909     handleResponse : function(response){
28910         if(this.form.errorReader){
28911             var rs = this.form.errorReader.read(response);
28912             var errors = [];
28913             if(rs.records){
28914                 for(var i = 0, len = rs.records.length; i < len; i++) {
28915                     var r = rs.records[i];
28916                     errors[i] = r.data;
28917                 }
28918             }
28919             if(errors.length < 1){
28920                 errors = null;
28921             }
28922             return {
28923                 success : rs.success,
28924                 errors : errors
28925             };
28926         }
28927         var ret = false;
28928         try {
28929             ret = Roo.decode(response.responseText);
28930         } catch (e) {
28931             ret = {
28932                 success: false,
28933                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
28934                 errors : []
28935             };
28936         }
28937         return ret;
28938         
28939     }
28940 });
28941
28942
28943 Roo.form.Action.Load = function(form, options){
28944     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
28945     this.reader = this.form.reader;
28946 };
28947
28948 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
28949     type : 'load',
28950
28951     run : function(){
28952         
28953         Roo.Ajax.request(Roo.apply(
28954                 this.createCallback(), {
28955                     method:this.getMethod(),
28956                     url:this.getUrl(false),
28957                     params:this.getParams()
28958         }));
28959     },
28960
28961     success : function(response){
28962         
28963         var result = this.processResponse(response);
28964         if(result === true || !result.success || !result.data){
28965             this.failureType = Roo.form.Action.LOAD_FAILURE;
28966             this.form.afterAction(this, false);
28967             return;
28968         }
28969         this.form.clearInvalid();
28970         this.form.setValues(result.data);
28971         this.form.afterAction(this, true);
28972     },
28973
28974     handleResponse : function(response){
28975         if(this.form.reader){
28976             var rs = this.form.reader.read(response);
28977             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
28978             return {
28979                 success : rs.success,
28980                 data : data
28981             };
28982         }
28983         return Roo.decode(response.responseText);
28984     }
28985 });
28986
28987 Roo.form.Action.ACTION_TYPES = {
28988     'load' : Roo.form.Action.Load,
28989     'submit' : Roo.form.Action.Submit
28990 };/*
28991  * Based on:
28992  * Ext JS Library 1.1.1
28993  * Copyright(c) 2006-2007, Ext JS, LLC.
28994  *
28995  * Originally Released Under LGPL - original licence link has changed is not relivant.
28996  *
28997  * Fork - LGPL
28998  * <script type="text/javascript">
28999  */
29000  
29001 /**
29002  * @class Roo.form.Layout
29003  * @extends Roo.Component
29004  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
29005  * @constructor
29006  * @param {Object} config Configuration options
29007  */
29008 Roo.form.Layout = function(config){
29009     var xitems = [];
29010     if (config.items) {
29011         xitems = config.items;
29012         delete config.items;
29013     }
29014     Roo.form.Layout.superclass.constructor.call(this, config);
29015     this.stack = [];
29016     Roo.each(xitems, this.addxtype, this);
29017      
29018 };
29019
29020 Roo.extend(Roo.form.Layout, Roo.Component, {
29021     /**
29022      * @cfg {String/Object} autoCreate
29023      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
29024      */
29025     /**
29026      * @cfg {String/Object/Function} style
29027      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
29028      * a function which returns such a specification.
29029      */
29030     /**
29031      * @cfg {String} labelAlign
29032      * Valid values are "left," "top" and "right" (defaults to "left")
29033      */
29034     /**
29035      * @cfg {Number} labelWidth
29036      * Fixed width in pixels of all field labels (defaults to undefined)
29037      */
29038     /**
29039      * @cfg {Boolean} clear
29040      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
29041      */
29042     clear : true,
29043     /**
29044      * @cfg {String} labelSeparator
29045      * The separator to use after field labels (defaults to ':')
29046      */
29047     labelSeparator : ':',
29048     /**
29049      * @cfg {Boolean} hideLabels
29050      * True to suppress the display of field labels in this layout (defaults to false)
29051      */
29052     hideLabels : false,
29053
29054     // private
29055     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
29056     
29057     isLayout : true,
29058     
29059     // private
29060     onRender : function(ct, position){
29061         if(this.el){ // from markup
29062             this.el = Roo.get(this.el);
29063         }else {  // generate
29064             var cfg = this.getAutoCreate();
29065             this.el = ct.createChild(cfg, position);
29066         }
29067         if(this.style){
29068             this.el.applyStyles(this.style);
29069         }
29070         if(this.labelAlign){
29071             this.el.addClass('x-form-label-'+this.labelAlign);
29072         }
29073         if(this.hideLabels){
29074             this.labelStyle = "display:none";
29075             this.elementStyle = "padding-left:0;";
29076         }else{
29077             if(typeof this.labelWidth == 'number'){
29078                 this.labelStyle = "width:"+this.labelWidth+"px;";
29079                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
29080             }
29081             if(this.labelAlign == 'top'){
29082                 this.labelStyle = "width:auto;";
29083                 this.elementStyle = "padding-left:0;";
29084             }
29085         }
29086         var stack = this.stack;
29087         var slen = stack.length;
29088         if(slen > 0){
29089             if(!this.fieldTpl){
29090                 var t = new Roo.Template(
29091                     '<div class="x-form-item {5}">',
29092                         '<label for="{0}" style="{2}">{1}{4}</label>',
29093                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29094                         '</div>',
29095                     '</div><div class="x-form-clear-left"></div>'
29096                 );
29097                 t.disableFormats = true;
29098                 t.compile();
29099                 Roo.form.Layout.prototype.fieldTpl = t;
29100             }
29101             for(var i = 0; i < slen; i++) {
29102                 if(stack[i].isFormField){
29103                     this.renderField(stack[i]);
29104                 }else{
29105                     this.renderComponent(stack[i]);
29106                 }
29107             }
29108         }
29109         if(this.clear){
29110             this.el.createChild({cls:'x-form-clear'});
29111         }
29112     },
29113
29114     // private
29115     renderField : function(f){
29116         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
29117                f.id, //0
29118                f.fieldLabel, //1
29119                f.labelStyle||this.labelStyle||'', //2
29120                this.elementStyle||'', //3
29121                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
29122                f.itemCls||this.itemCls||''  //5
29123        ], true).getPrevSibling());
29124     },
29125
29126     // private
29127     renderComponent : function(c){
29128         c.render(c.isLayout ? this.el : this.el.createChild());    
29129     },
29130     /**
29131      * Adds a object form elements (using the xtype property as the factory method.)
29132      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
29133      * @param {Object} config 
29134      */
29135     addxtype : function(o)
29136     {
29137         // create the lement.
29138         o.form = this.form;
29139         var fe = Roo.factory(o, Roo.form);
29140         this.form.allItems.push(fe);
29141         this.stack.push(fe);
29142         
29143         if (fe.isFormField) {
29144             this.form.items.add(fe);
29145         }
29146          
29147         return fe;
29148     }
29149 });
29150
29151 /**
29152  * @class Roo.form.Column
29153  * @extends Roo.form.Layout
29154  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
29155  * @constructor
29156  * @param {Object} config Configuration options
29157  */
29158 Roo.form.Column = function(config){
29159     Roo.form.Column.superclass.constructor.call(this, config);
29160 };
29161
29162 Roo.extend(Roo.form.Column, Roo.form.Layout, {
29163     /**
29164      * @cfg {Number/String} width
29165      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29166      */
29167     /**
29168      * @cfg {String/Object} autoCreate
29169      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
29170      */
29171
29172     // private
29173     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
29174
29175     // private
29176     onRender : function(ct, position){
29177         Roo.form.Column.superclass.onRender.call(this, ct, position);
29178         if(this.width){
29179             this.el.setWidth(this.width);
29180         }
29181     }
29182 });
29183
29184
29185 /**
29186  * @class Roo.form.Row
29187  * @extends Roo.form.Layout
29188  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
29189  * @constructor
29190  * @param {Object} config Configuration options
29191  */
29192
29193  
29194 Roo.form.Row = function(config){
29195     Roo.form.Row.superclass.constructor.call(this, config);
29196 };
29197  
29198 Roo.extend(Roo.form.Row, Roo.form.Layout, {
29199       /**
29200      * @cfg {Number/String} width
29201      * The fixed width of the column in pixels or CSS value (defaults to "auto")
29202      */
29203     /**
29204      * @cfg {Number/String} height
29205      * The fixed height of the column in pixels or CSS value (defaults to "auto")
29206      */
29207     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
29208     
29209     padWidth : 20,
29210     // private
29211     onRender : function(ct, position){
29212         //console.log('row render');
29213         if(!this.rowTpl){
29214             var t = new Roo.Template(
29215                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
29216                     '<label for="{0}" style="{2}">{1}{4}</label>',
29217                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
29218                     '</div>',
29219                 '</div>'
29220             );
29221             t.disableFormats = true;
29222             t.compile();
29223             Roo.form.Layout.prototype.rowTpl = t;
29224         }
29225         this.fieldTpl = this.rowTpl;
29226         
29227         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
29228         var labelWidth = 100;
29229         
29230         if ((this.labelAlign != 'top')) {
29231             if (typeof this.labelWidth == 'number') {
29232                 labelWidth = this.labelWidth
29233             }
29234             this.padWidth =  20 + labelWidth;
29235             
29236         }
29237         
29238         Roo.form.Column.superclass.onRender.call(this, ct, position);
29239         if(this.width){
29240             this.el.setWidth(this.width);
29241         }
29242         if(this.height){
29243             this.el.setHeight(this.height);
29244         }
29245     },
29246     
29247     // private
29248     renderField : function(f){
29249         f.fieldEl = this.fieldTpl.append(this.el, [
29250                f.id, f.fieldLabel,
29251                f.labelStyle||this.labelStyle||'',
29252                this.elementStyle||'',
29253                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
29254                f.itemCls||this.itemCls||'',
29255                f.width ? f.width + this.padWidth : 160 + this.padWidth
29256        ],true);
29257     }
29258 });
29259  
29260
29261 /**
29262  * @class Roo.form.FieldSet
29263  * @extends Roo.form.Layout
29264  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
29265  * @constructor
29266  * @param {Object} config Configuration options
29267  */
29268 Roo.form.FieldSet = function(config){
29269     Roo.form.FieldSet.superclass.constructor.call(this, config);
29270 };
29271
29272 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
29273     /**
29274      * @cfg {String} legend
29275      * The text to display as the legend for the FieldSet (defaults to '')
29276      */
29277     /**
29278      * @cfg {String/Object} autoCreate
29279      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
29280      */
29281
29282     // private
29283     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
29284
29285     // private
29286     onRender : function(ct, position){
29287         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
29288         if(this.legend){
29289             this.setLegend(this.legend);
29290         }
29291     },
29292
29293     // private
29294     setLegend : function(text){
29295         if(this.rendered){
29296             this.el.child('legend').update(text);
29297         }
29298     }
29299 });/*
29300  * Based on:
29301  * Ext JS Library 1.1.1
29302  * Copyright(c) 2006-2007, Ext JS, LLC.
29303  *
29304  * Originally Released Under LGPL - original licence link has changed is not relivant.
29305  *
29306  * Fork - LGPL
29307  * <script type="text/javascript">
29308  */
29309 /**
29310  * @class Roo.form.VTypes
29311  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
29312  * @singleton
29313  */
29314 Roo.form.VTypes = function(){
29315     // closure these in so they are only created once.
29316     var alpha = /^[a-zA-Z_]+$/;
29317     var alphanum = /^[a-zA-Z0-9_]+$/;
29318     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
29319     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
29320
29321     // All these messages and functions are configurable
29322     return {
29323         /**
29324          * The function used to validate email addresses
29325          * @param {String} value The email address
29326          */
29327         'email' : function(v){
29328             return email.test(v);
29329         },
29330         /**
29331          * The error text to display when the email validation function returns false
29332          * @type String
29333          */
29334         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
29335         /**
29336          * The keystroke filter mask to be applied on email input
29337          * @type RegExp
29338          */
29339         'emailMask' : /[a-z0-9_\.\-@]/i,
29340
29341         /**
29342          * The function used to validate URLs
29343          * @param {String} value The URL
29344          */
29345         'url' : function(v){
29346             return url.test(v);
29347         },
29348         /**
29349          * The error text to display when the url validation function returns false
29350          * @type String
29351          */
29352         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
29353         
29354         /**
29355          * The function used to validate alpha values
29356          * @param {String} value The value
29357          */
29358         'alpha' : function(v){
29359             return alpha.test(v);
29360         },
29361         /**
29362          * The error text to display when the alpha validation function returns false
29363          * @type String
29364          */
29365         'alphaText' : 'This field should only contain letters and _',
29366         /**
29367          * The keystroke filter mask to be applied on alpha input
29368          * @type RegExp
29369          */
29370         'alphaMask' : /[a-z_]/i,
29371
29372         /**
29373          * The function used to validate alphanumeric values
29374          * @param {String} value The value
29375          */
29376         'alphanum' : function(v){
29377             return alphanum.test(v);
29378         },
29379         /**
29380          * The error text to display when the alphanumeric validation function returns false
29381          * @type String
29382          */
29383         'alphanumText' : 'This field should only contain letters, numbers and _',
29384         /**
29385          * The keystroke filter mask to be applied on alphanumeric input
29386          * @type RegExp
29387          */
29388         'alphanumMask' : /[a-z0-9_]/i
29389     };
29390 }();//<script type="text/javascript">
29391
29392 /**
29393  * @class Roo.form.FCKeditor
29394  * @extends Roo.form.TextArea
29395  * Wrapper around the FCKEditor http://www.fckeditor.net
29396  * @constructor
29397  * Creates a new FCKeditor
29398  * @param {Object} config Configuration options
29399  */
29400 Roo.form.FCKeditor = function(config){
29401     Roo.form.FCKeditor.superclass.constructor.call(this, config);
29402     this.addEvents({
29403          /**
29404          * @event editorinit
29405          * Fired when the editor is initialized - you can add extra handlers here..
29406          * @param {FCKeditor} this
29407          * @param {Object} the FCK object.
29408          */
29409         editorinit : true
29410     });
29411     
29412     
29413 };
29414 Roo.form.FCKeditor.editors = { };
29415 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
29416 {
29417     //defaultAutoCreate : {
29418     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
29419     //},
29420     // private
29421     /**
29422      * @cfg {Object} fck options - see fck manual for details.
29423      */
29424     fckconfig : false,
29425     
29426     /**
29427      * @cfg {Object} fck toolbar set (Basic or Default)
29428      */
29429     toolbarSet : 'Basic',
29430     /**
29431      * @cfg {Object} fck BasePath
29432      */ 
29433     basePath : '/fckeditor/',
29434     
29435     
29436     frame : false,
29437     
29438     value : '',
29439     
29440    
29441     onRender : function(ct, position)
29442     {
29443         if(!this.el){
29444             this.defaultAutoCreate = {
29445                 tag: "textarea",
29446                 style:"width:300px;height:60px;",
29447                 autocomplete: "off"
29448             };
29449         }
29450         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
29451         /*
29452         if(this.grow){
29453             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
29454             if(this.preventScrollbars){
29455                 this.el.setStyle("overflow", "hidden");
29456             }
29457             this.el.setHeight(this.growMin);
29458         }
29459         */
29460         //console.log('onrender' + this.getId() );
29461         Roo.form.FCKeditor.editors[this.getId()] = this;
29462          
29463
29464         this.replaceTextarea() ;
29465         
29466     },
29467     
29468     getEditor : function() {
29469         return this.fckEditor;
29470     },
29471     /**
29472      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
29473      * @param {Mixed} value The value to set
29474      */
29475     
29476     
29477     setValue : function(value)
29478     {
29479         //console.log('setValue: ' + value);
29480         
29481         if(typeof(value) == 'undefined') { // not sure why this is happending...
29482             return;
29483         }
29484         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29485         
29486         //if(!this.el || !this.getEditor()) {
29487         //    this.value = value;
29488             //this.setValue.defer(100,this,[value]);    
29489         //    return;
29490         //} 
29491         
29492         if(!this.getEditor()) {
29493             return;
29494         }
29495         
29496         this.getEditor().SetData(value);
29497         
29498         //
29499
29500     },
29501
29502     /**
29503      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
29504      * @return {Mixed} value The field value
29505      */
29506     getValue : function()
29507     {
29508         
29509         if (this.frame && this.frame.dom.style.display == 'none') {
29510             return Roo.form.FCKeditor.superclass.getValue.call(this);
29511         }
29512         
29513         if(!this.el || !this.getEditor()) {
29514            
29515            // this.getValue.defer(100,this); 
29516             return this.value;
29517         }
29518        
29519         
29520         var value=this.getEditor().GetData();
29521         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
29522         return Roo.form.FCKeditor.superclass.getValue.call(this);
29523         
29524
29525     },
29526
29527     /**
29528      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
29529      * @return {Mixed} value The field value
29530      */
29531     getRawValue : function()
29532     {
29533         if (this.frame && this.frame.dom.style.display == 'none') {
29534             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29535         }
29536         
29537         if(!this.el || !this.getEditor()) {
29538             //this.getRawValue.defer(100,this); 
29539             return this.value;
29540             return;
29541         }
29542         
29543         
29544         
29545         var value=this.getEditor().GetData();
29546         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
29547         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
29548          
29549     },
29550     
29551     setSize : function(w,h) {
29552         
29553         
29554         
29555         //if (this.frame && this.frame.dom.style.display == 'none') {
29556         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29557         //    return;
29558         //}
29559         //if(!this.el || !this.getEditor()) {
29560         //    this.setSize.defer(100,this, [w,h]); 
29561         //    return;
29562         //}
29563         
29564         
29565         
29566         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
29567         
29568         this.frame.dom.setAttribute('width', w);
29569         this.frame.dom.setAttribute('height', h);
29570         this.frame.setSize(w,h);
29571         
29572     },
29573     
29574     toggleSourceEdit : function(value) {
29575         
29576       
29577          
29578         this.el.dom.style.display = value ? '' : 'none';
29579         this.frame.dom.style.display = value ?  'none' : '';
29580         
29581     },
29582     
29583     
29584     focus: function(tag)
29585     {
29586         if (this.frame.dom.style.display == 'none') {
29587             return Roo.form.FCKeditor.superclass.focus.call(this);
29588         }
29589         if(!this.el || !this.getEditor()) {
29590             this.focus.defer(100,this, [tag]); 
29591             return;
29592         }
29593         
29594         
29595         
29596         
29597         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
29598         this.getEditor().Focus();
29599         if (tgs.length) {
29600             if (!this.getEditor().Selection.GetSelection()) {
29601                 this.focus.defer(100,this, [tag]); 
29602                 return;
29603             }
29604             
29605             
29606             var r = this.getEditor().EditorDocument.createRange();
29607             r.setStart(tgs[0],0);
29608             r.setEnd(tgs[0],0);
29609             this.getEditor().Selection.GetSelection().removeAllRanges();
29610             this.getEditor().Selection.GetSelection().addRange(r);
29611             this.getEditor().Focus();
29612         }
29613         
29614     },
29615     
29616     
29617     
29618     replaceTextarea : function()
29619     {
29620         if ( document.getElementById( this.getId() + '___Frame' ) )
29621             return ;
29622         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
29623         //{
29624             // We must check the elements firstly using the Id and then the name.
29625         var oTextarea = document.getElementById( this.getId() );
29626         
29627         var colElementsByName = document.getElementsByName( this.getId() ) ;
29628          
29629         oTextarea.style.display = 'none' ;
29630
29631         if ( oTextarea.tabIndex ) {            
29632             this.TabIndex = oTextarea.tabIndex ;
29633         }
29634         
29635         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
29636         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
29637         this.frame = Roo.get(this.getId() + '___Frame')
29638     },
29639     
29640     _getConfigHtml : function()
29641     {
29642         var sConfig = '' ;
29643
29644         for ( var o in this.fckconfig ) {
29645             sConfig += sConfig.length > 0  ? '&amp;' : '';
29646             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
29647         }
29648
29649         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
29650     },
29651     
29652     
29653     _getIFrameHtml : function()
29654     {
29655         var sFile = 'fckeditor.html' ;
29656         /* no idea what this is about..
29657         try
29658         {
29659             if ( (/fcksource=true/i).test( window.top.location.search ) )
29660                 sFile = 'fckeditor.original.html' ;
29661         }
29662         catch (e) { 
29663         */
29664
29665         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
29666         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
29667         
29668         
29669         var html = '<iframe id="' + this.getId() +
29670             '___Frame" src="' + sLink +
29671             '" width="' + this.width +
29672             '" height="' + this.height + '"' +
29673             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
29674             ' frameborder="0" scrolling="no"></iframe>' ;
29675
29676         return html ;
29677     },
29678     
29679     _insertHtmlBefore : function( html, element )
29680     {
29681         if ( element.insertAdjacentHTML )       {
29682             // IE
29683             element.insertAdjacentHTML( 'beforeBegin', html ) ;
29684         } else { // Gecko
29685             var oRange = document.createRange() ;
29686             oRange.setStartBefore( element ) ;
29687             var oFragment = oRange.createContextualFragment( html );
29688             element.parentNode.insertBefore( oFragment, element ) ;
29689         }
29690     }
29691     
29692     
29693   
29694     
29695     
29696     
29697     
29698
29699 });
29700
29701 //Roo.reg('fckeditor', Roo.form.FCKeditor);
29702
29703 function FCKeditor_OnComplete(editorInstance){
29704     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
29705     f.fckEditor = editorInstance;
29706     //console.log("loaded");
29707     f.fireEvent('editorinit', f, editorInstance);
29708
29709   
29710
29711  
29712
29713
29714
29715
29716
29717
29718
29719
29720
29721
29722
29723
29724
29725
29726
29727 //<script type="text/javascript">
29728 /**
29729  * @class Roo.form.GridField
29730  * @extends Roo.form.Field
29731  * Embed a grid (or editable grid into a form)
29732  * STATUS ALPHA
29733  * 
29734  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
29735  * it needs 
29736  * xgrid.store = Roo.data.Store
29737  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
29738  * xgrid.store.reader = Roo.data.JsonReader 
29739  * 
29740  * 
29741  * @constructor
29742  * Creates a new GridField
29743  * @param {Object} config Configuration options
29744  */
29745 Roo.form.GridField = function(config){
29746     Roo.form.GridField.superclass.constructor.call(this, config);
29747      
29748 };
29749
29750 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
29751     /**
29752      * @cfg {Number} width  - used to restrict width of grid..
29753      */
29754     width : 100,
29755     /**
29756      * @cfg {Number} height - used to restrict height of grid..
29757      */
29758     height : 50,
29759      /**
29760      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
29761          * 
29762          *}
29763      */
29764     xgrid : false, 
29765     /**
29766      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29767      * {tag: "input", type: "checkbox", autocomplete: "off"})
29768      */
29769    // defaultAutoCreate : { tag: 'div' },
29770     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29771     /**
29772      * @cfg {String} addTitle Text to include for adding a title.
29773      */
29774     addTitle : false,
29775     //
29776     onResize : function(){
29777         Roo.form.Field.superclass.onResize.apply(this, arguments);
29778     },
29779
29780     initEvents : function(){
29781         // Roo.form.Checkbox.superclass.initEvents.call(this);
29782         // has no events...
29783        
29784     },
29785
29786
29787     getResizeEl : function(){
29788         return this.wrap;
29789     },
29790
29791     getPositionEl : function(){
29792         return this.wrap;
29793     },
29794
29795     // private
29796     onRender : function(ct, position){
29797         
29798         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
29799         var style = this.style;
29800         delete this.style;
29801         
29802         Roo.form.GridField.superclass.onRender.call(this, ct, position);
29803         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
29804         this.viewEl = this.wrap.createChild({ tag: 'div' });
29805         if (style) {
29806             this.viewEl.applyStyles(style);
29807         }
29808         if (this.width) {
29809             this.viewEl.setWidth(this.width);
29810         }
29811         if (this.height) {
29812             this.viewEl.setHeight(this.height);
29813         }
29814         //if(this.inputValue !== undefined){
29815         //this.setValue(this.value);
29816         
29817         
29818         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
29819         
29820         
29821         this.grid.render();
29822         this.grid.getDataSource().on('remove', this.refreshValue, this);
29823         this.grid.getDataSource().on('update', this.refreshValue, this);
29824         this.grid.on('afteredit', this.refreshValue, this);
29825  
29826     },
29827      
29828     
29829     /**
29830      * Sets the value of the item. 
29831      * @param {String} either an object  or a string..
29832      */
29833     setValue : function(v){
29834         //this.value = v;
29835         v = v || []; // empty set..
29836         // this does not seem smart - it really only affects memoryproxy grids..
29837         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
29838             var ds = this.grid.getDataSource();
29839             // assumes a json reader..
29840             var data = {}
29841             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
29842             ds.loadData( data);
29843         }
29844         // clear selection so it does not get stale.
29845         if (this.grid.sm) { 
29846             this.grid.sm.clearSelections();
29847         }
29848         
29849         Roo.form.GridField.superclass.setValue.call(this, v);
29850         this.refreshValue();
29851         // should load data in the grid really....
29852     },
29853     
29854     // private
29855     refreshValue: function() {
29856          var val = [];
29857         this.grid.getDataSource().each(function(r) {
29858             val.push(r.data);
29859         });
29860         this.el.dom.value = Roo.encode(val);
29861     }
29862     
29863      
29864     
29865     
29866 });/*
29867  * Based on:
29868  * Ext JS Library 1.1.1
29869  * Copyright(c) 2006-2007, Ext JS, LLC.
29870  *
29871  * Originally Released Under LGPL - original licence link has changed is not relivant.
29872  *
29873  * Fork - LGPL
29874  * <script type="text/javascript">
29875  */
29876 /**
29877  * @class Roo.form.DisplayField
29878  * @extends Roo.form.Field
29879  * A generic Field to display non-editable data.
29880  * @constructor
29881  * Creates a new Display Field item.
29882  * @param {Object} config Configuration options
29883  */
29884 Roo.form.DisplayField = function(config){
29885     Roo.form.DisplayField.superclass.constructor.call(this, config);
29886     
29887 };
29888
29889 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
29890     inputType:      'hidden',
29891     allowBlank:     true,
29892     readOnly:         true,
29893     
29894  
29895     /**
29896      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
29897      */
29898     focusClass : undefined,
29899     /**
29900      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
29901      */
29902     fieldClass: 'x-form-field',
29903     
29904      /**
29905      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
29906      */
29907     valueRenderer: undefined,
29908     
29909     width: 100,
29910     /**
29911      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
29912      * {tag: "input", type: "checkbox", autocomplete: "off"})
29913      */
29914      
29915  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
29916
29917     onResize : function(){
29918         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
29919         
29920     },
29921
29922     initEvents : function(){
29923         // Roo.form.Checkbox.superclass.initEvents.call(this);
29924         // has no events...
29925        
29926     },
29927
29928
29929     getResizeEl : function(){
29930         return this.wrap;
29931     },
29932
29933     getPositionEl : function(){
29934         return this.wrap;
29935     },
29936
29937     // private
29938     onRender : function(ct, position){
29939         
29940         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
29941         //if(this.inputValue !== undefined){
29942         this.wrap = this.el.wrap();
29943         
29944         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
29945         
29946         if (this.bodyStyle) {
29947             this.viewEl.applyStyles(this.bodyStyle);
29948         }
29949         //this.viewEl.setStyle('padding', '2px');
29950         
29951         this.setValue(this.value);
29952         
29953     },
29954 /*
29955     // private
29956     initValue : Roo.emptyFn,
29957
29958   */
29959
29960         // private
29961     onClick : function(){
29962         
29963     },
29964
29965     /**
29966      * Sets the checked state of the checkbox.
29967      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
29968      */
29969     setValue : function(v){
29970         this.value = v;
29971         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
29972         // this might be called before we have a dom element..
29973         if (!this.viewEl) {
29974             return;
29975         }
29976         this.viewEl.dom.innerHTML = html;
29977         Roo.form.DisplayField.superclass.setValue.call(this, v);
29978
29979     }
29980 });/*
29981  * 
29982  * Licence- LGPL
29983  * 
29984  */
29985
29986 /**
29987  * @class Roo.form.DayPicker
29988  * @extends Roo.form.Field
29989  * A Day picker show [M] [T] [W] ....
29990  * @constructor
29991  * Creates a new Day Picker
29992  * @param {Object} config Configuration options
29993  */
29994 Roo.form.DayPicker= function(config){
29995     Roo.form.DayPicker.superclass.constructor.call(this, config);
29996      
29997 };
29998
29999 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
30000     /**
30001      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
30002      */
30003     focusClass : undefined,
30004     /**
30005      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
30006      */
30007     fieldClass: "x-form-field",
30008    
30009     /**
30010      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
30011      * {tag: "input", type: "checkbox", autocomplete: "off"})
30012      */
30013     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
30014     
30015    
30016     actionMode : 'viewEl', 
30017     //
30018     // private
30019  
30020     inputType : 'hidden',
30021     
30022      
30023     inputElement: false, // real input element?
30024     basedOn: false, // ????
30025     
30026     isFormField: true, // not sure where this is needed!!!!
30027
30028     onResize : function(){
30029         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
30030         if(!this.boxLabel){
30031             this.el.alignTo(this.wrap, 'c-c');
30032         }
30033     },
30034
30035     initEvents : function(){
30036         Roo.form.Checkbox.superclass.initEvents.call(this);
30037         this.el.on("click", this.onClick,  this);
30038         this.el.on("change", this.onClick,  this);
30039     },
30040
30041
30042     getResizeEl : function(){
30043         return this.wrap;
30044     },
30045
30046     getPositionEl : function(){
30047         return this.wrap;
30048     },
30049
30050     
30051     // private
30052     onRender : function(ct, position){
30053         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
30054        
30055         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
30056         
30057         var r1 = '<table><tr>';
30058         var r2 = '<tr class="x-form-daypick-icons">';
30059         for (var i=0; i < 7; i++) {
30060             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
30061             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
30062         }
30063         
30064         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
30065         viewEl.select('img').on('click', this.onClick, this);
30066         this.viewEl = viewEl;   
30067         
30068         
30069         // this will not work on Chrome!!!
30070         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
30071         this.el.on('propertychange', this.setFromHidden,  this);  //ie
30072         
30073         
30074           
30075
30076     },
30077
30078     // private
30079     initValue : Roo.emptyFn,
30080
30081     /**
30082      * Returns the checked state of the checkbox.
30083      * @return {Boolean} True if checked, else false
30084      */
30085     getValue : function(){
30086         return this.el.dom.value;
30087         
30088     },
30089
30090         // private
30091     onClick : function(e){ 
30092         //this.setChecked(!this.checked);
30093         Roo.get(e.target).toggleClass('x-menu-item-checked');
30094         this.refreshValue();
30095         //if(this.el.dom.checked != this.checked){
30096         //    this.setValue(this.el.dom.checked);
30097        // }
30098     },
30099     
30100     // private
30101     refreshValue : function()
30102     {
30103         var val = '';
30104         this.viewEl.select('img',true).each(function(e,i,n)  {
30105             val += e.is(".x-menu-item-checked") ? String(n) : '';
30106         });
30107         this.setValue(val, true);
30108     },
30109
30110     /**
30111      * Sets the checked state of the checkbox.
30112      * On is always based on a string comparison between inputValue and the param.
30113      * @param {Boolean/String} value - the value to set 
30114      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
30115      */
30116     setValue : function(v,suppressEvent){
30117         if (!this.el.dom) {
30118             return;
30119         }
30120         var old = this.el.dom.value ;
30121         this.el.dom.value = v;
30122         if (suppressEvent) {
30123             return ;
30124         }
30125          
30126         // update display..
30127         this.viewEl.select('img',true).each(function(e,i,n)  {
30128             
30129             var on = e.is(".x-menu-item-checked");
30130             var newv = v.indexOf(String(n)) > -1;
30131             if (on != newv) {
30132                 e.toggleClass('x-menu-item-checked');
30133             }
30134             
30135         });
30136         
30137         
30138         this.fireEvent('change', this, v, old);
30139         
30140         
30141     },
30142    
30143     // handle setting of hidden value by some other method!!?!?
30144     setFromHidden: function()
30145     {
30146         if(!this.el){
30147             return;
30148         }
30149         //console.log("SET FROM HIDDEN");
30150         //alert('setFrom hidden');
30151         this.setValue(this.el.dom.value);
30152     },
30153     
30154     onDestroy : function()
30155     {
30156         if(this.viewEl){
30157             Roo.get(this.viewEl).remove();
30158         }
30159          
30160         Roo.form.DayPicker.superclass.onDestroy.call(this);
30161     }
30162
30163 });/*
30164  * RooJS Library 1.1.1
30165  * Copyright(c) 2008-2011  Alan Knowles
30166  *
30167  * License - LGPL
30168  */
30169  
30170
30171 /**
30172  * @class Roo.form.ComboCheck
30173  * @extends Roo.form.ComboBox
30174  * A combobox for multiple select items.
30175  *
30176  * FIXME - could do with a reset button..
30177  * 
30178  * @constructor
30179  * Create a new ComboCheck
30180  * @param {Object} config Configuration options
30181  */
30182 Roo.form.ComboCheck = function(config){
30183     Roo.form.ComboCheck.superclass.constructor.call(this, config);
30184     // should verify some data...
30185     // like
30186     // hiddenName = required..
30187     // displayField = required
30188     // valudField == required
30189     var req= [ 'hiddenName', 'displayField', 'valueField' ];
30190     var _t = this;
30191     Roo.each(req, function(e) {
30192         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
30193             throw "Roo.form.ComboCheck : missing value for: " + e;
30194         }
30195     });
30196     
30197     
30198 };
30199
30200 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
30201      
30202      
30203     editable : false,
30204      
30205     selectedClass: 'x-menu-item-checked', 
30206     
30207     // private
30208     onRender : function(ct, position){
30209         var _t = this;
30210         
30211         
30212         
30213         if(!this.tpl){
30214             var cls = 'x-combo-list';
30215
30216             
30217             this.tpl =  new Roo.Template({
30218                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
30219                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
30220                    '<span>{' + this.displayField + '}</span>' +
30221                     '</div>' 
30222                 
30223             });
30224         }
30225  
30226         
30227         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
30228         this.view.singleSelect = false;
30229         this.view.multiSelect = true;
30230         this.view.toggleSelect = true;
30231         this.pageTb.add(new Roo.Toolbar.Fill(), {
30232             
30233             text: 'Done',
30234             handler: function()
30235             {
30236                 _t.collapse();
30237             }
30238         });
30239     },
30240     
30241     onViewOver : function(e, t){
30242         // do nothing...
30243         return;
30244         
30245     },
30246     
30247     onViewClick : function(doFocus,index){
30248         return;
30249         
30250     },
30251     select: function () {
30252         //Roo.log("SELECT CALLED");
30253     },
30254      
30255     selectByValue : function(xv, scrollIntoView){
30256         var ar = this.getValueArray();
30257         var sels = [];
30258         
30259         Roo.each(ar, function(v) {
30260             if(v === undefined || v === null){
30261                 return;
30262             }
30263             var r = this.findRecord(this.valueField, v);
30264             if(r){
30265                 sels.push(this.store.indexOf(r))
30266                 
30267             }
30268         },this);
30269         this.view.select(sels);
30270         return false;
30271     },
30272     
30273     
30274     
30275     onSelect : function(record, index){
30276        // Roo.log("onselect Called");
30277        // this is only called by the clear button now..
30278         this.view.clearSelections();
30279         this.setValue('[]');
30280         if (this.value != this.valueBefore) {
30281             this.fireEvent('change', this, this.value, this.valueBefore);
30282         }
30283     },
30284     getValueArray : function()
30285     {
30286         var ar = [] ;
30287         
30288         try {
30289             //Roo.log(this.value);
30290             if (typeof(this.value) == 'undefined') {
30291                 return [];
30292             }
30293             var ar = Roo.decode(this.value);
30294             return  ar instanceof Array ? ar : []; //?? valid?
30295             
30296         } catch(e) {
30297             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
30298             return [];
30299         }
30300          
30301     },
30302     expand : function ()
30303     {
30304         Roo.form.ComboCheck.superclass.expand.call(this);
30305         this.valueBefore = this.value;
30306         
30307
30308     },
30309     
30310     collapse : function(){
30311         Roo.form.ComboCheck.superclass.collapse.call(this);
30312         var sl = this.view.getSelectedIndexes();
30313         var st = this.store;
30314         var nv = [];
30315         var tv = [];
30316         var r;
30317         Roo.each(sl, function(i) {
30318             r = st.getAt(i);
30319             nv.push(r.get(this.valueField));
30320         },this);
30321         this.setValue(Roo.encode(nv));
30322         if (this.value != this.valueBefore) {
30323
30324             this.fireEvent('change', this, this.value, this.valueBefore);
30325         }
30326         
30327     },
30328     
30329     setValue : function(v){
30330         // Roo.log(v);
30331         this.value = v;
30332         
30333         var vals = this.getValueArray();
30334         var tv = [];
30335         Roo.each(vals, function(k) {
30336             var r = this.findRecord(this.valueField, k);
30337             if(r){
30338                 tv.push(r.data[this.displayField]);
30339             }else if(this.valueNotFoundText !== undefined){
30340                 tv.push( this.valueNotFoundText );
30341             }
30342         },this);
30343        // Roo.log(tv);
30344         
30345         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
30346         this.hiddenField.value = v;
30347         this.value = v;
30348     }
30349     
30350 });//<script type="text/javasscript">
30351  
30352
30353 /**
30354  * @class Roo.DDView
30355  * A DnD enabled version of Roo.View.
30356  * @param {Element/String} container The Element in which to create the View.
30357  * @param {String} tpl The template string used to create the markup for each element of the View
30358  * @param {Object} config The configuration properties. These include all the config options of
30359  * {@link Roo.View} plus some specific to this class.<br>
30360  * <p>
30361  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
30362  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
30363  * <p>
30364  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
30365 .x-view-drag-insert-above {
30366         border-top:1px dotted #3366cc;
30367 }
30368 .x-view-drag-insert-below {
30369         border-bottom:1px dotted #3366cc;
30370 }
30371 </code></pre>
30372  * 
30373  */
30374  
30375 Roo.DDView = function(container, tpl, config) {
30376     Roo.DDView.superclass.constructor.apply(this, arguments);
30377     this.getEl().setStyle("outline", "0px none");
30378     this.getEl().unselectable();
30379     if (this.dragGroup) {
30380                 this.setDraggable(this.dragGroup.split(","));
30381     }
30382     if (this.dropGroup) {
30383                 this.setDroppable(this.dropGroup.split(","));
30384     }
30385     if (this.deletable) {
30386         this.setDeletable();
30387     }
30388     this.isDirtyFlag = false;
30389         this.addEvents({
30390                 "drop" : true
30391         });
30392 };
30393
30394 Roo.extend(Roo.DDView, Roo.View, {
30395 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
30396 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
30397 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
30398 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
30399
30400         isFormField: true,
30401
30402         reset: Roo.emptyFn,
30403         
30404         clearInvalid: Roo.form.Field.prototype.clearInvalid,
30405
30406         validate: function() {
30407                 return true;
30408         },
30409         
30410         destroy: function() {
30411                 this.purgeListeners();
30412                 this.getEl.removeAllListeners();
30413                 this.getEl().remove();
30414                 if (this.dragZone) {
30415                         if (this.dragZone.destroy) {
30416                                 this.dragZone.destroy();
30417                         }
30418                 }
30419                 if (this.dropZone) {
30420                         if (this.dropZone.destroy) {
30421                                 this.dropZone.destroy();
30422                         }
30423                 }
30424         },
30425
30426 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
30427         getName: function() {
30428                 return this.name;
30429         },
30430
30431 /**     Loads the View from a JSON string representing the Records to put into the Store. */
30432         setValue: function(v) {
30433                 if (!this.store) {
30434                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
30435                 }
30436                 var data = {};
30437                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
30438                 this.store.proxy = new Roo.data.MemoryProxy(data);
30439                 this.store.load();
30440         },
30441
30442 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
30443         getValue: function() {
30444                 var result = '(';
30445                 this.store.each(function(rec) {
30446                         result += rec.id + ',';
30447                 });
30448                 return result.substr(0, result.length - 1) + ')';
30449         },
30450         
30451         getIds: function() {
30452                 var i = 0, result = new Array(this.store.getCount());
30453                 this.store.each(function(rec) {
30454                         result[i++] = rec.id;
30455                 });
30456                 return result;
30457         },
30458         
30459         isDirty: function() {
30460                 return this.isDirtyFlag;
30461         },
30462
30463 /**
30464  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
30465  *      whole Element becomes the target, and this causes the drop gesture to append.
30466  */
30467     getTargetFromEvent : function(e) {
30468                 var target = e.getTarget();
30469                 while ((target !== null) && (target.parentNode != this.el.dom)) {
30470                 target = target.parentNode;
30471                 }
30472                 if (!target) {
30473                         target = this.el.dom.lastChild || this.el.dom;
30474                 }
30475                 return target;
30476     },
30477
30478 /**
30479  *      Create the drag data which consists of an object which has the property "ddel" as
30480  *      the drag proxy element. 
30481  */
30482     getDragData : function(e) {
30483         var target = this.findItemFromChild(e.getTarget());
30484                 if(target) {
30485                         this.handleSelection(e);
30486                         var selNodes = this.getSelectedNodes();
30487             var dragData = {
30488                 source: this,
30489                 copy: this.copy || (this.allowCopy && e.ctrlKey),
30490                 nodes: selNodes,
30491                 records: []
30492                         };
30493                         var selectedIndices = this.getSelectedIndexes();
30494                         for (var i = 0; i < selectedIndices.length; i++) {
30495                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
30496                         }
30497                         if (selNodes.length == 1) {
30498                                 dragData.ddel = target.cloneNode(true); // the div element
30499                         } else {
30500                                 var div = document.createElement('div'); // create the multi element drag "ghost"
30501                                 div.className = 'multi-proxy';
30502                                 for (var i = 0, len = selNodes.length; i < len; i++) {
30503                                         div.appendChild(selNodes[i].cloneNode(true));
30504                                 }
30505                                 dragData.ddel = div;
30506                         }
30507             //console.log(dragData)
30508             //console.log(dragData.ddel.innerHTML)
30509                         return dragData;
30510                 }
30511         //console.log('nodragData')
30512                 return false;
30513     },
30514     
30515 /**     Specify to which ddGroup items in this DDView may be dragged. */
30516     setDraggable: function(ddGroup) {
30517         if (ddGroup instanceof Array) {
30518                 Roo.each(ddGroup, this.setDraggable, this);
30519                 return;
30520         }
30521         if (this.dragZone) {
30522                 this.dragZone.addToGroup(ddGroup);
30523         } else {
30524                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
30525                                 containerScroll: true,
30526                                 ddGroup: ddGroup 
30527
30528                         });
30529 //                      Draggability implies selection. DragZone's mousedown selects the element.
30530                         if (!this.multiSelect) { this.singleSelect = true; }
30531
30532 //                      Wire the DragZone's handlers up to methods in *this*
30533                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
30534                 }
30535     },
30536
30537 /**     Specify from which ddGroup this DDView accepts drops. */
30538     setDroppable: function(ddGroup) {
30539         if (ddGroup instanceof Array) {
30540                 Roo.each(ddGroup, this.setDroppable, this);
30541                 return;
30542         }
30543         if (this.dropZone) {
30544                 this.dropZone.addToGroup(ddGroup);
30545         } else {
30546                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
30547                                 containerScroll: true,
30548                                 ddGroup: ddGroup
30549                         });
30550
30551 //                      Wire the DropZone's handlers up to methods in *this*
30552                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
30553                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
30554                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
30555                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
30556                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
30557                 }
30558     },
30559
30560 /**     Decide whether to drop above or below a View node. */
30561     getDropPoint : function(e, n, dd){
30562         if (n == this.el.dom) { return "above"; }
30563                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
30564                 var c = t + (b - t) / 2;
30565                 var y = Roo.lib.Event.getPageY(e);
30566                 if(y <= c) {
30567                         return "above";
30568                 }else{
30569                         return "below";
30570                 }
30571     },
30572
30573     onNodeEnter : function(n, dd, e, data){
30574                 return false;
30575     },
30576     
30577     onNodeOver : function(n, dd, e, data){
30578                 var pt = this.getDropPoint(e, n, dd);
30579                 // set the insert point style on the target node
30580                 var dragElClass = this.dropNotAllowed;
30581                 if (pt) {
30582                         var targetElClass;
30583                         if (pt == "above"){
30584                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
30585                                 targetElClass = "x-view-drag-insert-above";
30586                         } else {
30587                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
30588                                 targetElClass = "x-view-drag-insert-below";
30589                         }
30590                         if (this.lastInsertClass != targetElClass){
30591                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
30592                                 this.lastInsertClass = targetElClass;
30593                         }
30594                 }
30595                 return dragElClass;
30596         },
30597
30598     onNodeOut : function(n, dd, e, data){
30599                 this.removeDropIndicators(n);
30600     },
30601
30602     onNodeDrop : function(n, dd, e, data){
30603         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
30604                 return false;
30605         }
30606         var pt = this.getDropPoint(e, n, dd);
30607                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
30608                 if (pt == "below") { insertAt++; }
30609                 for (var i = 0; i < data.records.length; i++) {
30610                         var r = data.records[i];
30611                         var dup = this.store.getById(r.id);
30612                         if (dup && (dd != this.dragZone)) {
30613                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
30614                         } else {
30615                                 if (data.copy) {
30616                                         this.store.insert(insertAt++, r.copy());
30617                                 } else {
30618                                         data.source.isDirtyFlag = true;
30619                                         r.store.remove(r);
30620                                         this.store.insert(insertAt++, r);
30621                                 }
30622                                 this.isDirtyFlag = true;
30623                         }
30624                 }
30625                 this.dragZone.cachedTarget = null;
30626                 return true;
30627     },
30628
30629     removeDropIndicators : function(n){
30630                 if(n){
30631                         Roo.fly(n).removeClass([
30632                                 "x-view-drag-insert-above",
30633                                 "x-view-drag-insert-below"]);
30634                         this.lastInsertClass = "_noclass";
30635                 }
30636     },
30637
30638 /**
30639  *      Utility method. Add a delete option to the DDView's context menu.
30640  *      @param {String} imageUrl The URL of the "delete" icon image.
30641  */
30642         setDeletable: function(imageUrl) {
30643                 if (!this.singleSelect && !this.multiSelect) {
30644                         this.singleSelect = true;
30645                 }
30646                 var c = this.getContextMenu();
30647                 this.contextMenu.on("itemclick", function(item) {
30648                         switch (item.id) {
30649                                 case "delete":
30650                                         this.remove(this.getSelectedIndexes());
30651                                         break;
30652                         }
30653                 }, this);
30654                 this.contextMenu.add({
30655                         icon: imageUrl,
30656                         id: "delete",
30657                         text: 'Delete'
30658                 });
30659         },
30660         
30661 /**     Return the context menu for this DDView. */
30662         getContextMenu: function() {
30663                 if (!this.contextMenu) {
30664 //                      Create the View's context menu
30665                         this.contextMenu = new Roo.menu.Menu({
30666                                 id: this.id + "-contextmenu"
30667                         });
30668                         this.el.on("contextmenu", this.showContextMenu, this);
30669                 }
30670                 return this.contextMenu;
30671         },
30672         
30673         disableContextMenu: function() {
30674                 if (this.contextMenu) {
30675                         this.el.un("contextmenu", this.showContextMenu, this);
30676                 }
30677         },
30678
30679         showContextMenu: function(e, item) {
30680         item = this.findItemFromChild(e.getTarget());
30681                 if (item) {
30682                         e.stopEvent();
30683                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
30684                         this.contextMenu.showAt(e.getXY());
30685             }
30686     },
30687
30688 /**
30689  *      Remove {@link Roo.data.Record}s at the specified indices.
30690  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
30691  */
30692     remove: function(selectedIndices) {
30693                 selectedIndices = [].concat(selectedIndices);
30694                 for (var i = 0; i < selectedIndices.length; i++) {
30695                         var rec = this.store.getAt(selectedIndices[i]);
30696                         this.store.remove(rec);
30697                 }
30698     },
30699
30700 /**
30701  *      Double click fires the event, but also, if this is draggable, and there is only one other
30702  *      related DropZone, it transfers the selected node.
30703  */
30704     onDblClick : function(e){
30705         var item = this.findItemFromChild(e.getTarget());
30706         if(item){
30707             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
30708                 return false;
30709             }
30710             if (this.dragGroup) {
30711                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
30712                     while (targets.indexOf(this.dropZone) > -1) {
30713                             targets.remove(this.dropZone);
30714                                 }
30715                     if (targets.length == 1) {
30716                                         this.dragZone.cachedTarget = null;
30717                         var el = Roo.get(targets[0].getEl());
30718                         var box = el.getBox(true);
30719                         targets[0].onNodeDrop(el.dom, {
30720                                 target: el.dom,
30721                                 xy: [box.x, box.y + box.height - 1]
30722                         }, null, this.getDragData(e));
30723                     }
30724                 }
30725         }
30726     },
30727     
30728     handleSelection: function(e) {
30729                 this.dragZone.cachedTarget = null;
30730         var item = this.findItemFromChild(e.getTarget());
30731         if (!item) {
30732                 this.clearSelections(true);
30733                 return;
30734         }
30735                 if (item && (this.multiSelect || this.singleSelect)){
30736                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
30737                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
30738                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
30739                                 this.unselect(item);
30740                         } else {
30741                                 this.select(item, this.multiSelect && e.ctrlKey);
30742                                 this.lastSelection = item;
30743                         }
30744                 }
30745     },
30746
30747     onItemClick : function(item, index, e){
30748                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
30749                         return false;
30750                 }
30751                 return true;
30752     },
30753
30754     unselect : function(nodeInfo, suppressEvent){
30755                 var node = this.getNode(nodeInfo);
30756                 if(node && this.isSelected(node)){
30757                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
30758                                 Roo.fly(node).removeClass(this.selectedClass);
30759                                 this.selections.remove(node);
30760                                 if(!suppressEvent){
30761                                         this.fireEvent("selectionchange", this, this.selections);
30762                                 }
30763                         }
30764                 }
30765     }
30766 });
30767 /*
30768  * Based on:
30769  * Ext JS Library 1.1.1
30770  * Copyright(c) 2006-2007, Ext JS, LLC.
30771  *
30772  * Originally Released Under LGPL - original licence link has changed is not relivant.
30773  *
30774  * Fork - LGPL
30775  * <script type="text/javascript">
30776  */
30777  
30778 /**
30779  * @class Roo.LayoutManager
30780  * @extends Roo.util.Observable
30781  * Base class for layout managers.
30782  */
30783 Roo.LayoutManager = function(container, config){
30784     Roo.LayoutManager.superclass.constructor.call(this);
30785     this.el = Roo.get(container);
30786     // ie scrollbar fix
30787     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
30788         document.body.scroll = "no";
30789     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
30790         this.el.position('relative');
30791     }
30792     this.id = this.el.id;
30793     this.el.addClass("x-layout-container");
30794     /** false to disable window resize monitoring @type Boolean */
30795     this.monitorWindowResize = true;
30796     this.regions = {};
30797     this.addEvents({
30798         /**
30799          * @event layout
30800          * Fires when a layout is performed. 
30801          * @param {Roo.LayoutManager} this
30802          */
30803         "layout" : true,
30804         /**
30805          * @event regionresized
30806          * Fires when the user resizes a region. 
30807          * @param {Roo.LayoutRegion} region The resized region
30808          * @param {Number} newSize The new size (width for east/west, height for north/south)
30809          */
30810         "regionresized" : true,
30811         /**
30812          * @event regioncollapsed
30813          * Fires when a region is collapsed. 
30814          * @param {Roo.LayoutRegion} region The collapsed region
30815          */
30816         "regioncollapsed" : true,
30817         /**
30818          * @event regionexpanded
30819          * Fires when a region is expanded.  
30820          * @param {Roo.LayoutRegion} region The expanded region
30821          */
30822         "regionexpanded" : true
30823     });
30824     this.updating = false;
30825     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
30826 };
30827
30828 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
30829     /**
30830      * Returns true if this layout is currently being updated
30831      * @return {Boolean}
30832      */
30833     isUpdating : function(){
30834         return this.updating; 
30835     },
30836     
30837     /**
30838      * Suspend the LayoutManager from doing auto-layouts while
30839      * making multiple add or remove calls
30840      */
30841     beginUpdate : function(){
30842         this.updating = true;    
30843     },
30844     
30845     /**
30846      * Restore auto-layouts and optionally disable the manager from performing a layout
30847      * @param {Boolean} noLayout true to disable a layout update 
30848      */
30849     endUpdate : function(noLayout){
30850         this.updating = false;
30851         if(!noLayout){
30852             this.layout();
30853         }    
30854     },
30855     
30856     layout: function(){
30857         
30858     },
30859     
30860     onRegionResized : function(region, newSize){
30861         this.fireEvent("regionresized", region, newSize);
30862         this.layout();
30863     },
30864     
30865     onRegionCollapsed : function(region){
30866         this.fireEvent("regioncollapsed", region);
30867     },
30868     
30869     onRegionExpanded : function(region){
30870         this.fireEvent("regionexpanded", region);
30871     },
30872         
30873     /**
30874      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
30875      * performs box-model adjustments.
30876      * @return {Object} The size as an object {width: (the width), height: (the height)}
30877      */
30878     getViewSize : function(){
30879         var size;
30880         if(this.el.dom != document.body){
30881             size = this.el.getSize();
30882         }else{
30883             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
30884         }
30885         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
30886         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30887         return size;
30888     },
30889     
30890     /**
30891      * Returns the Element this layout is bound to.
30892      * @return {Roo.Element}
30893      */
30894     getEl : function(){
30895         return this.el;
30896     },
30897     
30898     /**
30899      * Returns the specified region.
30900      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
30901      * @return {Roo.LayoutRegion}
30902      */
30903     getRegion : function(target){
30904         return this.regions[target.toLowerCase()];
30905     },
30906     
30907     onWindowResize : function(){
30908         if(this.monitorWindowResize){
30909             this.layout();
30910         }
30911     }
30912 });/*
30913  * Based on:
30914  * Ext JS Library 1.1.1
30915  * Copyright(c) 2006-2007, Ext JS, LLC.
30916  *
30917  * Originally Released Under LGPL - original licence link has changed is not relivant.
30918  *
30919  * Fork - LGPL
30920  * <script type="text/javascript">
30921  */
30922 /**
30923  * @class Roo.BorderLayout
30924  * @extends Roo.LayoutManager
30925  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
30926  * please see: <br><br>
30927  * <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>
30928  * <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>
30929  * Example:
30930  <pre><code>
30931  var layout = new Roo.BorderLayout(document.body, {
30932     north: {
30933         initialSize: 25,
30934         titlebar: false
30935     },
30936     west: {
30937         split:true,
30938         initialSize: 200,
30939         minSize: 175,
30940         maxSize: 400,
30941         titlebar: true,
30942         collapsible: true
30943     },
30944     east: {
30945         split:true,
30946         initialSize: 202,
30947         minSize: 175,
30948         maxSize: 400,
30949         titlebar: true,
30950         collapsible: true
30951     },
30952     south: {
30953         split:true,
30954         initialSize: 100,
30955         minSize: 100,
30956         maxSize: 200,
30957         titlebar: true,
30958         collapsible: true
30959     },
30960     center: {
30961         titlebar: true,
30962         autoScroll:true,
30963         resizeTabs: true,
30964         minTabWidth: 50,
30965         preferredTabWidth: 150
30966     }
30967 });
30968
30969 // shorthand
30970 var CP = Roo.ContentPanel;
30971
30972 layout.beginUpdate();
30973 layout.add("north", new CP("north", "North"));
30974 layout.add("south", new CP("south", {title: "South", closable: true}));
30975 layout.add("west", new CP("west", {title: "West"}));
30976 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
30977 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
30978 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
30979 layout.getRegion("center").showPanel("center1");
30980 layout.endUpdate();
30981 </code></pre>
30982
30983 <b>The container the layout is rendered into can be either the body element or any other element.
30984 If it is not the body element, the container needs to either be an absolute positioned element,
30985 or you will need to add "position:relative" to the css of the container.  You will also need to specify
30986 the container size if it is not the body element.</b>
30987
30988 * @constructor
30989 * Create a new BorderLayout
30990 * @param {String/HTMLElement/Element} container The container this layout is bound to
30991 * @param {Object} config Configuration options
30992  */
30993 Roo.BorderLayout = function(container, config){
30994     config = config || {};
30995     Roo.BorderLayout.superclass.constructor.call(this, container, config);
30996     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
30997     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
30998         var target = this.factory.validRegions[i];
30999         if(config[target]){
31000             this.addRegion(target, config[target]);
31001         }
31002     }
31003 };
31004
31005 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
31006     /**
31007      * Creates and adds a new region if it doesn't already exist.
31008      * @param {String} target The target region key (north, south, east, west or center).
31009      * @param {Object} config The regions config object
31010      * @return {BorderLayoutRegion} The new region
31011      */
31012     addRegion : function(target, config){
31013         if(!this.regions[target]){
31014             var r = this.factory.create(target, this, config);
31015             this.bindRegion(target, r);
31016         }
31017         return this.regions[target];
31018     },
31019
31020     // private (kinda)
31021     bindRegion : function(name, r){
31022         this.regions[name] = r;
31023         r.on("visibilitychange", this.layout, this);
31024         r.on("paneladded", this.layout, this);
31025         r.on("panelremoved", this.layout, this);
31026         r.on("invalidated", this.layout, this);
31027         r.on("resized", this.onRegionResized, this);
31028         r.on("collapsed", this.onRegionCollapsed, this);
31029         r.on("expanded", this.onRegionExpanded, this);
31030     },
31031
31032     /**
31033      * Performs a layout update.
31034      */
31035     layout : function(){
31036         if(this.updating) return;
31037         var size = this.getViewSize();
31038         var w = size.width;
31039         var h = size.height;
31040         var centerW = w;
31041         var centerH = h;
31042         var centerY = 0;
31043         var centerX = 0;
31044         //var x = 0, y = 0;
31045
31046         var rs = this.regions;
31047         var north = rs["north"];
31048         var south = rs["south"]; 
31049         var west = rs["west"];
31050         var east = rs["east"];
31051         var center = rs["center"];
31052         //if(this.hideOnLayout){ // not supported anymore
31053             //c.el.setStyle("display", "none");
31054         //}
31055         if(north && north.isVisible()){
31056             var b = north.getBox();
31057             var m = north.getMargins();
31058             b.width = w - (m.left+m.right);
31059             b.x = m.left;
31060             b.y = m.top;
31061             centerY = b.height + b.y + m.bottom;
31062             centerH -= centerY;
31063             north.updateBox(this.safeBox(b));
31064         }
31065         if(south && south.isVisible()){
31066             var b = south.getBox();
31067             var m = south.getMargins();
31068             b.width = w - (m.left+m.right);
31069             b.x = m.left;
31070             var totalHeight = (b.height + m.top + m.bottom);
31071             b.y = h - totalHeight + m.top;
31072             centerH -= totalHeight;
31073             south.updateBox(this.safeBox(b));
31074         }
31075         if(west && west.isVisible()){
31076             var b = west.getBox();
31077             var m = west.getMargins();
31078             b.height = centerH - (m.top+m.bottom);
31079             b.x = m.left;
31080             b.y = centerY + m.top;
31081             var totalWidth = (b.width + m.left + m.right);
31082             centerX += totalWidth;
31083             centerW -= totalWidth;
31084             west.updateBox(this.safeBox(b));
31085         }
31086         if(east && east.isVisible()){
31087             var b = east.getBox();
31088             var m = east.getMargins();
31089             b.height = centerH - (m.top+m.bottom);
31090             var totalWidth = (b.width + m.left + m.right);
31091             b.x = w - totalWidth + m.left;
31092             b.y = centerY + m.top;
31093             centerW -= totalWidth;
31094             east.updateBox(this.safeBox(b));
31095         }
31096         if(center){
31097             var m = center.getMargins();
31098             var centerBox = {
31099                 x: centerX + m.left,
31100                 y: centerY + m.top,
31101                 width: centerW - (m.left+m.right),
31102                 height: centerH - (m.top+m.bottom)
31103             };
31104             //if(this.hideOnLayout){
31105                 //center.el.setStyle("display", "block");
31106             //}
31107             center.updateBox(this.safeBox(centerBox));
31108         }
31109         this.el.repaint();
31110         this.fireEvent("layout", this);
31111     },
31112
31113     // private
31114     safeBox : function(box){
31115         box.width = Math.max(0, box.width);
31116         box.height = Math.max(0, box.height);
31117         return box;
31118     },
31119
31120     /**
31121      * Adds a ContentPanel (or subclass) to this layout.
31122      * @param {String} target The target region key (north, south, east, west or center).
31123      * @param {Roo.ContentPanel} panel The panel to add
31124      * @return {Roo.ContentPanel} The added panel
31125      */
31126     add : function(target, panel){
31127          
31128         target = target.toLowerCase();
31129         return this.regions[target].add(panel);
31130     },
31131
31132     /**
31133      * Remove a ContentPanel (or subclass) to this layout.
31134      * @param {String} target The target region key (north, south, east, west or center).
31135      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31136      * @return {Roo.ContentPanel} The removed panel
31137      */
31138     remove : function(target, panel){
31139         target = target.toLowerCase();
31140         return this.regions[target].remove(panel);
31141     },
31142
31143     /**
31144      * Searches all regions for a panel with the specified id
31145      * @param {String} panelId
31146      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31147      */
31148     findPanel : function(panelId){
31149         var rs = this.regions;
31150         for(var target in rs){
31151             if(typeof rs[target] != "function"){
31152                 var p = rs[target].getPanel(panelId);
31153                 if(p){
31154                     return p;
31155                 }
31156             }
31157         }
31158         return null;
31159     },
31160
31161     /**
31162      * Searches all regions for a panel with the specified id and activates (shows) it.
31163      * @param {String/ContentPanel} panelId The panels id or the panel itself
31164      * @return {Roo.ContentPanel} The shown panel or null
31165      */
31166     showPanel : function(panelId) {
31167       var rs = this.regions;
31168       for(var target in rs){
31169          var r = rs[target];
31170          if(typeof r != "function"){
31171             if(r.hasPanel(panelId)){
31172                return r.showPanel(panelId);
31173             }
31174          }
31175       }
31176       return null;
31177    },
31178
31179    /**
31180      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31181      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31182      */
31183     restoreState : function(provider){
31184         if(!provider){
31185             provider = Roo.state.Manager;
31186         }
31187         var sm = new Roo.LayoutStateManager();
31188         sm.init(this, provider);
31189     },
31190
31191     /**
31192      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
31193      * object should contain properties for each region to add ContentPanels to, and each property's value should be
31194      * a valid ContentPanel config object.  Example:
31195      * <pre><code>
31196 // Create the main layout
31197 var layout = new Roo.BorderLayout('main-ct', {
31198     west: {
31199         split:true,
31200         minSize: 175,
31201         titlebar: true
31202     },
31203     center: {
31204         title:'Components'
31205     }
31206 }, 'main-ct');
31207
31208 // Create and add multiple ContentPanels at once via configs
31209 layout.batchAdd({
31210    west: {
31211        id: 'source-files',
31212        autoCreate:true,
31213        title:'Ext Source Files',
31214        autoScroll:true,
31215        fitToFrame:true
31216    },
31217    center : {
31218        el: cview,
31219        autoScroll:true,
31220        fitToFrame:true,
31221        toolbar: tb,
31222        resizeEl:'cbody'
31223    }
31224 });
31225 </code></pre>
31226      * @param {Object} regions An object containing ContentPanel configs by region name
31227      */
31228     batchAdd : function(regions){
31229         this.beginUpdate();
31230         for(var rname in regions){
31231             var lr = this.regions[rname];
31232             if(lr){
31233                 this.addTypedPanels(lr, regions[rname]);
31234             }
31235         }
31236         this.endUpdate();
31237     },
31238
31239     // private
31240     addTypedPanels : function(lr, ps){
31241         if(typeof ps == 'string'){
31242             lr.add(new Roo.ContentPanel(ps));
31243         }
31244         else if(ps instanceof Array){
31245             for(var i =0, len = ps.length; i < len; i++){
31246                 this.addTypedPanels(lr, ps[i]);
31247             }
31248         }
31249         else if(!ps.events){ // raw config?
31250             var el = ps.el;
31251             delete ps.el; // prevent conflict
31252             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
31253         }
31254         else {  // panel object assumed!
31255             lr.add(ps);
31256         }
31257     },
31258     /**
31259      * Adds a xtype elements to the layout.
31260      * <pre><code>
31261
31262 layout.addxtype({
31263        xtype : 'ContentPanel',
31264        region: 'west',
31265        items: [ .... ]
31266    }
31267 );
31268
31269 layout.addxtype({
31270         xtype : 'NestedLayoutPanel',
31271         region: 'west',
31272         layout: {
31273            center: { },
31274            west: { }   
31275         },
31276         items : [ ... list of content panels or nested layout panels.. ]
31277    }
31278 );
31279 </code></pre>
31280      * @param {Object} cfg Xtype definition of item to add.
31281      */
31282     addxtype : function(cfg)
31283     {
31284         // basically accepts a pannel...
31285         // can accept a layout region..!?!?
31286         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31287         
31288         if (!cfg.xtype.match(/Panel$/)) {
31289             return false;
31290         }
31291         var ret = false;
31292         
31293         if (typeof(cfg.region) == 'undefined') {
31294             Roo.log("Failed to add Panel, region was not set");
31295             Roo.log(cfg);
31296             return false;
31297         }
31298         var region = cfg.region;
31299         delete cfg.region;
31300         
31301           
31302         var xitems = [];
31303         if (cfg.items) {
31304             xitems = cfg.items;
31305             delete cfg.items;
31306         }
31307         var nb = false;
31308         
31309         switch(cfg.xtype) 
31310         {
31311             case 'ContentPanel':  // ContentPanel (el, cfg)
31312             case 'ScrollPanel':  // ContentPanel (el, cfg)
31313                 if(cfg.autoCreate) {
31314                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31315                 } else {
31316                     var el = this.el.createChild();
31317                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31318                 }
31319                 
31320                 this.add(region, ret);
31321                 break;
31322             
31323             
31324             case 'TreePanel': // our new panel!
31325                 cfg.el = this.el.createChild();
31326                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31327                 this.add(region, ret);
31328                 break;
31329             
31330             case 'NestedLayoutPanel': 
31331                 // create a new Layout (which is  a Border Layout...
31332                 var el = this.el.createChild();
31333                 var clayout = cfg.layout;
31334                 delete cfg.layout;
31335                 clayout.items   = clayout.items  || [];
31336                 // replace this exitems with the clayout ones..
31337                 xitems = clayout.items;
31338                  
31339                 
31340                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31341                     cfg.background = false;
31342                 }
31343                 var layout = new Roo.BorderLayout(el, clayout);
31344                 
31345                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
31346                 //console.log('adding nested layout panel '  + cfg.toSource());
31347                 this.add(region, ret);
31348                 nb = {}; /// find first...
31349                 break;
31350                 
31351             case 'GridPanel': 
31352             
31353                 // needs grid and region
31354                 
31355                 //var el = this.getRegion(region).el.createChild();
31356                 var el = this.el.createChild();
31357                 // create the grid first...
31358                 
31359                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
31360                 delete cfg.grid;
31361                 if (region == 'center' && this.active ) {
31362                     cfg.background = false;
31363                 }
31364                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
31365                 
31366                 this.add(region, ret);
31367                 if (cfg.background) {
31368                     ret.on('activate', function(gp) {
31369                         if (!gp.grid.rendered) {
31370                             gp.grid.render();
31371                         }
31372                     });
31373                 } else {
31374                     grid.render();
31375                 }
31376                 break;
31377            
31378                
31379                 
31380                 
31381             default: 
31382                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
31383                 return null;
31384              // GridPanel (grid, cfg)
31385             
31386         }
31387         this.beginUpdate();
31388         // add children..
31389         var region = '';
31390         var abn = {};
31391         Roo.each(xitems, function(i)  {
31392             region = nb && i.region ? i.region : false;
31393             
31394             var add = ret.addxtype(i);
31395            
31396             if (region) {
31397                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31398                 if (!i.background) {
31399                     abn[region] = nb[region] ;
31400                 }
31401             }
31402             
31403         });
31404         this.endUpdate();
31405
31406         // make the last non-background panel active..
31407         //if (nb) { Roo.log(abn); }
31408         if (nb) {
31409             
31410             for(var r in abn) {
31411                 region = this.getRegion(r);
31412                 if (region) {
31413                     // tried using nb[r], but it does not work..
31414                      
31415                     region.showPanel(abn[r]);
31416                    
31417                 }
31418             }
31419         }
31420         return ret;
31421         
31422     }
31423 });
31424
31425 /**
31426  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
31427  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
31428  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
31429  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
31430  * <pre><code>
31431 // shorthand
31432 var CP = Roo.ContentPanel;
31433
31434 var layout = Roo.BorderLayout.create({
31435     north: {
31436         initialSize: 25,
31437         titlebar: false,
31438         panels: [new CP("north", "North")]
31439     },
31440     west: {
31441         split:true,
31442         initialSize: 200,
31443         minSize: 175,
31444         maxSize: 400,
31445         titlebar: true,
31446         collapsible: true,
31447         panels: [new CP("west", {title: "West"})]
31448     },
31449     east: {
31450         split:true,
31451         initialSize: 202,
31452         minSize: 175,
31453         maxSize: 400,
31454         titlebar: true,
31455         collapsible: true,
31456         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
31457     },
31458     south: {
31459         split:true,
31460         initialSize: 100,
31461         minSize: 100,
31462         maxSize: 200,
31463         titlebar: true,
31464         collapsible: true,
31465         panels: [new CP("south", {title: "South", closable: true})]
31466     },
31467     center: {
31468         titlebar: true,
31469         autoScroll:true,
31470         resizeTabs: true,
31471         minTabWidth: 50,
31472         preferredTabWidth: 150,
31473         panels: [
31474             new CP("center1", {title: "Close Me", closable: true}),
31475             new CP("center2", {title: "Center Panel", closable: false})
31476         ]
31477     }
31478 }, document.body);
31479
31480 layout.getRegion("center").showPanel("center1");
31481 </code></pre>
31482  * @param config
31483  * @param targetEl
31484  */
31485 Roo.BorderLayout.create = function(config, targetEl){
31486     var layout = new Roo.BorderLayout(targetEl || document.body, config);
31487     layout.beginUpdate();
31488     var regions = Roo.BorderLayout.RegionFactory.validRegions;
31489     for(var j = 0, jlen = regions.length; j < jlen; j++){
31490         var lr = regions[j];
31491         if(layout.regions[lr] && config[lr].panels){
31492             var r = layout.regions[lr];
31493             var ps = config[lr].panels;
31494             layout.addTypedPanels(r, ps);
31495         }
31496     }
31497     layout.endUpdate();
31498     return layout;
31499 };
31500
31501 // private
31502 Roo.BorderLayout.RegionFactory = {
31503     // private
31504     validRegions : ["north","south","east","west","center"],
31505
31506     // private
31507     create : function(target, mgr, config){
31508         target = target.toLowerCase();
31509         if(config.lightweight || config.basic){
31510             return new Roo.BasicLayoutRegion(mgr, config, target);
31511         }
31512         switch(target){
31513             case "north":
31514                 return new Roo.NorthLayoutRegion(mgr, config);
31515             case "south":
31516                 return new Roo.SouthLayoutRegion(mgr, config);
31517             case "east":
31518                 return new Roo.EastLayoutRegion(mgr, config);
31519             case "west":
31520                 return new Roo.WestLayoutRegion(mgr, config);
31521             case "center":
31522                 return new Roo.CenterLayoutRegion(mgr, config);
31523         }
31524         throw 'Layout region "'+target+'" not supported.';
31525     }
31526 };/*
31527  * Based on:
31528  * Ext JS Library 1.1.1
31529  * Copyright(c) 2006-2007, Ext JS, LLC.
31530  *
31531  * Originally Released Under LGPL - original licence link has changed is not relivant.
31532  *
31533  * Fork - LGPL
31534  * <script type="text/javascript">
31535  */
31536  
31537 /**
31538  * @class Roo.BasicLayoutRegion
31539  * @extends Roo.util.Observable
31540  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31541  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31542  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31543  */
31544 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
31545     this.mgr = mgr;
31546     this.position  = pos;
31547     this.events = {
31548         /**
31549          * @scope Roo.BasicLayoutRegion
31550          */
31551         
31552         /**
31553          * @event beforeremove
31554          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31555          * @param {Roo.LayoutRegion} this
31556          * @param {Roo.ContentPanel} panel The panel
31557          * @param {Object} e The cancel event object
31558          */
31559         "beforeremove" : true,
31560         /**
31561          * @event invalidated
31562          * Fires when the layout for this region is changed.
31563          * @param {Roo.LayoutRegion} this
31564          */
31565         "invalidated" : true,
31566         /**
31567          * @event visibilitychange
31568          * Fires when this region is shown or hidden 
31569          * @param {Roo.LayoutRegion} this
31570          * @param {Boolean} visibility true or false
31571          */
31572         "visibilitychange" : true,
31573         /**
31574          * @event paneladded
31575          * Fires when a panel is added. 
31576          * @param {Roo.LayoutRegion} this
31577          * @param {Roo.ContentPanel} panel The panel
31578          */
31579         "paneladded" : true,
31580         /**
31581          * @event panelremoved
31582          * Fires when a panel is removed. 
31583          * @param {Roo.LayoutRegion} this
31584          * @param {Roo.ContentPanel} panel The panel
31585          */
31586         "panelremoved" : true,
31587         /**
31588          * @event collapsed
31589          * Fires when this region is collapsed.
31590          * @param {Roo.LayoutRegion} this
31591          */
31592         "collapsed" : true,
31593         /**
31594          * @event expanded
31595          * Fires when this region is expanded.
31596          * @param {Roo.LayoutRegion} this
31597          */
31598         "expanded" : true,
31599         /**
31600          * @event slideshow
31601          * Fires when this region is slid into view.
31602          * @param {Roo.LayoutRegion} this
31603          */
31604         "slideshow" : true,
31605         /**
31606          * @event slidehide
31607          * Fires when this region slides out of view. 
31608          * @param {Roo.LayoutRegion} this
31609          */
31610         "slidehide" : true,
31611         /**
31612          * @event panelactivated
31613          * Fires when a panel is activated. 
31614          * @param {Roo.LayoutRegion} this
31615          * @param {Roo.ContentPanel} panel The activated panel
31616          */
31617         "panelactivated" : true,
31618         /**
31619          * @event resized
31620          * Fires when the user resizes this region. 
31621          * @param {Roo.LayoutRegion} this
31622          * @param {Number} newSize The new size (width for east/west, height for north/south)
31623          */
31624         "resized" : true
31625     };
31626     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31627     this.panels = new Roo.util.MixedCollection();
31628     this.panels.getKey = this.getPanelId.createDelegate(this);
31629     this.box = null;
31630     this.activePanel = null;
31631     // ensure listeners are added...
31632     
31633     if (config.listeners || config.events) {
31634         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
31635             listeners : config.listeners || {},
31636             events : config.events || {}
31637         });
31638     }
31639     
31640     if(skipConfig !== true){
31641         this.applyConfig(config);
31642     }
31643 };
31644
31645 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
31646     getPanelId : function(p){
31647         return p.getId();
31648     },
31649     
31650     applyConfig : function(config){
31651         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31652         this.config = config;
31653         
31654     },
31655     
31656     /**
31657      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31658      * the width, for horizontal (north, south) the height.
31659      * @param {Number} newSize The new width or height
31660      */
31661     resizeTo : function(newSize){
31662         var el = this.el ? this.el :
31663                  (this.activePanel ? this.activePanel.getEl() : null);
31664         if(el){
31665             switch(this.position){
31666                 case "east":
31667                 case "west":
31668                     el.setWidth(newSize);
31669                     this.fireEvent("resized", this, newSize);
31670                 break;
31671                 case "north":
31672                 case "south":
31673                     el.setHeight(newSize);
31674                     this.fireEvent("resized", this, newSize);
31675                 break;                
31676             }
31677         }
31678     },
31679     
31680     getBox : function(){
31681         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31682     },
31683     
31684     getMargins : function(){
31685         return this.margins;
31686     },
31687     
31688     updateBox : function(box){
31689         this.box = box;
31690         var el = this.activePanel.getEl();
31691         el.dom.style.left = box.x + "px";
31692         el.dom.style.top = box.y + "px";
31693         this.activePanel.setSize(box.width, box.height);
31694     },
31695     
31696     /**
31697      * Returns the container element for this region.
31698      * @return {Roo.Element}
31699      */
31700     getEl : function(){
31701         return this.activePanel;
31702     },
31703     
31704     /**
31705      * Returns true if this region is currently visible.
31706      * @return {Boolean}
31707      */
31708     isVisible : function(){
31709         return this.activePanel ? true : false;
31710     },
31711     
31712     setActivePanel : function(panel){
31713         panel = this.getPanel(panel);
31714         if(this.activePanel && this.activePanel != panel){
31715             this.activePanel.setActiveState(false);
31716             this.activePanel.getEl().setLeftTop(-10000,-10000);
31717         }
31718         this.activePanel = panel;
31719         panel.setActiveState(true);
31720         if(this.box){
31721             panel.setSize(this.box.width, this.box.height);
31722         }
31723         this.fireEvent("panelactivated", this, panel);
31724         this.fireEvent("invalidated");
31725     },
31726     
31727     /**
31728      * Show the specified panel.
31729      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
31730      * @return {Roo.ContentPanel} The shown panel or null
31731      */
31732     showPanel : function(panel){
31733         if(panel = this.getPanel(panel)){
31734             this.setActivePanel(panel);
31735         }
31736         return panel;
31737     },
31738     
31739     /**
31740      * Get the active panel for this region.
31741      * @return {Roo.ContentPanel} The active panel or null
31742      */
31743     getActivePanel : function(){
31744         return this.activePanel;
31745     },
31746     
31747     /**
31748      * Add the passed ContentPanel(s)
31749      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
31750      * @return {Roo.ContentPanel} The panel added (if only one was added)
31751      */
31752     add : function(panel){
31753         if(arguments.length > 1){
31754             for(var i = 0, len = arguments.length; i < len; i++) {
31755                 this.add(arguments[i]);
31756             }
31757             return null;
31758         }
31759         if(this.hasPanel(panel)){
31760             this.showPanel(panel);
31761             return panel;
31762         }
31763         var el = panel.getEl();
31764         if(el.dom.parentNode != this.mgr.el.dom){
31765             this.mgr.el.dom.appendChild(el.dom);
31766         }
31767         if(panel.setRegion){
31768             panel.setRegion(this);
31769         }
31770         this.panels.add(panel);
31771         el.setStyle("position", "absolute");
31772         if(!panel.background){
31773             this.setActivePanel(panel);
31774             if(this.config.initialSize && this.panels.getCount()==1){
31775                 this.resizeTo(this.config.initialSize);
31776             }
31777         }
31778         this.fireEvent("paneladded", this, panel);
31779         return panel;
31780     },
31781     
31782     /**
31783      * Returns true if the panel is in this region.
31784      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31785      * @return {Boolean}
31786      */
31787     hasPanel : function(panel){
31788         if(typeof panel == "object"){ // must be panel obj
31789             panel = panel.getId();
31790         }
31791         return this.getPanel(panel) ? true : false;
31792     },
31793     
31794     /**
31795      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
31796      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31797      * @param {Boolean} preservePanel Overrides the config preservePanel option
31798      * @return {Roo.ContentPanel} The panel that was removed
31799      */
31800     remove : function(panel, preservePanel){
31801         panel = this.getPanel(panel);
31802         if(!panel){
31803             return null;
31804         }
31805         var e = {};
31806         this.fireEvent("beforeremove", this, panel, e);
31807         if(e.cancel === true){
31808             return null;
31809         }
31810         var panelId = panel.getId();
31811         this.panels.removeKey(panelId);
31812         return panel;
31813     },
31814     
31815     /**
31816      * Returns the panel specified or null if it's not in this region.
31817      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
31818      * @return {Roo.ContentPanel}
31819      */
31820     getPanel : function(id){
31821         if(typeof id == "object"){ // must be panel obj
31822             return id;
31823         }
31824         return this.panels.get(id);
31825     },
31826     
31827     /**
31828      * Returns this regions position (north/south/east/west/center).
31829      * @return {String} 
31830      */
31831     getPosition: function(){
31832         return this.position;    
31833     }
31834 });/*
31835  * Based on:
31836  * Ext JS Library 1.1.1
31837  * Copyright(c) 2006-2007, Ext JS, LLC.
31838  *
31839  * Originally Released Under LGPL - original licence link has changed is not relivant.
31840  *
31841  * Fork - LGPL
31842  * <script type="text/javascript">
31843  */
31844  
31845 /**
31846  * @class Roo.LayoutRegion
31847  * @extends Roo.BasicLayoutRegion
31848  * This class represents a region in a layout manager.
31849  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
31850  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
31851  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
31852  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
31853  * @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})
31854  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
31855  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
31856  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
31857  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
31858  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
31859  * @cfg {String}    title           The title for the region (overrides panel titles)
31860  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
31861  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
31862  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
31863  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
31864  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
31865  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
31866  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
31867  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
31868  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
31869  * @cfg {Boolean}   showPin         True to show a pin button
31870  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
31871  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
31872  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
31873  * @cfg {Number}    width           For East/West panels
31874  * @cfg {Number}    height          For North/South panels
31875  * @cfg {Boolean}   split           To show the splitter
31876  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
31877  */
31878 Roo.LayoutRegion = function(mgr, config, pos){
31879     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
31880     var dh = Roo.DomHelper;
31881     /** This region's container element 
31882     * @type Roo.Element */
31883     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
31884     /** This region's title element 
31885     * @type Roo.Element */
31886
31887     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
31888         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
31889         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
31890     ]}, true);
31891     this.titleEl.enableDisplayMode();
31892     /** This region's title text element 
31893     * @type HTMLElement */
31894     this.titleTextEl = this.titleEl.dom.firstChild;
31895     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
31896     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
31897     this.closeBtn.enableDisplayMode();
31898     this.closeBtn.on("click", this.closeClicked, this);
31899     this.closeBtn.hide();
31900
31901     this.createBody(config);
31902     this.visible = true;
31903     this.collapsed = false;
31904
31905     if(config.hideWhenEmpty){
31906         this.hide();
31907         this.on("paneladded", this.validateVisibility, this);
31908         this.on("panelremoved", this.validateVisibility, this);
31909     }
31910     this.applyConfig(config);
31911 };
31912
31913 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
31914
31915     createBody : function(){
31916         /** This region's body element 
31917         * @type Roo.Element */
31918         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
31919     },
31920
31921     applyConfig : function(c){
31922         if(c.collapsible && this.position != "center" && !this.collapsedEl){
31923             var dh = Roo.DomHelper;
31924             if(c.titlebar !== false){
31925                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
31926                 this.collapseBtn.on("click", this.collapse, this);
31927                 this.collapseBtn.enableDisplayMode();
31928
31929                 if(c.showPin === true || this.showPin){
31930                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
31931                     this.stickBtn.enableDisplayMode();
31932                     this.stickBtn.on("click", this.expand, this);
31933                     this.stickBtn.hide();
31934                 }
31935             }
31936             /** This region's collapsed element
31937             * @type Roo.Element */
31938             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
31939                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
31940             ]}, true);
31941             if(c.floatable !== false){
31942                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
31943                this.collapsedEl.on("click", this.collapseClick, this);
31944             }
31945
31946             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
31947                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
31948                    id: "message", unselectable: "on", style:{"float":"left"}});
31949                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
31950              }
31951             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
31952             this.expandBtn.on("click", this.expand, this);
31953         }
31954         if(this.collapseBtn){
31955             this.collapseBtn.setVisible(c.collapsible == true);
31956         }
31957         this.cmargins = c.cmargins || this.cmargins ||
31958                          (this.position == "west" || this.position == "east" ?
31959                              {top: 0, left: 2, right:2, bottom: 0} :
31960                              {top: 2, left: 0, right:0, bottom: 2});
31961         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31962         this.bottomTabs = c.tabPosition != "top";
31963         this.autoScroll = c.autoScroll || false;
31964         if(this.autoScroll){
31965             this.bodyEl.setStyle("overflow", "auto");
31966         }else{
31967             this.bodyEl.setStyle("overflow", "hidden");
31968         }
31969         //if(c.titlebar !== false){
31970             if((!c.titlebar && !c.title) || c.titlebar === false){
31971                 this.titleEl.hide();
31972             }else{
31973                 this.titleEl.show();
31974                 if(c.title){
31975                     this.titleTextEl.innerHTML = c.title;
31976                 }
31977             }
31978         //}
31979         this.duration = c.duration || .30;
31980         this.slideDuration = c.slideDuration || .45;
31981         this.config = c;
31982         if(c.collapsed){
31983             this.collapse(true);
31984         }
31985         if(c.hidden){
31986             this.hide();
31987         }
31988     },
31989     /**
31990      * Returns true if this region is currently visible.
31991      * @return {Boolean}
31992      */
31993     isVisible : function(){
31994         return this.visible;
31995     },
31996
31997     /**
31998      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
31999      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32000      */
32001     setCollapsedTitle : function(title){
32002         title = title || "&#160;";
32003         if(this.collapsedTitleTextEl){
32004             this.collapsedTitleTextEl.innerHTML = title;
32005         }
32006     },
32007
32008     getBox : function(){
32009         var b;
32010         if(!this.collapsed){
32011             b = this.el.getBox(false, true);
32012         }else{
32013             b = this.collapsedEl.getBox(false, true);
32014         }
32015         return b;
32016     },
32017
32018     getMargins : function(){
32019         return this.collapsed ? this.cmargins : this.margins;
32020     },
32021
32022     highlight : function(){
32023         this.el.addClass("x-layout-panel-dragover");
32024     },
32025
32026     unhighlight : function(){
32027         this.el.removeClass("x-layout-panel-dragover");
32028     },
32029
32030     updateBox : function(box){
32031         this.box = box;
32032         if(!this.collapsed){
32033             this.el.dom.style.left = box.x + "px";
32034             this.el.dom.style.top = box.y + "px";
32035             this.updateBody(box.width, box.height);
32036         }else{
32037             this.collapsedEl.dom.style.left = box.x + "px";
32038             this.collapsedEl.dom.style.top = box.y + "px";
32039             this.collapsedEl.setSize(box.width, box.height);
32040         }
32041         if(this.tabs){
32042             this.tabs.autoSizeTabs();
32043         }
32044     },
32045
32046     updateBody : function(w, h){
32047         if(w !== null){
32048             this.el.setWidth(w);
32049             w -= this.el.getBorderWidth("rl");
32050             if(this.config.adjustments){
32051                 w += this.config.adjustments[0];
32052             }
32053         }
32054         if(h !== null){
32055             this.el.setHeight(h);
32056             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32057             h -= this.el.getBorderWidth("tb");
32058             if(this.config.adjustments){
32059                 h += this.config.adjustments[1];
32060             }
32061             this.bodyEl.setHeight(h);
32062             if(this.tabs){
32063                 h = this.tabs.syncHeight(h);
32064             }
32065         }
32066         if(this.panelSize){
32067             w = w !== null ? w : this.panelSize.width;
32068             h = h !== null ? h : this.panelSize.height;
32069         }
32070         if(this.activePanel){
32071             var el = this.activePanel.getEl();
32072             w = w !== null ? w : el.getWidth();
32073             h = h !== null ? h : el.getHeight();
32074             this.panelSize = {width: w, height: h};
32075             this.activePanel.setSize(w, h);
32076         }
32077         if(Roo.isIE && this.tabs){
32078             this.tabs.el.repaint();
32079         }
32080     },
32081
32082     /**
32083      * Returns the container element for this region.
32084      * @return {Roo.Element}
32085      */
32086     getEl : function(){
32087         return this.el;
32088     },
32089
32090     /**
32091      * Hides this region.
32092      */
32093     hide : function(){
32094         if(!this.collapsed){
32095             this.el.dom.style.left = "-2000px";
32096             this.el.hide();
32097         }else{
32098             this.collapsedEl.dom.style.left = "-2000px";
32099             this.collapsedEl.hide();
32100         }
32101         this.visible = false;
32102         this.fireEvent("visibilitychange", this, false);
32103     },
32104
32105     /**
32106      * Shows this region if it was previously hidden.
32107      */
32108     show : function(){
32109         if(!this.collapsed){
32110             this.el.show();
32111         }else{
32112             this.collapsedEl.show();
32113         }
32114         this.visible = true;
32115         this.fireEvent("visibilitychange", this, true);
32116     },
32117
32118     closeClicked : function(){
32119         if(this.activePanel){
32120             this.remove(this.activePanel);
32121         }
32122     },
32123
32124     collapseClick : function(e){
32125         if(this.isSlid){
32126            e.stopPropagation();
32127            this.slideIn();
32128         }else{
32129            e.stopPropagation();
32130            this.slideOut();
32131         }
32132     },
32133
32134     /**
32135      * Collapses this region.
32136      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32137      */
32138     collapse : function(skipAnim){
32139         if(this.collapsed) return;
32140         this.collapsed = true;
32141         if(this.split){
32142             this.split.el.hide();
32143         }
32144         if(this.config.animate && skipAnim !== true){
32145             this.fireEvent("invalidated", this);
32146             this.animateCollapse();
32147         }else{
32148             this.el.setLocation(-20000,-20000);
32149             this.el.hide();
32150             this.collapsedEl.show();
32151             this.fireEvent("collapsed", this);
32152             this.fireEvent("invalidated", this);
32153         }
32154     },
32155
32156     animateCollapse : function(){
32157         // overridden
32158     },
32159
32160     /**
32161      * Expands this region if it was previously collapsed.
32162      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32163      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32164      */
32165     expand : function(e, skipAnim){
32166         if(e) e.stopPropagation();
32167         if(!this.collapsed || this.el.hasActiveFx()) return;
32168         if(this.isSlid){
32169             this.afterSlideIn();
32170             skipAnim = true;
32171         }
32172         this.collapsed = false;
32173         if(this.config.animate && skipAnim !== true){
32174             this.animateExpand();
32175         }else{
32176             this.el.show();
32177             if(this.split){
32178                 this.split.el.show();
32179             }
32180             this.collapsedEl.setLocation(-2000,-2000);
32181             this.collapsedEl.hide();
32182             this.fireEvent("invalidated", this);
32183             this.fireEvent("expanded", this);
32184         }
32185     },
32186
32187     animateExpand : function(){
32188         // overridden
32189     },
32190
32191     initTabs : function()
32192     {
32193         this.bodyEl.setStyle("overflow", "hidden");
32194         var ts = new Roo.TabPanel(
32195                 this.bodyEl.dom,
32196                 {
32197                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
32198                     disableTooltips: this.config.disableTabTips,
32199                     toolbar : this.config.toolbar
32200                 }
32201         );
32202         if(this.config.hideTabs){
32203             ts.stripWrap.setDisplayed(false);
32204         }
32205         this.tabs = ts;
32206         ts.resizeTabs = this.config.resizeTabs === true;
32207         ts.minTabWidth = this.config.minTabWidth || 40;
32208         ts.maxTabWidth = this.config.maxTabWidth || 250;
32209         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32210         ts.monitorResize = false;
32211         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32212         ts.bodyEl.addClass('x-layout-tabs-body');
32213         this.panels.each(this.initPanelAsTab, this);
32214     },
32215
32216     initPanelAsTab : function(panel){
32217         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
32218                     this.config.closeOnTab && panel.isClosable());
32219         if(panel.tabTip !== undefined){
32220             ti.setTooltip(panel.tabTip);
32221         }
32222         ti.on("activate", function(){
32223               this.setActivePanel(panel);
32224         }, this);
32225         if(this.config.closeOnTab){
32226             ti.on("beforeclose", function(t, e){
32227                 e.cancel = true;
32228                 this.remove(panel);
32229             }, this);
32230         }
32231         return ti;
32232     },
32233
32234     updatePanelTitle : function(panel, title){
32235         if(this.activePanel == panel){
32236             this.updateTitle(title);
32237         }
32238         if(this.tabs){
32239             var ti = this.tabs.getTab(panel.getEl().id);
32240             ti.setText(title);
32241             if(panel.tabTip !== undefined){
32242                 ti.setTooltip(panel.tabTip);
32243             }
32244         }
32245     },
32246
32247     updateTitle : function(title){
32248         if(this.titleTextEl && !this.config.title){
32249             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32250         }
32251     },
32252
32253     setActivePanel : function(panel){
32254         panel = this.getPanel(panel);
32255         if(this.activePanel && this.activePanel != panel){
32256             this.activePanel.setActiveState(false);
32257         }
32258         this.activePanel = panel;
32259         panel.setActiveState(true);
32260         if(this.panelSize){
32261             panel.setSize(this.panelSize.width, this.panelSize.height);
32262         }
32263         if(this.closeBtn){
32264             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32265         }
32266         this.updateTitle(panel.getTitle());
32267         if(this.tabs){
32268             this.fireEvent("invalidated", this);
32269         }
32270         this.fireEvent("panelactivated", this, panel);
32271     },
32272
32273     /**
32274      * Shows the specified panel.
32275      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32276      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32277      */
32278     showPanel : function(panel){
32279         if(panel = this.getPanel(panel)){
32280             if(this.tabs){
32281                 var tab = this.tabs.getTab(panel.getEl().id);
32282                 if(tab.isHidden()){
32283                     this.tabs.unhideTab(tab.id);
32284                 }
32285                 tab.activate();
32286             }else{
32287                 this.setActivePanel(panel);
32288             }
32289         }
32290         return panel;
32291     },
32292
32293     /**
32294      * Get the active panel for this region.
32295      * @return {Roo.ContentPanel} The active panel or null
32296      */
32297     getActivePanel : function(){
32298         return this.activePanel;
32299     },
32300
32301     validateVisibility : function(){
32302         if(this.panels.getCount() < 1){
32303             this.updateTitle("&#160;");
32304             this.closeBtn.hide();
32305             this.hide();
32306         }else{
32307             if(!this.isVisible()){
32308                 this.show();
32309             }
32310         }
32311     },
32312
32313     /**
32314      * Adds the passed ContentPanel(s) to this region.
32315      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32316      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32317      */
32318     add : function(panel){
32319         if(arguments.length > 1){
32320             for(var i = 0, len = arguments.length; i < len; i++) {
32321                 this.add(arguments[i]);
32322             }
32323             return null;
32324         }
32325         if(this.hasPanel(panel)){
32326             this.showPanel(panel);
32327             return panel;
32328         }
32329         panel.setRegion(this);
32330         this.panels.add(panel);
32331         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32332             this.bodyEl.dom.appendChild(panel.getEl().dom);
32333             if(panel.background !== true){
32334                 this.setActivePanel(panel);
32335             }
32336             this.fireEvent("paneladded", this, panel);
32337             return panel;
32338         }
32339         if(!this.tabs){
32340             this.initTabs();
32341         }else{
32342             this.initPanelAsTab(panel);
32343         }
32344         if(panel.background !== true){
32345             this.tabs.activate(panel.getEl().id);
32346         }
32347         this.fireEvent("paneladded", this, panel);
32348         return panel;
32349     },
32350
32351     /**
32352      * Hides the tab for the specified panel.
32353      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32354      */
32355     hidePanel : function(panel){
32356         if(this.tabs && (panel = this.getPanel(panel))){
32357             this.tabs.hideTab(panel.getEl().id);
32358         }
32359     },
32360
32361     /**
32362      * Unhides the tab for a previously hidden panel.
32363      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32364      */
32365     unhidePanel : function(panel){
32366         if(this.tabs && (panel = this.getPanel(panel))){
32367             this.tabs.unhideTab(panel.getEl().id);
32368         }
32369     },
32370
32371     clearPanels : function(){
32372         while(this.panels.getCount() > 0){
32373              this.remove(this.panels.first());
32374         }
32375     },
32376
32377     /**
32378      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32379      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32380      * @param {Boolean} preservePanel Overrides the config preservePanel option
32381      * @return {Roo.ContentPanel} The panel that was removed
32382      */
32383     remove : function(panel, preservePanel){
32384         panel = this.getPanel(panel);
32385         if(!panel){
32386             return null;
32387         }
32388         var e = {};
32389         this.fireEvent("beforeremove", this, panel, e);
32390         if(e.cancel === true){
32391             return null;
32392         }
32393         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32394         var panelId = panel.getId();
32395         this.panels.removeKey(panelId);
32396         if(preservePanel){
32397             document.body.appendChild(panel.getEl().dom);
32398         }
32399         if(this.tabs){
32400             this.tabs.removeTab(panel.getEl().id);
32401         }else if (!preservePanel){
32402             this.bodyEl.dom.removeChild(panel.getEl().dom);
32403         }
32404         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32405             var p = this.panels.first();
32406             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32407             tempEl.appendChild(p.getEl().dom);
32408             this.bodyEl.update("");
32409             this.bodyEl.dom.appendChild(p.getEl().dom);
32410             tempEl = null;
32411             this.updateTitle(p.getTitle());
32412             this.tabs = null;
32413             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32414             this.setActivePanel(p);
32415         }
32416         panel.setRegion(null);
32417         if(this.activePanel == panel){
32418             this.activePanel = null;
32419         }
32420         if(this.config.autoDestroy !== false && preservePanel !== true){
32421             try{panel.destroy();}catch(e){}
32422         }
32423         this.fireEvent("panelremoved", this, panel);
32424         return panel;
32425     },
32426
32427     /**
32428      * Returns the TabPanel component used by this region
32429      * @return {Roo.TabPanel}
32430      */
32431     getTabs : function(){
32432         return this.tabs;
32433     },
32434
32435     createTool : function(parentEl, className){
32436         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
32437             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
32438         btn.addClassOnOver("x-layout-tools-button-over");
32439         return btn;
32440     }
32441 });/*
32442  * Based on:
32443  * Ext JS Library 1.1.1
32444  * Copyright(c) 2006-2007, Ext JS, LLC.
32445  *
32446  * Originally Released Under LGPL - original licence link has changed is not relivant.
32447  *
32448  * Fork - LGPL
32449  * <script type="text/javascript">
32450  */
32451  
32452
32453
32454 /**
32455  * @class Roo.SplitLayoutRegion
32456  * @extends Roo.LayoutRegion
32457  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32458  */
32459 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
32460     this.cursor = cursor;
32461     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
32462 };
32463
32464 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
32465     splitTip : "Drag to resize.",
32466     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32467     useSplitTips : false,
32468
32469     applyConfig : function(config){
32470         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
32471         if(config.split){
32472             if(!this.split){
32473                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
32474                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
32475                 /** The SplitBar for this region 
32476                 * @type Roo.SplitBar */
32477                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
32478                 this.split.on("moved", this.onSplitMove, this);
32479                 this.split.useShim = config.useShim === true;
32480                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32481                 if(this.useSplitTips){
32482                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32483                 }
32484                 if(config.collapsible){
32485                     this.split.el.on("dblclick", this.collapse,  this);
32486                 }
32487             }
32488             if(typeof config.minSize != "undefined"){
32489                 this.split.minSize = config.minSize;
32490             }
32491             if(typeof config.maxSize != "undefined"){
32492                 this.split.maxSize = config.maxSize;
32493             }
32494             if(config.hideWhenEmpty || config.hidden || config.collapsed){
32495                 this.hideSplitter();
32496             }
32497         }
32498     },
32499
32500     getHMaxSize : function(){
32501          var cmax = this.config.maxSize || 10000;
32502          var center = this.mgr.getRegion("center");
32503          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32504     },
32505
32506     getVMaxSize : function(){
32507          var cmax = this.config.maxSize || 10000;
32508          var center = this.mgr.getRegion("center");
32509          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32510     },
32511
32512     onSplitMove : function(split, newSize){
32513         this.fireEvent("resized", this, newSize);
32514     },
32515     
32516     /** 
32517      * Returns the {@link Roo.SplitBar} for this region.
32518      * @return {Roo.SplitBar}
32519      */
32520     getSplitBar : function(){
32521         return this.split;
32522     },
32523     
32524     hide : function(){
32525         this.hideSplitter();
32526         Roo.SplitLayoutRegion.superclass.hide.call(this);
32527     },
32528
32529     hideSplitter : function(){
32530         if(this.split){
32531             this.split.el.setLocation(-2000,-2000);
32532             this.split.el.hide();
32533         }
32534     },
32535
32536     show : function(){
32537         if(this.split){
32538             this.split.el.show();
32539         }
32540         Roo.SplitLayoutRegion.superclass.show.call(this);
32541     },
32542     
32543     beforeSlide: function(){
32544         if(Roo.isGecko){// firefox overflow auto bug workaround
32545             this.bodyEl.clip();
32546             if(this.tabs) this.tabs.bodyEl.clip();
32547             if(this.activePanel){
32548                 this.activePanel.getEl().clip();
32549                 
32550                 if(this.activePanel.beforeSlide){
32551                     this.activePanel.beforeSlide();
32552                 }
32553             }
32554         }
32555     },
32556     
32557     afterSlide : function(){
32558         if(Roo.isGecko){// firefox overflow auto bug workaround
32559             this.bodyEl.unclip();
32560             if(this.tabs) this.tabs.bodyEl.unclip();
32561             if(this.activePanel){
32562                 this.activePanel.getEl().unclip();
32563                 if(this.activePanel.afterSlide){
32564                     this.activePanel.afterSlide();
32565                 }
32566             }
32567         }
32568     },
32569
32570     initAutoHide : function(){
32571         if(this.autoHide !== false){
32572             if(!this.autoHideHd){
32573                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32574                 this.autoHideHd = {
32575                     "mouseout": function(e){
32576                         if(!e.within(this.el, true)){
32577                             st.delay(500);
32578                         }
32579                     },
32580                     "mouseover" : function(e){
32581                         st.cancel();
32582                     },
32583                     scope : this
32584                 };
32585             }
32586             this.el.on(this.autoHideHd);
32587         }
32588     },
32589
32590     clearAutoHide : function(){
32591         if(this.autoHide !== false){
32592             this.el.un("mouseout", this.autoHideHd.mouseout);
32593             this.el.un("mouseover", this.autoHideHd.mouseover);
32594         }
32595     },
32596
32597     clearMonitor : function(){
32598         Roo.get(document).un("click", this.slideInIf, this);
32599     },
32600
32601     // these names are backwards but not changed for compat
32602     slideOut : function(){
32603         if(this.isSlid || this.el.hasActiveFx()){
32604             return;
32605         }
32606         this.isSlid = true;
32607         if(this.collapseBtn){
32608             this.collapseBtn.hide();
32609         }
32610         this.closeBtnState = this.closeBtn.getStyle('display');
32611         this.closeBtn.hide();
32612         if(this.stickBtn){
32613             this.stickBtn.show();
32614         }
32615         this.el.show();
32616         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
32617         this.beforeSlide();
32618         this.el.setStyle("z-index", 10001);
32619         this.el.slideIn(this.getSlideAnchor(), {
32620             callback: function(){
32621                 this.afterSlide();
32622                 this.initAutoHide();
32623                 Roo.get(document).on("click", this.slideInIf, this);
32624                 this.fireEvent("slideshow", this);
32625             },
32626             scope: this,
32627             block: true
32628         });
32629     },
32630
32631     afterSlideIn : function(){
32632         this.clearAutoHide();
32633         this.isSlid = false;
32634         this.clearMonitor();
32635         this.el.setStyle("z-index", "");
32636         if(this.collapseBtn){
32637             this.collapseBtn.show();
32638         }
32639         this.closeBtn.setStyle('display', this.closeBtnState);
32640         if(this.stickBtn){
32641             this.stickBtn.hide();
32642         }
32643         this.fireEvent("slidehide", this);
32644     },
32645
32646     slideIn : function(cb){
32647         if(!this.isSlid || this.el.hasActiveFx()){
32648             Roo.callback(cb);
32649             return;
32650         }
32651         this.isSlid = false;
32652         this.beforeSlide();
32653         this.el.slideOut(this.getSlideAnchor(), {
32654             callback: function(){
32655                 this.el.setLeftTop(-10000, -10000);
32656                 this.afterSlide();
32657                 this.afterSlideIn();
32658                 Roo.callback(cb);
32659             },
32660             scope: this,
32661             block: true
32662         });
32663     },
32664     
32665     slideInIf : function(e){
32666         if(!e.within(this.el)){
32667             this.slideIn();
32668         }
32669     },
32670
32671     animateCollapse : function(){
32672         this.beforeSlide();
32673         this.el.setStyle("z-index", 20000);
32674         var anchor = this.getSlideAnchor();
32675         this.el.slideOut(anchor, {
32676             callback : function(){
32677                 this.el.setStyle("z-index", "");
32678                 this.collapsedEl.slideIn(anchor, {duration:.3});
32679                 this.afterSlide();
32680                 this.el.setLocation(-10000,-10000);
32681                 this.el.hide();
32682                 this.fireEvent("collapsed", this);
32683             },
32684             scope: this,
32685             block: true
32686         });
32687     },
32688
32689     animateExpand : function(){
32690         this.beforeSlide();
32691         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
32692         this.el.setStyle("z-index", 20000);
32693         this.collapsedEl.hide({
32694             duration:.1
32695         });
32696         this.el.slideIn(this.getSlideAnchor(), {
32697             callback : function(){
32698                 this.el.setStyle("z-index", "");
32699                 this.afterSlide();
32700                 if(this.split){
32701                     this.split.el.show();
32702                 }
32703                 this.fireEvent("invalidated", this);
32704                 this.fireEvent("expanded", this);
32705             },
32706             scope: this,
32707             block: true
32708         });
32709     },
32710
32711     anchors : {
32712         "west" : "left",
32713         "east" : "right",
32714         "north" : "top",
32715         "south" : "bottom"
32716     },
32717
32718     sanchors : {
32719         "west" : "l",
32720         "east" : "r",
32721         "north" : "t",
32722         "south" : "b"
32723     },
32724
32725     canchors : {
32726         "west" : "tl-tr",
32727         "east" : "tr-tl",
32728         "north" : "tl-bl",
32729         "south" : "bl-tl"
32730     },
32731
32732     getAnchor : function(){
32733         return this.anchors[this.position];
32734     },
32735
32736     getCollapseAnchor : function(){
32737         return this.canchors[this.position];
32738     },
32739
32740     getSlideAnchor : function(){
32741         return this.sanchors[this.position];
32742     },
32743
32744     getAlignAdj : function(){
32745         var cm = this.cmargins;
32746         switch(this.position){
32747             case "west":
32748                 return [0, 0];
32749             break;
32750             case "east":
32751                 return [0, 0];
32752             break;
32753             case "north":
32754                 return [0, 0];
32755             break;
32756             case "south":
32757                 return [0, 0];
32758             break;
32759         }
32760     },
32761
32762     getExpandAdj : function(){
32763         var c = this.collapsedEl, cm = this.cmargins;
32764         switch(this.position){
32765             case "west":
32766                 return [-(cm.right+c.getWidth()+cm.left), 0];
32767             break;
32768             case "east":
32769                 return [cm.right+c.getWidth()+cm.left, 0];
32770             break;
32771             case "north":
32772                 return [0, -(cm.top+cm.bottom+c.getHeight())];
32773             break;
32774             case "south":
32775                 return [0, cm.top+cm.bottom+c.getHeight()];
32776             break;
32777         }
32778     }
32779 });/*
32780  * Based on:
32781  * Ext JS Library 1.1.1
32782  * Copyright(c) 2006-2007, Ext JS, LLC.
32783  *
32784  * Originally Released Under LGPL - original licence link has changed is not relivant.
32785  *
32786  * Fork - LGPL
32787  * <script type="text/javascript">
32788  */
32789 /*
32790  * These classes are private internal classes
32791  */
32792 Roo.CenterLayoutRegion = function(mgr, config){
32793     Roo.LayoutRegion.call(this, mgr, config, "center");
32794     this.visible = true;
32795     this.minWidth = config.minWidth || 20;
32796     this.minHeight = config.minHeight || 20;
32797 };
32798
32799 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
32800     hide : function(){
32801         // center panel can't be hidden
32802     },
32803     
32804     show : function(){
32805         // center panel can't be hidden
32806     },
32807     
32808     getMinWidth: function(){
32809         return this.minWidth;
32810     },
32811     
32812     getMinHeight: function(){
32813         return this.minHeight;
32814     }
32815 });
32816
32817
32818 Roo.NorthLayoutRegion = function(mgr, config){
32819     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
32820     if(this.split){
32821         this.split.placement = Roo.SplitBar.TOP;
32822         this.split.orientation = Roo.SplitBar.VERTICAL;
32823         this.split.el.addClass("x-layout-split-v");
32824     }
32825     var size = config.initialSize || config.height;
32826     if(typeof size != "undefined"){
32827         this.el.setHeight(size);
32828     }
32829 };
32830 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
32831     orientation: Roo.SplitBar.VERTICAL,
32832     getBox : function(){
32833         if(this.collapsed){
32834             return this.collapsedEl.getBox();
32835         }
32836         var box = this.el.getBox();
32837         if(this.split){
32838             box.height += this.split.el.getHeight();
32839         }
32840         return box;
32841     },
32842     
32843     updateBox : function(box){
32844         if(this.split && !this.collapsed){
32845             box.height -= this.split.el.getHeight();
32846             this.split.el.setLeft(box.x);
32847             this.split.el.setTop(box.y+box.height);
32848             this.split.el.setWidth(box.width);
32849         }
32850         if(this.collapsed){
32851             this.updateBody(box.width, null);
32852         }
32853         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32854     }
32855 });
32856
32857 Roo.SouthLayoutRegion = function(mgr, config){
32858     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
32859     if(this.split){
32860         this.split.placement = Roo.SplitBar.BOTTOM;
32861         this.split.orientation = Roo.SplitBar.VERTICAL;
32862         this.split.el.addClass("x-layout-split-v");
32863     }
32864     var size = config.initialSize || config.height;
32865     if(typeof size != "undefined"){
32866         this.el.setHeight(size);
32867     }
32868 };
32869 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
32870     orientation: Roo.SplitBar.VERTICAL,
32871     getBox : function(){
32872         if(this.collapsed){
32873             return this.collapsedEl.getBox();
32874         }
32875         var box = this.el.getBox();
32876         if(this.split){
32877             var sh = this.split.el.getHeight();
32878             box.height += sh;
32879             box.y -= sh;
32880         }
32881         return box;
32882     },
32883     
32884     updateBox : function(box){
32885         if(this.split && !this.collapsed){
32886             var sh = this.split.el.getHeight();
32887             box.height -= sh;
32888             box.y += sh;
32889             this.split.el.setLeft(box.x);
32890             this.split.el.setTop(box.y-sh);
32891             this.split.el.setWidth(box.width);
32892         }
32893         if(this.collapsed){
32894             this.updateBody(box.width, null);
32895         }
32896         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32897     }
32898 });
32899
32900 Roo.EastLayoutRegion = function(mgr, config){
32901     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
32902     if(this.split){
32903         this.split.placement = Roo.SplitBar.RIGHT;
32904         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32905         this.split.el.addClass("x-layout-split-h");
32906     }
32907     var size = config.initialSize || config.width;
32908     if(typeof size != "undefined"){
32909         this.el.setWidth(size);
32910     }
32911 };
32912 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
32913     orientation: Roo.SplitBar.HORIZONTAL,
32914     getBox : function(){
32915         if(this.collapsed){
32916             return this.collapsedEl.getBox();
32917         }
32918         var box = this.el.getBox();
32919         if(this.split){
32920             var sw = this.split.el.getWidth();
32921             box.width += sw;
32922             box.x -= sw;
32923         }
32924         return box;
32925     },
32926
32927     updateBox : function(box){
32928         if(this.split && !this.collapsed){
32929             var sw = this.split.el.getWidth();
32930             box.width -= sw;
32931             this.split.el.setLeft(box.x);
32932             this.split.el.setTop(box.y);
32933             this.split.el.setHeight(box.height);
32934             box.x += sw;
32935         }
32936         if(this.collapsed){
32937             this.updateBody(null, box.height);
32938         }
32939         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32940     }
32941 });
32942
32943 Roo.WestLayoutRegion = function(mgr, config){
32944     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
32945     if(this.split){
32946         this.split.placement = Roo.SplitBar.LEFT;
32947         this.split.orientation = Roo.SplitBar.HORIZONTAL;
32948         this.split.el.addClass("x-layout-split-h");
32949     }
32950     var size = config.initialSize || config.width;
32951     if(typeof size != "undefined"){
32952         this.el.setWidth(size);
32953     }
32954 };
32955 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
32956     orientation: Roo.SplitBar.HORIZONTAL,
32957     getBox : function(){
32958         if(this.collapsed){
32959             return this.collapsedEl.getBox();
32960         }
32961         var box = this.el.getBox();
32962         if(this.split){
32963             box.width += this.split.el.getWidth();
32964         }
32965         return box;
32966     },
32967     
32968     updateBox : function(box){
32969         if(this.split && !this.collapsed){
32970             var sw = this.split.el.getWidth();
32971             box.width -= sw;
32972             this.split.el.setLeft(box.x+box.width);
32973             this.split.el.setTop(box.y);
32974             this.split.el.setHeight(box.height);
32975         }
32976         if(this.collapsed){
32977             this.updateBody(null, box.height);
32978         }
32979         Roo.LayoutRegion.prototype.updateBox.call(this, box);
32980     }
32981 });
32982 /*
32983  * Based on:
32984  * Ext JS Library 1.1.1
32985  * Copyright(c) 2006-2007, Ext JS, LLC.
32986  *
32987  * Originally Released Under LGPL - original licence link has changed is not relivant.
32988  *
32989  * Fork - LGPL
32990  * <script type="text/javascript">
32991  */
32992  
32993  
32994 /*
32995  * Private internal class for reading and applying state
32996  */
32997 Roo.LayoutStateManager = function(layout){
32998      // default empty state
32999      this.state = {
33000         north: {},
33001         south: {},
33002         east: {},
33003         west: {}       
33004     };
33005 };
33006
33007 Roo.LayoutStateManager.prototype = {
33008     init : function(layout, provider){
33009         this.provider = provider;
33010         var state = provider.get(layout.id+"-layout-state");
33011         if(state){
33012             var wasUpdating = layout.isUpdating();
33013             if(!wasUpdating){
33014                 layout.beginUpdate();
33015             }
33016             for(var key in state){
33017                 if(typeof state[key] != "function"){
33018                     var rstate = state[key];
33019                     var r = layout.getRegion(key);
33020                     if(r && rstate){
33021                         if(rstate.size){
33022                             r.resizeTo(rstate.size);
33023                         }
33024                         if(rstate.collapsed == true){
33025                             r.collapse(true);
33026                         }else{
33027                             r.expand(null, true);
33028                         }
33029                     }
33030                 }
33031             }
33032             if(!wasUpdating){
33033                 layout.endUpdate();
33034             }
33035             this.state = state; 
33036         }
33037         this.layout = layout;
33038         layout.on("regionresized", this.onRegionResized, this);
33039         layout.on("regioncollapsed", this.onRegionCollapsed, this);
33040         layout.on("regionexpanded", this.onRegionExpanded, this);
33041     },
33042     
33043     storeState : function(){
33044         this.provider.set(this.layout.id+"-layout-state", this.state);
33045     },
33046     
33047     onRegionResized : function(region, newSize){
33048         this.state[region.getPosition()].size = newSize;
33049         this.storeState();
33050     },
33051     
33052     onRegionCollapsed : function(region){
33053         this.state[region.getPosition()].collapsed = true;
33054         this.storeState();
33055     },
33056     
33057     onRegionExpanded : function(region){
33058         this.state[region.getPosition()].collapsed = false;
33059         this.storeState();
33060     }
33061 };/*
33062  * Based on:
33063  * Ext JS Library 1.1.1
33064  * Copyright(c) 2006-2007, Ext JS, LLC.
33065  *
33066  * Originally Released Under LGPL - original licence link has changed is not relivant.
33067  *
33068  * Fork - LGPL
33069  * <script type="text/javascript">
33070  */
33071 /**
33072  * @class Roo.ContentPanel
33073  * @extends Roo.util.Observable
33074  * A basic ContentPanel element.
33075  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33076  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33077  * @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
33078  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33079  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33080  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33081  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33082  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33083  * @cfg {String} title          The title for this panel
33084  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33085  * @cfg {String} url            Calls {@link #setUrl} with this value
33086  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33087  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33088  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33089  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33090
33091  * @constructor
33092  * Create a new ContentPanel.
33093  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33094  * @param {String/Object} config A string to set only the title or a config object
33095  * @param {String} content (optional) Set the HTML content for this panel
33096  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33097  */
33098 Roo.ContentPanel = function(el, config, content){
33099     
33100      
33101     /*
33102     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
33103         config = el;
33104         el = Roo.id();
33105     }
33106     if (config && config.parentLayout) { 
33107         el = config.parentLayout.el.createChild(); 
33108     }
33109     */
33110     if(el.autoCreate){ // xtype is available if this is called from factory
33111         config = el;
33112         el = Roo.id();
33113     }
33114     this.el = Roo.get(el);
33115     if(!this.el && config && config.autoCreate){
33116         if(typeof config.autoCreate == "object"){
33117             if(!config.autoCreate.id){
33118                 config.autoCreate.id = config.id||el;
33119             }
33120             this.el = Roo.DomHelper.append(document.body,
33121                         config.autoCreate, true);
33122         }else{
33123             this.el = Roo.DomHelper.append(document.body,
33124                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
33125         }
33126     }
33127     this.closable = false;
33128     this.loaded = false;
33129     this.active = false;
33130     if(typeof config == "string"){
33131         this.title = config;
33132     }else{
33133         Roo.apply(this, config);
33134     }
33135     
33136     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
33137         this.wrapEl = this.el.wrap();
33138         this.toolbar.container = this.el.insertSibling(false, 'before');
33139         this.toolbar = new Roo.Toolbar(this.toolbar);
33140     }
33141     
33142     // xtype created footer. - not sure if will work as we normally have to render first..
33143     if (this.footer && !this.footer.el && this.footer.xtype) {
33144         if (!this.wrapEl) {
33145             this.wrapEl = this.el.wrap();
33146         }
33147     
33148         this.footer.container = this.wrapEl.createChild();
33149          
33150         this.footer = Roo.factory(this.footer, Roo);
33151         
33152     }
33153     
33154     if(this.resizeEl){
33155         this.resizeEl = Roo.get(this.resizeEl, true);
33156     }else{
33157         this.resizeEl = this.el;
33158     }
33159     this.addEvents({
33160         /**
33161          * @event activate
33162          * Fires when this panel is activated. 
33163          * @param {Roo.ContentPanel} this
33164          */
33165         "activate" : true,
33166         /**
33167          * @event deactivate
33168          * Fires when this panel is activated. 
33169          * @param {Roo.ContentPanel} this
33170          */
33171         "deactivate" : true,
33172
33173         /**
33174          * @event resize
33175          * Fires when this panel is resized if fitToFrame is true.
33176          * @param {Roo.ContentPanel} this
33177          * @param {Number} width The width after any component adjustments
33178          * @param {Number} height The height after any component adjustments
33179          */
33180         "resize" : true,
33181         
33182          /**
33183          * @event render
33184          * Fires when this tab is created
33185          * @param {Roo.ContentPanel} this
33186          */
33187         "render" : true
33188         
33189         
33190         
33191     });
33192     if(this.autoScroll){
33193         this.resizeEl.setStyle("overflow", "auto");
33194     } else {
33195         // fix randome scrolling
33196         this.el.on('scroll', function() {
33197             Roo.log('fix random scolling');
33198             this.scrollTo('top',0); 
33199         });
33200     }
33201     content = content || this.content;
33202     if(content){
33203         this.setContent(content);
33204     }
33205     if(config && config.url){
33206         this.setUrl(this.url, this.params, this.loadOnce);
33207     }
33208     
33209     
33210     
33211     Roo.ContentPanel.superclass.constructor.call(this);
33212     
33213     this.fireEvent('render', this);
33214 };
33215
33216 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
33217     tabTip:'',
33218     setRegion : function(region){
33219         this.region = region;
33220         if(region){
33221            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
33222         }else{
33223            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
33224         } 
33225     },
33226     
33227     /**
33228      * Returns the toolbar for this Panel if one was configured. 
33229      * @return {Roo.Toolbar} 
33230      */
33231     getToolbar : function(){
33232         return this.toolbar;
33233     },
33234     
33235     setActiveState : function(active){
33236         this.active = active;
33237         if(!active){
33238             this.fireEvent("deactivate", this);
33239         }else{
33240             this.fireEvent("activate", this);
33241         }
33242     },
33243     /**
33244      * Updates this panel's element
33245      * @param {String} content The new content
33246      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33247     */
33248     setContent : function(content, loadScripts){
33249         this.el.update(content, loadScripts);
33250     },
33251
33252     ignoreResize : function(w, h){
33253         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33254             return true;
33255         }else{
33256             this.lastSize = {width: w, height: h};
33257             return false;
33258         }
33259     },
33260     /**
33261      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33262      * @return {Roo.UpdateManager} The UpdateManager
33263      */
33264     getUpdateManager : function(){
33265         return this.el.getUpdateManager();
33266     },
33267      /**
33268      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33269      * @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:
33270 <pre><code>
33271 panel.load({
33272     url: "your-url.php",
33273     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33274     callback: yourFunction,
33275     scope: yourObject, //(optional scope)
33276     discardUrl: false,
33277     nocache: false,
33278     text: "Loading...",
33279     timeout: 30,
33280     scripts: false
33281 });
33282 </code></pre>
33283      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33284      * 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.
33285      * @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}
33286      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33287      * @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.
33288      * @return {Roo.ContentPanel} this
33289      */
33290     load : function(){
33291         var um = this.el.getUpdateManager();
33292         um.update.apply(um, arguments);
33293         return this;
33294     },
33295
33296
33297     /**
33298      * 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.
33299      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33300      * @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)
33301      * @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)
33302      * @return {Roo.UpdateManager} The UpdateManager
33303      */
33304     setUrl : function(url, params, loadOnce){
33305         if(this.refreshDelegate){
33306             this.removeListener("activate", this.refreshDelegate);
33307         }
33308         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33309         this.on("activate", this.refreshDelegate);
33310         return this.el.getUpdateManager();
33311     },
33312     
33313     _handleRefresh : function(url, params, loadOnce){
33314         if(!loadOnce || !this.loaded){
33315             var updater = this.el.getUpdateManager();
33316             updater.update(url, params, this._setLoaded.createDelegate(this));
33317         }
33318     },
33319     
33320     _setLoaded : function(){
33321         this.loaded = true;
33322     }, 
33323     
33324     /**
33325      * Returns this panel's id
33326      * @return {String} 
33327      */
33328     getId : function(){
33329         return this.el.id;
33330     },
33331     
33332     /** 
33333      * Returns this panel's element - used by regiosn to add.
33334      * @return {Roo.Element} 
33335      */
33336     getEl : function(){
33337         return this.wrapEl || this.el;
33338     },
33339     
33340     adjustForComponents : function(width, height)
33341     {
33342         Roo.log('adjustForComponents ');
33343         if(this.resizeEl != this.el){
33344             width -= this.el.getFrameWidth('lr');
33345             height -= this.el.getFrameWidth('tb');
33346         }
33347         if(this.toolbar){
33348             var te = this.toolbar.getEl();
33349             height -= te.getHeight();
33350             te.setWidth(width);
33351         }
33352         if(this.footer){
33353             var te = this.footer.getEl();
33354             Roo.log("footer:" + te.getHeight());
33355             
33356             height -= te.getHeight();
33357             te.setWidth(width);
33358         }
33359         
33360         
33361         if(this.adjustments){
33362             width += this.adjustments[0];
33363             height += this.adjustments[1];
33364         }
33365         return {"width": width, "height": height};
33366     },
33367     
33368     setSize : function(width, height){
33369         if(this.fitToFrame && !this.ignoreResize(width, height)){
33370             if(this.fitContainer && this.resizeEl != this.el){
33371                 this.el.setSize(width, height);
33372             }
33373             var size = this.adjustForComponents(width, height);
33374             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33375             this.fireEvent('resize', this, size.width, size.height);
33376         }
33377     },
33378     
33379     /**
33380      * Returns this panel's title
33381      * @return {String} 
33382      */
33383     getTitle : function(){
33384         return this.title;
33385     },
33386     
33387     /**
33388      * Set this panel's title
33389      * @param {String} title
33390      */
33391     setTitle : function(title){
33392         this.title = title;
33393         if(this.region){
33394             this.region.updatePanelTitle(this, title);
33395         }
33396     },
33397     
33398     /**
33399      * Returns true is this panel was configured to be closable
33400      * @return {Boolean} 
33401      */
33402     isClosable : function(){
33403         return this.closable;
33404     },
33405     
33406     beforeSlide : function(){
33407         this.el.clip();
33408         this.resizeEl.clip();
33409     },
33410     
33411     afterSlide : function(){
33412         this.el.unclip();
33413         this.resizeEl.unclip();
33414     },
33415     
33416     /**
33417      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33418      *   Will fail silently if the {@link #setUrl} method has not been called.
33419      *   This does not activate the panel, just updates its content.
33420      */
33421     refresh : function(){
33422         if(this.refreshDelegate){
33423            this.loaded = false;
33424            this.refreshDelegate();
33425         }
33426     },
33427     
33428     /**
33429      * Destroys this panel
33430      */
33431     destroy : function(){
33432         this.el.removeAllListeners();
33433         var tempEl = document.createElement("span");
33434         tempEl.appendChild(this.el.dom);
33435         tempEl.innerHTML = "";
33436         this.el.remove();
33437         this.el = null;
33438     },
33439     
33440     /**
33441      * form - if the content panel contains a form - this is a reference to it.
33442      * @type {Roo.form.Form}
33443      */
33444     form : false,
33445     /**
33446      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33447      *    This contains a reference to it.
33448      * @type {Roo.View}
33449      */
33450     view : false,
33451     
33452       /**
33453      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33454      * <pre><code>
33455
33456 layout.addxtype({
33457        xtype : 'Form',
33458        items: [ .... ]
33459    }
33460 );
33461
33462 </code></pre>
33463      * @param {Object} cfg Xtype definition of item to add.
33464      */
33465     
33466     addxtype : function(cfg) {
33467         // add form..
33468         if (cfg.xtype.match(/^Form$/)) {
33469             
33470             var el;
33471             //if (this.footer) {
33472             //    el = this.footer.container.insertSibling(false, 'before');
33473             //} else {
33474                 el = this.el.createChild();
33475             //}
33476
33477             this.form = new  Roo.form.Form(cfg);
33478             
33479             
33480             if ( this.form.allItems.length) this.form.render(el.dom);
33481             return this.form;
33482         }
33483         // should only have one of theses..
33484         if (['View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33485             // views..
33486             cfg.el = this.el.appendChild(document.createElement("div"));
33487             // factory?
33488             
33489             var ret = new Roo.factory(cfg);
33490             ret.render && ret.render(false, ''); // render blank..
33491             this.view = ret;
33492             return ret;
33493         }
33494         return false;
33495     }
33496 });
33497
33498 /**
33499  * @class Roo.GridPanel
33500  * @extends Roo.ContentPanel
33501  * @constructor
33502  * Create a new GridPanel.
33503  * @param {Roo.grid.Grid} grid The grid for this panel
33504  * @param {String/Object} config A string to set only the panel's title, or a config object
33505  */
33506 Roo.GridPanel = function(grid, config){
33507     
33508   
33509     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33510         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
33511         
33512     this.wrapper.dom.appendChild(grid.getGridEl().dom);
33513     
33514     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
33515     
33516     if(this.toolbar){
33517         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
33518     }
33519     // xtype created footer. - not sure if will work as we normally have to render first..
33520     if (this.footer && !this.footer.el && this.footer.xtype) {
33521         
33522         this.footer.container = this.grid.getView().getFooterPanel(true);
33523         this.footer.dataSource = this.grid.dataSource;
33524         this.footer = Roo.factory(this.footer, Roo);
33525         
33526     }
33527     
33528     grid.monitorWindowResize = false; // turn off autosizing
33529     grid.autoHeight = false;
33530     grid.autoWidth = false;
33531     this.grid = grid;
33532     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
33533 };
33534
33535 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
33536     getId : function(){
33537         return this.grid.id;
33538     },
33539     
33540     /**
33541      * Returns the grid for this panel
33542      * @return {Roo.grid.Grid} 
33543      */
33544     getGrid : function(){
33545         return this.grid;    
33546     },
33547     
33548     setSize : function(width, height){
33549         if(!this.ignoreResize(width, height)){
33550             var grid = this.grid;
33551             var size = this.adjustForComponents(width, height);
33552             grid.getGridEl().setSize(size.width, size.height);
33553             grid.autoSize();
33554         }
33555     },
33556     
33557     beforeSlide : function(){
33558         this.grid.getView().scroller.clip();
33559     },
33560     
33561     afterSlide : function(){
33562         this.grid.getView().scroller.unclip();
33563     },
33564     
33565     destroy : function(){
33566         this.grid.destroy();
33567         delete this.grid;
33568         Roo.GridPanel.superclass.destroy.call(this); 
33569     }
33570 });
33571
33572
33573 /**
33574  * @class Roo.NestedLayoutPanel
33575  * @extends Roo.ContentPanel
33576  * @constructor
33577  * Create a new NestedLayoutPanel.
33578  * 
33579  * 
33580  * @param {Roo.BorderLayout} layout The layout for this panel
33581  * @param {String/Object} config A string to set only the title or a config object
33582  */
33583 Roo.NestedLayoutPanel = function(layout, config)
33584 {
33585     // construct with only one argument..
33586     /* FIXME - implement nicer consturctors
33587     if (layout.layout) {
33588         config = layout;
33589         layout = config.layout;
33590         delete config.layout;
33591     }
33592     if (layout.xtype && !layout.getEl) {
33593         // then layout needs constructing..
33594         layout = Roo.factory(layout, Roo);
33595     }
33596     */
33597     
33598     
33599     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
33600     
33601     layout.monitorWindowResize = false; // turn off autosizing
33602     this.layout = layout;
33603     this.layout.getEl().addClass("x-layout-nested-layout");
33604     
33605     
33606     
33607     
33608 };
33609
33610 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
33611
33612     setSize : function(width, height){
33613         if(!this.ignoreResize(width, height)){
33614             var size = this.adjustForComponents(width, height);
33615             var el = this.layout.getEl();
33616             el.setSize(size.width, size.height);
33617             var touch = el.dom.offsetWidth;
33618             this.layout.layout();
33619             // ie requires a double layout on the first pass
33620             if(Roo.isIE && !this.initialized){
33621                 this.initialized = true;
33622                 this.layout.layout();
33623             }
33624         }
33625     },
33626     
33627     // activate all subpanels if not currently active..
33628     
33629     setActiveState : function(active){
33630         this.active = active;
33631         if(!active){
33632             this.fireEvent("deactivate", this);
33633             return;
33634         }
33635         
33636         this.fireEvent("activate", this);
33637         // not sure if this should happen before or after..
33638         if (!this.layout) {
33639             return; // should not happen..
33640         }
33641         var reg = false;
33642         for (var r in this.layout.regions) {
33643             reg = this.layout.getRegion(r);
33644             if (reg.getActivePanel()) {
33645                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
33646                 reg.setActivePanel(reg.getActivePanel());
33647                 continue;
33648             }
33649             if (!reg.panels.length) {
33650                 continue;
33651             }
33652             reg.showPanel(reg.getPanel(0));
33653         }
33654         
33655         
33656         
33657         
33658     },
33659     
33660     /**
33661      * Returns the nested BorderLayout for this panel
33662      * @return {Roo.BorderLayout} 
33663      */
33664     getLayout : function(){
33665         return this.layout;
33666     },
33667     
33668      /**
33669      * Adds a xtype elements to the layout of the nested panel
33670      * <pre><code>
33671
33672 panel.addxtype({
33673        xtype : 'ContentPanel',
33674        region: 'west',
33675        items: [ .... ]
33676    }
33677 );
33678
33679 panel.addxtype({
33680         xtype : 'NestedLayoutPanel',
33681         region: 'west',
33682         layout: {
33683            center: { },
33684            west: { }   
33685         },
33686         items : [ ... list of content panels or nested layout panels.. ]
33687    }
33688 );
33689 </code></pre>
33690      * @param {Object} cfg Xtype definition of item to add.
33691      */
33692     addxtype : function(cfg) {
33693         return this.layout.addxtype(cfg);
33694     
33695     }
33696 });
33697
33698 Roo.ScrollPanel = function(el, config, content){
33699     config = config || {};
33700     config.fitToFrame = true;
33701     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
33702     
33703     this.el.dom.style.overflow = "hidden";
33704     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
33705     this.el.removeClass("x-layout-inactive-content");
33706     this.el.on("mousewheel", this.onWheel, this);
33707
33708     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
33709     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
33710     up.unselectable(); down.unselectable();
33711     up.on("click", this.scrollUp, this);
33712     down.on("click", this.scrollDown, this);
33713     up.addClassOnOver("x-scroller-btn-over");
33714     down.addClassOnOver("x-scroller-btn-over");
33715     up.addClassOnClick("x-scroller-btn-click");
33716     down.addClassOnClick("x-scroller-btn-click");
33717     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
33718
33719     this.resizeEl = this.el;
33720     this.el = wrap; this.up = up; this.down = down;
33721 };
33722
33723 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
33724     increment : 100,
33725     wheelIncrement : 5,
33726     scrollUp : function(){
33727         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
33728     },
33729
33730     scrollDown : function(){
33731         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
33732     },
33733
33734     afterScroll : function(){
33735         var el = this.resizeEl;
33736         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
33737         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33738         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
33739     },
33740
33741     setSize : function(){
33742         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
33743         this.afterScroll();
33744     },
33745
33746     onWheel : function(e){
33747         var d = e.getWheelDelta();
33748         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
33749         this.afterScroll();
33750         e.stopEvent();
33751     },
33752
33753     setContent : function(content, loadScripts){
33754         this.resizeEl.update(content, loadScripts);
33755     }
33756
33757 });
33758
33759
33760
33761
33762
33763
33764
33765
33766
33767 /**
33768  * @class Roo.TreePanel
33769  * @extends Roo.ContentPanel
33770  * @constructor
33771  * Create a new TreePanel. - defaults to fit/scoll contents.
33772  * @param {String/Object} config A string to set only the panel's title, or a config object
33773  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
33774  */
33775 Roo.TreePanel = function(config){
33776     var el = config.el;
33777     var tree = config.tree;
33778     delete config.tree; 
33779     delete config.el; // hopefull!
33780     
33781     // wrapper for IE7 strict & safari scroll issue
33782     
33783     var treeEl = el.createChild();
33784     config.resizeEl = treeEl;
33785     
33786     
33787     
33788     Roo.TreePanel.superclass.constructor.call(this, el, config);
33789  
33790  
33791     this.tree = new Roo.tree.TreePanel(treeEl , tree);
33792     //console.log(tree);
33793     this.on('activate', function()
33794     {
33795         if (this.tree.rendered) {
33796             return;
33797         }
33798         //console.log('render tree');
33799         this.tree.render();
33800     });
33801     // this should not be needed.. - it's actually the 'el' that resizes?
33802     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
33803     
33804     //this.on('resize',  function (cp, w, h) {
33805     //        this.tree.innerCt.setWidth(w);
33806     //        this.tree.innerCt.setHeight(h);
33807     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
33808     //});
33809
33810         
33811     
33812 };
33813
33814 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
33815     fitToFrame : true,
33816     autoScroll : true
33817 });
33818
33819
33820
33821
33822
33823
33824
33825
33826
33827
33828
33829 /*
33830  * Based on:
33831  * Ext JS Library 1.1.1
33832  * Copyright(c) 2006-2007, Ext JS, LLC.
33833  *
33834  * Originally Released Under LGPL - original licence link has changed is not relivant.
33835  *
33836  * Fork - LGPL
33837  * <script type="text/javascript">
33838  */
33839  
33840
33841 /**
33842  * @class Roo.ReaderLayout
33843  * @extends Roo.BorderLayout
33844  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
33845  * center region containing two nested regions (a top one for a list view and one for item preview below),
33846  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
33847  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
33848  * expedites the setup of the overall layout and regions for this common application style.
33849  * Example:
33850  <pre><code>
33851 var reader = new Roo.ReaderLayout();
33852 var CP = Roo.ContentPanel;  // shortcut for adding
33853
33854 reader.beginUpdate();
33855 reader.add("north", new CP("north", "North"));
33856 reader.add("west", new CP("west", {title: "West"}));
33857 reader.add("east", new CP("east", {title: "East"}));
33858
33859 reader.regions.listView.add(new CP("listView", "List"));
33860 reader.regions.preview.add(new CP("preview", "Preview"));
33861 reader.endUpdate();
33862 </code></pre>
33863 * @constructor
33864 * Create a new ReaderLayout
33865 * @param {Object} config Configuration options
33866 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
33867 * document.body if omitted)
33868 */
33869 Roo.ReaderLayout = function(config, renderTo){
33870     var c = config || {size:{}};
33871     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
33872         north: c.north !== false ? Roo.apply({
33873             split:false,
33874             initialSize: 32,
33875             titlebar: false
33876         }, c.north) : false,
33877         west: c.west !== false ? Roo.apply({
33878             split:true,
33879             initialSize: 200,
33880             minSize: 175,
33881             maxSize: 400,
33882             titlebar: true,
33883             collapsible: true,
33884             animate: true,
33885             margins:{left:5,right:0,bottom:5,top:5},
33886             cmargins:{left:5,right:5,bottom:5,top:5}
33887         }, c.west) : false,
33888         east: c.east !== false ? Roo.apply({
33889             split:true,
33890             initialSize: 200,
33891             minSize: 175,
33892             maxSize: 400,
33893             titlebar: true,
33894             collapsible: true,
33895             animate: true,
33896             margins:{left:0,right:5,bottom:5,top:5},
33897             cmargins:{left:5,right:5,bottom:5,top:5}
33898         }, c.east) : false,
33899         center: Roo.apply({
33900             tabPosition: 'top',
33901             autoScroll:false,
33902             closeOnTab: true,
33903             titlebar:false,
33904             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
33905         }, c.center)
33906     });
33907
33908     this.el.addClass('x-reader');
33909
33910     this.beginUpdate();
33911
33912     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
33913         south: c.preview !== false ? Roo.apply({
33914             split:true,
33915             initialSize: 200,
33916             minSize: 100,
33917             autoScroll:true,
33918             collapsible:true,
33919             titlebar: true,
33920             cmargins:{top:5,left:0, right:0, bottom:0}
33921         }, c.preview) : false,
33922         center: Roo.apply({
33923             autoScroll:false,
33924             titlebar:false,
33925             minHeight:200
33926         }, c.listView)
33927     });
33928     this.add('center', new Roo.NestedLayoutPanel(inner,
33929             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
33930
33931     this.endUpdate();
33932
33933     this.regions.preview = inner.getRegion('south');
33934     this.regions.listView = inner.getRegion('center');
33935 };
33936
33937 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
33938  * Based on:
33939  * Ext JS Library 1.1.1
33940  * Copyright(c) 2006-2007, Ext JS, LLC.
33941  *
33942  * Originally Released Under LGPL - original licence link has changed is not relivant.
33943  *
33944  * Fork - LGPL
33945  * <script type="text/javascript">
33946  */
33947  
33948 /**
33949  * @class Roo.grid.Grid
33950  * @extends Roo.util.Observable
33951  * This class represents the primary interface of a component based grid control.
33952  * <br><br>Usage:<pre><code>
33953  var grid = new Roo.grid.Grid("my-container-id", {
33954      ds: myDataStore,
33955      cm: myColModel,
33956      selModel: mySelectionModel,
33957      autoSizeColumns: true,
33958      monitorWindowResize: false,
33959      trackMouseOver: true
33960  });
33961  // set any options
33962  grid.render();
33963  * </code></pre>
33964  * <b>Common Problems:</b><br/>
33965  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
33966  * element will correct this<br/>
33967  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
33968  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
33969  * are unpredictable.<br/>
33970  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
33971  * grid to calculate dimensions/offsets.<br/>
33972   * @constructor
33973  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
33974  * The container MUST have some type of size defined for the grid to fill. The container will be
33975  * automatically set to position relative if it isn't already.
33976  * @param {Object} config A config object that sets properties on this grid.
33977  */
33978 Roo.grid.Grid = function(container, config){
33979         // initialize the container
33980         this.container = Roo.get(container);
33981         this.container.update("");
33982         this.container.setStyle("overflow", "hidden");
33983     this.container.addClass('x-grid-container');
33984
33985     this.id = this.container.id;
33986
33987     Roo.apply(this, config);
33988     // check and correct shorthanded configs
33989     if(this.ds){
33990         this.dataSource = this.ds;
33991         delete this.ds;
33992     }
33993     if(this.cm){
33994         this.colModel = this.cm;
33995         delete this.cm;
33996     }
33997     if(this.sm){
33998         this.selModel = this.sm;
33999         delete this.sm;
34000     }
34001
34002     if (this.selModel) {
34003         this.selModel = Roo.factory(this.selModel, Roo.grid);
34004         this.sm = this.selModel;
34005         this.sm.xmodule = this.xmodule || false;
34006     }
34007     if (typeof(this.colModel.config) == 'undefined') {
34008         this.colModel = new Roo.grid.ColumnModel(this.colModel);
34009         this.cm = this.colModel;
34010         this.cm.xmodule = this.xmodule || false;
34011     }
34012     if (this.dataSource) {
34013         this.dataSource= Roo.factory(this.dataSource, Roo.data);
34014         this.ds = this.dataSource;
34015         this.ds.xmodule = this.xmodule || false;
34016          
34017     }
34018     
34019     
34020     
34021     if(this.width){
34022         this.container.setWidth(this.width);
34023     }
34024
34025     if(this.height){
34026         this.container.setHeight(this.height);
34027     }
34028     /** @private */
34029         this.addEvents({
34030         // raw events
34031         /**
34032          * @event click
34033          * The raw click event for the entire grid.
34034          * @param {Roo.EventObject} e
34035          */
34036         "click" : true,
34037         /**
34038          * @event dblclick
34039          * The raw dblclick event for the entire grid.
34040          * @param {Roo.EventObject} e
34041          */
34042         "dblclick" : true,
34043         /**
34044          * @event contextmenu
34045          * The raw contextmenu event for the entire grid.
34046          * @param {Roo.EventObject} e
34047          */
34048         "contextmenu" : true,
34049         /**
34050          * @event mousedown
34051          * The raw mousedown event for the entire grid.
34052          * @param {Roo.EventObject} e
34053          */
34054         "mousedown" : true,
34055         /**
34056          * @event mouseup
34057          * The raw mouseup event for the entire grid.
34058          * @param {Roo.EventObject} e
34059          */
34060         "mouseup" : true,
34061         /**
34062          * @event mouseover
34063          * The raw mouseover event for the entire grid.
34064          * @param {Roo.EventObject} e
34065          */
34066         "mouseover" : true,
34067         /**
34068          * @event mouseout
34069          * The raw mouseout event for the entire grid.
34070          * @param {Roo.EventObject} e
34071          */
34072         "mouseout" : true,
34073         /**
34074          * @event keypress
34075          * The raw keypress event for the entire grid.
34076          * @param {Roo.EventObject} e
34077          */
34078         "keypress" : true,
34079         /**
34080          * @event keydown
34081          * The raw keydown event for the entire grid.
34082          * @param {Roo.EventObject} e
34083          */
34084         "keydown" : true,
34085
34086         // custom events
34087
34088         /**
34089          * @event cellclick
34090          * Fires when a cell is clicked
34091          * @param {Grid} this
34092          * @param {Number} rowIndex
34093          * @param {Number} columnIndex
34094          * @param {Roo.EventObject} e
34095          */
34096         "cellclick" : true,
34097         /**
34098          * @event celldblclick
34099          * Fires when a cell is double clicked
34100          * @param {Grid} this
34101          * @param {Number} rowIndex
34102          * @param {Number} columnIndex
34103          * @param {Roo.EventObject} e
34104          */
34105         "celldblclick" : true,
34106         /**
34107          * @event rowclick
34108          * Fires when a row is clicked
34109          * @param {Grid} this
34110          * @param {Number} rowIndex
34111          * @param {Roo.EventObject} e
34112          */
34113         "rowclick" : true,
34114         /**
34115          * @event rowdblclick
34116          * Fires when a row is double clicked
34117          * @param {Grid} this
34118          * @param {Number} rowIndex
34119          * @param {Roo.EventObject} e
34120          */
34121         "rowdblclick" : true,
34122         /**
34123          * @event headerclick
34124          * Fires when a header is clicked
34125          * @param {Grid} this
34126          * @param {Number} columnIndex
34127          * @param {Roo.EventObject} e
34128          */
34129         "headerclick" : true,
34130         /**
34131          * @event headerdblclick
34132          * Fires when a header cell is double clicked
34133          * @param {Grid} this
34134          * @param {Number} columnIndex
34135          * @param {Roo.EventObject} e
34136          */
34137         "headerdblclick" : true,
34138         /**
34139          * @event rowcontextmenu
34140          * Fires when a row is right clicked
34141          * @param {Grid} this
34142          * @param {Number} rowIndex
34143          * @param {Roo.EventObject} e
34144          */
34145         "rowcontextmenu" : true,
34146         /**
34147          * @event cellcontextmenu
34148          * Fires when a cell is right clicked
34149          * @param {Grid} this
34150          * @param {Number} rowIndex
34151          * @param {Number} cellIndex
34152          * @param {Roo.EventObject} e
34153          */
34154          "cellcontextmenu" : true,
34155         /**
34156          * @event headercontextmenu
34157          * Fires when a header is right clicked
34158          * @param {Grid} this
34159          * @param {Number} columnIndex
34160          * @param {Roo.EventObject} e
34161          */
34162         "headercontextmenu" : true,
34163         /**
34164          * @event bodyscroll
34165          * Fires when the body element is scrolled
34166          * @param {Number} scrollLeft
34167          * @param {Number} scrollTop
34168          */
34169         "bodyscroll" : true,
34170         /**
34171          * @event columnresize
34172          * Fires when the user resizes a column
34173          * @param {Number} columnIndex
34174          * @param {Number} newSize
34175          */
34176         "columnresize" : true,
34177         /**
34178          * @event columnmove
34179          * Fires when the user moves a column
34180          * @param {Number} oldIndex
34181          * @param {Number} newIndex
34182          */
34183         "columnmove" : true,
34184         /**
34185          * @event startdrag
34186          * Fires when row(s) start being dragged
34187          * @param {Grid} this
34188          * @param {Roo.GridDD} dd The drag drop object
34189          * @param {event} e The raw browser event
34190          */
34191         "startdrag" : true,
34192         /**
34193          * @event enddrag
34194          * Fires when a drag operation is complete
34195          * @param {Grid} this
34196          * @param {Roo.GridDD} dd The drag drop object
34197          * @param {event} e The raw browser event
34198          */
34199         "enddrag" : true,
34200         /**
34201          * @event dragdrop
34202          * Fires when dragged row(s) are dropped on a valid DD target
34203          * @param {Grid} this
34204          * @param {Roo.GridDD} dd The drag drop object
34205          * @param {String} targetId The target drag drop object
34206          * @param {event} e The raw browser event
34207          */
34208         "dragdrop" : true,
34209         /**
34210          * @event dragover
34211          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
34212          * @param {Grid} this
34213          * @param {Roo.GridDD} dd The drag drop object
34214          * @param {String} targetId The target drag drop object
34215          * @param {event} e The raw browser event
34216          */
34217         "dragover" : true,
34218         /**
34219          * @event dragenter
34220          *  Fires when the dragged row(s) first cross another DD target while being dragged
34221          * @param {Grid} this
34222          * @param {Roo.GridDD} dd The drag drop object
34223          * @param {String} targetId The target drag drop object
34224          * @param {event} e The raw browser event
34225          */
34226         "dragenter" : true,
34227         /**
34228          * @event dragout
34229          * Fires when the dragged row(s) leave another DD target while being dragged
34230          * @param {Grid} this
34231          * @param {Roo.GridDD} dd The drag drop object
34232          * @param {String} targetId The target drag drop object
34233          * @param {event} e The raw browser event
34234          */
34235         "dragout" : true,
34236         /**
34237          * @event rowclass
34238          * Fires when a row is rendered, so you can change add a style to it.
34239          * @param {GridView} gridview   The grid view
34240          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
34241          */
34242         'rowclass' : true,
34243
34244         /**
34245          * @event render
34246          * Fires when the grid is rendered
34247          * @param {Grid} grid
34248          */
34249         'render' : true
34250     });
34251
34252     Roo.grid.Grid.superclass.constructor.call(this);
34253 };
34254 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
34255     
34256     /**
34257      * @cfg {String} ddGroup - drag drop group.
34258      */
34259
34260     /**
34261      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
34262      */
34263     minColumnWidth : 25,
34264
34265     /**
34266      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
34267      * <b>on initial render.</b> It is more efficient to explicitly size the columns
34268      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
34269      */
34270     autoSizeColumns : false,
34271
34272     /**
34273      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
34274      */
34275     autoSizeHeaders : true,
34276
34277     /**
34278      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
34279      */
34280     monitorWindowResize : true,
34281
34282     /**
34283      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
34284      * rows measured to get a columns size. Default is 0 (all rows).
34285      */
34286     maxRowsToMeasure : 0,
34287
34288     /**
34289      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
34290      */
34291     trackMouseOver : true,
34292
34293     /**
34294     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
34295     */
34296     
34297     /**
34298     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
34299     */
34300     enableDragDrop : false,
34301     
34302     /**
34303     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
34304     */
34305     enableColumnMove : true,
34306     
34307     /**
34308     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
34309     */
34310     enableColumnHide : true,
34311     
34312     /**
34313     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
34314     */
34315     enableRowHeightSync : false,
34316     
34317     /**
34318     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
34319     */
34320     stripeRows : true,
34321     
34322     /**
34323     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
34324     */
34325     autoHeight : false,
34326
34327     /**
34328      * @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.
34329      */
34330     autoExpandColumn : false,
34331
34332     /**
34333     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
34334     * Default is 50.
34335     */
34336     autoExpandMin : 50,
34337
34338     /**
34339     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
34340     */
34341     autoExpandMax : 1000,
34342
34343     /**
34344     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
34345     */
34346     view : null,
34347
34348     /**
34349     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
34350     */
34351     loadMask : false,
34352     /**
34353     * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
34354     */
34355     dropTarget: false,
34356     
34357    
34358     
34359     // private
34360     rendered : false,
34361
34362     /**
34363     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
34364     * of a fixed width. Default is false.
34365     */
34366     /**
34367     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
34368     */
34369     /**
34370      * Called once after all setup has been completed and the grid is ready to be rendered.
34371      * @return {Roo.grid.Grid} this
34372      */
34373     render : function()
34374     {
34375         var c = this.container;
34376         // try to detect autoHeight/width mode
34377         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
34378             this.autoHeight = true;
34379         }
34380         var view = this.getView();
34381         view.init(this);
34382
34383         c.on("click", this.onClick, this);
34384         c.on("dblclick", this.onDblClick, this);
34385         c.on("contextmenu", this.onContextMenu, this);
34386         c.on("keydown", this.onKeyDown, this);
34387
34388         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
34389
34390         this.getSelectionModel().init(this);
34391
34392         view.render();
34393
34394         if(this.loadMask){
34395             this.loadMask = new Roo.LoadMask(this.container,
34396                     Roo.apply({store:this.dataSource}, this.loadMask));
34397         }
34398         
34399         
34400         if (this.toolbar && this.toolbar.xtype) {
34401             this.toolbar.container = this.getView().getHeaderPanel(true);
34402             this.toolbar = new Roo.Toolbar(this.toolbar);
34403         }
34404         if (this.footer && this.footer.xtype) {
34405             this.footer.dataSource = this.getDataSource();
34406             this.footer.container = this.getView().getFooterPanel(true);
34407             this.footer = Roo.factory(this.footer, Roo);
34408         }
34409         if (this.dropTarget && this.dropTarget.xtype) {
34410             delete this.dropTarget.xtype;
34411             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
34412         }
34413         
34414         
34415         this.rendered = true;
34416         this.fireEvent('render', this);
34417         return this;
34418     },
34419
34420         /**
34421          * Reconfigures the grid to use a different Store and Column Model.
34422          * The View will be bound to the new objects and refreshed.
34423          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
34424          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
34425          */
34426     reconfigure : function(dataSource, colModel){
34427         if(this.loadMask){
34428             this.loadMask.destroy();
34429             this.loadMask = new Roo.LoadMask(this.container,
34430                     Roo.apply({store:dataSource}, this.loadMask));
34431         }
34432         this.view.bind(dataSource, colModel);
34433         this.dataSource = dataSource;
34434         this.colModel = colModel;
34435         this.view.refresh(true);
34436     },
34437
34438     // private
34439     onKeyDown : function(e){
34440         this.fireEvent("keydown", e);
34441     },
34442
34443     /**
34444      * Destroy this grid.
34445      * @param {Boolean} removeEl True to remove the element
34446      */
34447     destroy : function(removeEl, keepListeners){
34448         if(this.loadMask){
34449             this.loadMask.destroy();
34450         }
34451         var c = this.container;
34452         c.removeAllListeners();
34453         this.view.destroy();
34454         this.colModel.purgeListeners();
34455         if(!keepListeners){
34456             this.purgeListeners();
34457         }
34458         c.update("");
34459         if(removeEl === true){
34460             c.remove();
34461         }
34462     },
34463
34464     // private
34465     processEvent : function(name, e){
34466         this.fireEvent(name, e);
34467         var t = e.getTarget();
34468         var v = this.view;
34469         var header = v.findHeaderIndex(t);
34470         if(header !== false){
34471             this.fireEvent("header" + name, this, header, e);
34472         }else{
34473             var row = v.findRowIndex(t);
34474             var cell = v.findCellIndex(t);
34475             if(row !== false){
34476                 this.fireEvent("row" + name, this, row, e);
34477                 if(cell !== false){
34478                     this.fireEvent("cell" + name, this, row, cell, e);
34479                 }
34480             }
34481         }
34482     },
34483
34484     // private
34485     onClick : function(e){
34486         this.processEvent("click", e);
34487     },
34488
34489     // private
34490     onContextMenu : function(e, t){
34491         this.processEvent("contextmenu", e);
34492     },
34493
34494     // private
34495     onDblClick : function(e){
34496         this.processEvent("dblclick", e);
34497     },
34498
34499     // private
34500     walkCells : function(row, col, step, fn, scope){
34501         var cm = this.colModel, clen = cm.getColumnCount();
34502         var ds = this.dataSource, rlen = ds.getCount(), first = true;
34503         if(step < 0){
34504             if(col < 0){
34505                 row--;
34506                 first = false;
34507             }
34508             while(row >= 0){
34509                 if(!first){
34510                     col = clen-1;
34511                 }
34512                 first = false;
34513                 while(col >= 0){
34514                     if(fn.call(scope || this, row, col, cm) === true){
34515                         return [row, col];
34516                     }
34517                     col--;
34518                 }
34519                 row--;
34520             }
34521         } else {
34522             if(col >= clen){
34523                 row++;
34524                 first = false;
34525             }
34526             while(row < rlen){
34527                 if(!first){
34528                     col = 0;
34529                 }
34530                 first = false;
34531                 while(col < clen){
34532                     if(fn.call(scope || this, row, col, cm) === true){
34533                         return [row, col];
34534                     }
34535                     col++;
34536                 }
34537                 row++;
34538             }
34539         }
34540         return null;
34541     },
34542
34543     // private
34544     getSelections : function(){
34545         return this.selModel.getSelections();
34546     },
34547
34548     /**
34549      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
34550      * but if manual update is required this method will initiate it.
34551      */
34552     autoSize : function(){
34553         if(this.rendered){
34554             this.view.layout();
34555             if(this.view.adjustForScroll){
34556                 this.view.adjustForScroll();
34557             }
34558         }
34559     },
34560
34561     /**
34562      * Returns the grid's underlying element.
34563      * @return {Element} The element
34564      */
34565     getGridEl : function(){
34566         return this.container;
34567     },
34568
34569     // private for compatibility, overridden by editor grid
34570     stopEditing : function(){},
34571
34572     /**
34573      * Returns the grid's SelectionModel.
34574      * @return {SelectionModel}
34575      */
34576     getSelectionModel : function(){
34577         if(!this.selModel){
34578             this.selModel = new Roo.grid.RowSelectionModel();
34579         }
34580         return this.selModel;
34581     },
34582
34583     /**
34584      * Returns the grid's DataSource.
34585      * @return {DataSource}
34586      */
34587     getDataSource : function(){
34588         return this.dataSource;
34589     },
34590
34591     /**
34592      * Returns the grid's ColumnModel.
34593      * @return {ColumnModel}
34594      */
34595     getColumnModel : function(){
34596         return this.colModel;
34597     },
34598
34599     /**
34600      * Returns the grid's GridView object.
34601      * @return {GridView}
34602      */
34603     getView : function(){
34604         if(!this.view){
34605             this.view = new Roo.grid.GridView(this.viewConfig);
34606         }
34607         return this.view;
34608     },
34609     /**
34610      * Called to get grid's drag proxy text, by default returns this.ddText.
34611      * @return {String}
34612      */
34613     getDragDropText : function(){
34614         var count = this.selModel.getCount();
34615         return String.format(this.ddText, count, count == 1 ? '' : 's');
34616     }
34617 });
34618 /**
34619  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
34620  * %0 is replaced with the number of selected rows.
34621  * @type String
34622  */
34623 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
34624  * Based on:
34625  * Ext JS Library 1.1.1
34626  * Copyright(c) 2006-2007, Ext JS, LLC.
34627  *
34628  * Originally Released Under LGPL - original licence link has changed is not relivant.
34629  *
34630  * Fork - LGPL
34631  * <script type="text/javascript">
34632  */
34633  
34634 Roo.grid.AbstractGridView = function(){
34635         this.grid = null;
34636         
34637         this.events = {
34638             "beforerowremoved" : true,
34639             "beforerowsinserted" : true,
34640             "beforerefresh" : true,
34641             "rowremoved" : true,
34642             "rowsinserted" : true,
34643             "rowupdated" : true,
34644             "refresh" : true
34645         };
34646     Roo.grid.AbstractGridView.superclass.constructor.call(this);
34647 };
34648
34649 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
34650     rowClass : "x-grid-row",
34651     cellClass : "x-grid-cell",
34652     tdClass : "x-grid-td",
34653     hdClass : "x-grid-hd",
34654     splitClass : "x-grid-hd-split",
34655     
34656         init: function(grid){
34657         this.grid = grid;
34658                 var cid = this.grid.getGridEl().id;
34659         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
34660         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
34661         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
34662         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
34663         },
34664         
34665         getColumnRenderers : function(){
34666         var renderers = [];
34667         var cm = this.grid.colModel;
34668         var colCount = cm.getColumnCount();
34669         for(var i = 0; i < colCount; i++){
34670             renderers[i] = cm.getRenderer(i);
34671         }
34672         return renderers;
34673     },
34674     
34675     getColumnIds : function(){
34676         var ids = [];
34677         var cm = this.grid.colModel;
34678         var colCount = cm.getColumnCount();
34679         for(var i = 0; i < colCount; i++){
34680             ids[i] = cm.getColumnId(i);
34681         }
34682         return ids;
34683     },
34684     
34685     getDataIndexes : function(){
34686         if(!this.indexMap){
34687             this.indexMap = this.buildIndexMap();
34688         }
34689         return this.indexMap.colToData;
34690     },
34691     
34692     getColumnIndexByDataIndex : function(dataIndex){
34693         if(!this.indexMap){
34694             this.indexMap = this.buildIndexMap();
34695         }
34696         return this.indexMap.dataToCol[dataIndex];
34697     },
34698     
34699     /**
34700      * Set a css style for a column dynamically. 
34701      * @param {Number} colIndex The index of the column
34702      * @param {String} name The css property name
34703      * @param {String} value The css value
34704      */
34705     setCSSStyle : function(colIndex, name, value){
34706         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
34707         Roo.util.CSS.updateRule(selector, name, value);
34708     },
34709     
34710     generateRules : function(cm){
34711         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
34712         Roo.util.CSS.removeStyleSheet(rulesId);
34713         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34714             var cid = cm.getColumnId(i);
34715             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
34716                          this.tdSelector, cid, " {\n}\n",
34717                          this.hdSelector, cid, " {\n}\n",
34718                          this.splitSelector, cid, " {\n}\n");
34719         }
34720         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34721     }
34722 });/*
34723  * Based on:
34724  * Ext JS Library 1.1.1
34725  * Copyright(c) 2006-2007, Ext JS, LLC.
34726  *
34727  * Originally Released Under LGPL - original licence link has changed is not relivant.
34728  *
34729  * Fork - LGPL
34730  * <script type="text/javascript">
34731  */
34732
34733 // private
34734 // This is a support class used internally by the Grid components
34735 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
34736     this.grid = grid;
34737     this.view = grid.getView();
34738     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34739     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
34740     if(hd2){
34741         this.setHandleElId(Roo.id(hd));
34742         this.setOuterHandleElId(Roo.id(hd2));
34743     }
34744     this.scroll = false;
34745 };
34746 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
34747     maxDragWidth: 120,
34748     getDragData : function(e){
34749         var t = Roo.lib.Event.getTarget(e);
34750         var h = this.view.findHeaderCell(t);
34751         if(h){
34752             return {ddel: h.firstChild, header:h};
34753         }
34754         return false;
34755     },
34756
34757     onInitDrag : function(e){
34758         this.view.headersDisabled = true;
34759         var clone = this.dragData.ddel.cloneNode(true);
34760         clone.id = Roo.id();
34761         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
34762         this.proxy.update(clone);
34763         return true;
34764     },
34765
34766     afterValidDrop : function(){
34767         var v = this.view;
34768         setTimeout(function(){
34769             v.headersDisabled = false;
34770         }, 50);
34771     },
34772
34773     afterInvalidDrop : function(){
34774         var v = this.view;
34775         setTimeout(function(){
34776             v.headersDisabled = false;
34777         }, 50);
34778     }
34779 });
34780 /*
34781  * Based on:
34782  * Ext JS Library 1.1.1
34783  * Copyright(c) 2006-2007, Ext JS, LLC.
34784  *
34785  * Originally Released Under LGPL - original licence link has changed is not relivant.
34786  *
34787  * Fork - LGPL
34788  * <script type="text/javascript">
34789  */
34790 // private
34791 // This is a support class used internally by the Grid components
34792 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
34793     this.grid = grid;
34794     this.view = grid.getView();
34795     // split the proxies so they don't interfere with mouse events
34796     this.proxyTop = Roo.DomHelper.append(document.body, {
34797         cls:"col-move-top", html:"&#160;"
34798     }, true);
34799     this.proxyBottom = Roo.DomHelper.append(document.body, {
34800         cls:"col-move-bottom", html:"&#160;"
34801     }, true);
34802     this.proxyTop.hide = this.proxyBottom.hide = function(){
34803         this.setLeftTop(-100,-100);
34804         this.setStyle("visibility", "hidden");
34805     };
34806     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
34807     // temporarily disabled
34808     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
34809     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
34810 };
34811 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
34812     proxyOffsets : [-4, -9],
34813     fly: Roo.Element.fly,
34814
34815     getTargetFromEvent : function(e){
34816         var t = Roo.lib.Event.getTarget(e);
34817         var cindex = this.view.findCellIndex(t);
34818         if(cindex !== false){
34819             return this.view.getHeaderCell(cindex);
34820         }
34821         return null;
34822     },
34823
34824     nextVisible : function(h){
34825         var v = this.view, cm = this.grid.colModel;
34826         h = h.nextSibling;
34827         while(h){
34828             if(!cm.isHidden(v.getCellIndex(h))){
34829                 return h;
34830             }
34831             h = h.nextSibling;
34832         }
34833         return null;
34834     },
34835
34836     prevVisible : function(h){
34837         var v = this.view, cm = this.grid.colModel;
34838         h = h.prevSibling;
34839         while(h){
34840             if(!cm.isHidden(v.getCellIndex(h))){
34841                 return h;
34842             }
34843             h = h.prevSibling;
34844         }
34845         return null;
34846     },
34847
34848     positionIndicator : function(h, n, e){
34849         var x = Roo.lib.Event.getPageX(e);
34850         var r = Roo.lib.Dom.getRegion(n.firstChild);
34851         var px, pt, py = r.top + this.proxyOffsets[1];
34852         if((r.right - x) <= (r.right-r.left)/2){
34853             px = r.right+this.view.borderWidth;
34854             pt = "after";
34855         }else{
34856             px = r.left;
34857             pt = "before";
34858         }
34859         var oldIndex = this.view.getCellIndex(h);
34860         var newIndex = this.view.getCellIndex(n);
34861
34862         if(this.grid.colModel.isFixed(newIndex)){
34863             return false;
34864         }
34865
34866         var locked = this.grid.colModel.isLocked(newIndex);
34867
34868         if(pt == "after"){
34869             newIndex++;
34870         }
34871         if(oldIndex < newIndex){
34872             newIndex--;
34873         }
34874         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
34875             return false;
34876         }
34877         px +=  this.proxyOffsets[0];
34878         this.proxyTop.setLeftTop(px, py);
34879         this.proxyTop.show();
34880         if(!this.bottomOffset){
34881             this.bottomOffset = this.view.mainHd.getHeight();
34882         }
34883         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
34884         this.proxyBottom.show();
34885         return pt;
34886     },
34887
34888     onNodeEnter : function(n, dd, e, data){
34889         if(data.header != n){
34890             this.positionIndicator(data.header, n, e);
34891         }
34892     },
34893
34894     onNodeOver : function(n, dd, e, data){
34895         var result = false;
34896         if(data.header != n){
34897             result = this.positionIndicator(data.header, n, e);
34898         }
34899         if(!result){
34900             this.proxyTop.hide();
34901             this.proxyBottom.hide();
34902         }
34903         return result ? this.dropAllowed : this.dropNotAllowed;
34904     },
34905
34906     onNodeOut : function(n, dd, e, data){
34907         this.proxyTop.hide();
34908         this.proxyBottom.hide();
34909     },
34910
34911     onNodeDrop : function(n, dd, e, data){
34912         var h = data.header;
34913         if(h != n){
34914             var cm = this.grid.colModel;
34915             var x = Roo.lib.Event.getPageX(e);
34916             var r = Roo.lib.Dom.getRegion(n.firstChild);
34917             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
34918             var oldIndex = this.view.getCellIndex(h);
34919             var newIndex = this.view.getCellIndex(n);
34920             var locked = cm.isLocked(newIndex);
34921             if(pt == "after"){
34922                 newIndex++;
34923             }
34924             if(oldIndex < newIndex){
34925                 newIndex--;
34926             }
34927             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
34928                 return false;
34929             }
34930             cm.setLocked(oldIndex, locked, true);
34931             cm.moveColumn(oldIndex, newIndex);
34932             this.grid.fireEvent("columnmove", oldIndex, newIndex);
34933             return true;
34934         }
34935         return false;
34936     }
34937 });
34938 /*
34939  * Based on:
34940  * Ext JS Library 1.1.1
34941  * Copyright(c) 2006-2007, Ext JS, LLC.
34942  *
34943  * Originally Released Under LGPL - original licence link has changed is not relivant.
34944  *
34945  * Fork - LGPL
34946  * <script type="text/javascript">
34947  */
34948   
34949 /**
34950  * @class Roo.grid.GridView
34951  * @extends Roo.util.Observable
34952  *
34953  * @constructor
34954  * @param {Object} config
34955  */
34956 Roo.grid.GridView = function(config){
34957     Roo.grid.GridView.superclass.constructor.call(this);
34958     this.el = null;
34959
34960     Roo.apply(this, config);
34961 };
34962
34963 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
34964
34965     
34966     rowClass : "x-grid-row",
34967
34968     cellClass : "x-grid-col",
34969
34970     tdClass : "x-grid-td",
34971
34972     hdClass : "x-grid-hd",
34973
34974     splitClass : "x-grid-split",
34975
34976     sortClasses : ["sort-asc", "sort-desc"],
34977
34978     enableMoveAnim : false,
34979
34980     hlColor: "C3DAF9",
34981
34982     dh : Roo.DomHelper,
34983
34984     fly : Roo.Element.fly,
34985
34986     css : Roo.util.CSS,
34987
34988     borderWidth: 1,
34989
34990     splitOffset: 3,
34991
34992     scrollIncrement : 22,
34993
34994     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
34995
34996     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
34997
34998     bind : function(ds, cm){
34999         if(this.ds){
35000             this.ds.un("load", this.onLoad, this);
35001             this.ds.un("datachanged", this.onDataChange, this);
35002             this.ds.un("add", this.onAdd, this);
35003             this.ds.un("remove", this.onRemove, this);
35004             this.ds.un("update", this.onUpdate, this);
35005             this.ds.un("clear", this.onClear, this);
35006         }
35007         if(ds){
35008             ds.on("load", this.onLoad, this);
35009             ds.on("datachanged", this.onDataChange, this);
35010             ds.on("add", this.onAdd, this);
35011             ds.on("remove", this.onRemove, this);
35012             ds.on("update", this.onUpdate, this);
35013             ds.on("clear", this.onClear, this);
35014         }
35015         this.ds = ds;
35016
35017         if(this.cm){
35018             this.cm.un("widthchange", this.onColWidthChange, this);
35019             this.cm.un("headerchange", this.onHeaderChange, this);
35020             this.cm.un("hiddenchange", this.onHiddenChange, this);
35021             this.cm.un("columnmoved", this.onColumnMove, this);
35022             this.cm.un("columnlockchange", this.onColumnLock, this);
35023         }
35024         if(cm){
35025             this.generateRules(cm);
35026             cm.on("widthchange", this.onColWidthChange, this);
35027             cm.on("headerchange", this.onHeaderChange, this);
35028             cm.on("hiddenchange", this.onHiddenChange, this);
35029             cm.on("columnmoved", this.onColumnMove, this);
35030             cm.on("columnlockchange", this.onColumnLock, this);
35031         }
35032         this.cm = cm;
35033     },
35034
35035     init: function(grid){
35036         Roo.grid.GridView.superclass.init.call(this, grid);
35037
35038         this.bind(grid.dataSource, grid.colModel);
35039
35040         grid.on("headerclick", this.handleHeaderClick, this);
35041
35042         if(grid.trackMouseOver){
35043             grid.on("mouseover", this.onRowOver, this);
35044             grid.on("mouseout", this.onRowOut, this);
35045         }
35046         grid.cancelTextSelection = function(){};
35047         this.gridId = grid.id;
35048
35049         var tpls = this.templates || {};
35050
35051         if(!tpls.master){
35052             tpls.master = new Roo.Template(
35053                '<div class="x-grid" hidefocus="true">',
35054                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
35055                   '<div class="x-grid-topbar"></div>',
35056                   '<div class="x-grid-scroller"><div></div></div>',
35057                   '<div class="x-grid-locked">',
35058                       '<div class="x-grid-header">{lockedHeader}</div>',
35059                       '<div class="x-grid-body">{lockedBody}</div>',
35060                   "</div>",
35061                   '<div class="x-grid-viewport">',
35062                       '<div class="x-grid-header">{header}</div>',
35063                       '<div class="x-grid-body">{body}</div>',
35064                   "</div>",
35065                   '<div class="x-grid-bottombar"></div>',
35066                  
35067                   '<div class="x-grid-resize-proxy">&#160;</div>',
35068                "</div>"
35069             );
35070             tpls.master.disableformats = true;
35071         }
35072
35073         if(!tpls.header){
35074             tpls.header = new Roo.Template(
35075                '<table border="0" cellspacing="0" cellpadding="0">',
35076                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
35077                "</table>{splits}"
35078             );
35079             tpls.header.disableformats = true;
35080         }
35081         tpls.header.compile();
35082
35083         if(!tpls.hcell){
35084             tpls.hcell = new Roo.Template(
35085                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
35086                 '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
35087                 "</div></td>"
35088              );
35089              tpls.hcell.disableFormats = true;
35090         }
35091         tpls.hcell.compile();
35092
35093         if(!tpls.hsplit){
35094             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
35095             tpls.hsplit.disableFormats = true;
35096         }
35097         tpls.hsplit.compile();
35098
35099         if(!tpls.body){
35100             tpls.body = new Roo.Template(
35101                '<table border="0" cellspacing="0" cellpadding="0">',
35102                "<tbody>{rows}</tbody>",
35103                "</table>"
35104             );
35105             tpls.body.disableFormats = true;
35106         }
35107         tpls.body.compile();
35108
35109         if(!tpls.row){
35110             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
35111             tpls.row.disableFormats = true;
35112         }
35113         tpls.row.compile();
35114
35115         if(!tpls.cell){
35116             tpls.cell = new Roo.Template(
35117                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
35118                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
35119                 "</td>"
35120             );
35121             tpls.cell.disableFormats = true;
35122         }
35123         tpls.cell.compile();
35124
35125         this.templates = tpls;
35126     },
35127
35128     // remap these for backwards compat
35129     onColWidthChange : function(){
35130         this.updateColumns.apply(this, arguments);
35131     },
35132     onHeaderChange : function(){
35133         this.updateHeaders.apply(this, arguments);
35134     }, 
35135     onHiddenChange : function(){
35136         this.handleHiddenChange.apply(this, arguments);
35137     },
35138     onColumnMove : function(){
35139         this.handleColumnMove.apply(this, arguments);
35140     },
35141     onColumnLock : function(){
35142         this.handleLockChange.apply(this, arguments);
35143     },
35144
35145     onDataChange : function(){
35146         this.refresh();
35147         this.updateHeaderSortState();
35148     },
35149
35150     onClear : function(){
35151         this.refresh();
35152     },
35153
35154     onUpdate : function(ds, record){
35155         this.refreshRow(record);
35156     },
35157
35158     refreshRow : function(record){
35159         var ds = this.ds, index;
35160         if(typeof record == 'number'){
35161             index = record;
35162             record = ds.getAt(index);
35163         }else{
35164             index = ds.indexOf(record);
35165         }
35166         this.insertRows(ds, index, index, true);
35167         this.onRemove(ds, record, index+1, true);
35168         this.syncRowHeights(index, index);
35169         this.layout();
35170         this.fireEvent("rowupdated", this, index, record);
35171     },
35172
35173     onAdd : function(ds, records, index){
35174         this.insertRows(ds, index, index + (records.length-1));
35175     },
35176
35177     onRemove : function(ds, record, index, isUpdate){
35178         if(isUpdate !== true){
35179             this.fireEvent("beforerowremoved", this, index, record);
35180         }
35181         var bt = this.getBodyTable(), lt = this.getLockedTable();
35182         if(bt.rows[index]){
35183             bt.firstChild.removeChild(bt.rows[index]);
35184         }
35185         if(lt.rows[index]){
35186             lt.firstChild.removeChild(lt.rows[index]);
35187         }
35188         if(isUpdate !== true){
35189             this.stripeRows(index);
35190             this.syncRowHeights(index, index);
35191             this.layout();
35192             this.fireEvent("rowremoved", this, index, record);
35193         }
35194     },
35195
35196     onLoad : function(){
35197         this.scrollToTop();
35198     },
35199
35200     /**
35201      * Scrolls the grid to the top
35202      */
35203     scrollToTop : function(){
35204         if(this.scroller){
35205             this.scroller.dom.scrollTop = 0;
35206             this.syncScroll();
35207         }
35208     },
35209
35210     /**
35211      * Gets a panel in the header of the grid that can be used for toolbars etc.
35212      * After modifying the contents of this panel a call to grid.autoSize() may be
35213      * required to register any changes in size.
35214      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
35215      * @return Roo.Element
35216      */
35217     getHeaderPanel : function(doShow){
35218         if(doShow){
35219             this.headerPanel.show();
35220         }
35221         return this.headerPanel;
35222     },
35223
35224     /**
35225      * Gets a panel in the footer of the grid that can be used for toolbars etc.
35226      * After modifying the contents of this panel a call to grid.autoSize() may be
35227      * required to register any changes in size.
35228      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
35229      * @return Roo.Element
35230      */
35231     getFooterPanel : function(doShow){
35232         if(doShow){
35233             this.footerPanel.show();
35234         }
35235         return this.footerPanel;
35236     },
35237
35238     initElements : function(){
35239         var E = Roo.Element;
35240         var el = this.grid.getGridEl().dom.firstChild;
35241         var cs = el.childNodes;
35242
35243         this.el = new E(el);
35244         
35245          this.focusEl = new E(el.firstChild);
35246         this.focusEl.swallowEvent("click", true);
35247         
35248         this.headerPanel = new E(cs[1]);
35249         this.headerPanel.enableDisplayMode("block");
35250
35251         this.scroller = new E(cs[2]);
35252         this.scrollSizer = new E(this.scroller.dom.firstChild);
35253
35254         this.lockedWrap = new E(cs[3]);
35255         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
35256         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
35257
35258         this.mainWrap = new E(cs[4]);
35259         this.mainHd = new E(this.mainWrap.dom.firstChild);
35260         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
35261
35262         this.footerPanel = new E(cs[5]);
35263         this.footerPanel.enableDisplayMode("block");
35264
35265         this.resizeProxy = new E(cs[6]);
35266
35267         this.headerSelector = String.format(
35268            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
35269            this.lockedHd.id, this.mainHd.id
35270         );
35271
35272         this.splitterSelector = String.format(
35273            '#{0} div.x-grid-split, #{1} div.x-grid-split',
35274            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
35275         );
35276     },
35277     idToCssName : function(s)
35278     {
35279         return s.replace(/[^a-z0-9]+/ig, '-');
35280     },
35281
35282     getHeaderCell : function(index){
35283         return Roo.DomQuery.select(this.headerSelector)[index];
35284     },
35285
35286     getHeaderCellMeasure : function(index){
35287         return this.getHeaderCell(index).firstChild;
35288     },
35289
35290     getHeaderCellText : function(index){
35291         return this.getHeaderCell(index).firstChild.firstChild;
35292     },
35293
35294     getLockedTable : function(){
35295         return this.lockedBody.dom.firstChild;
35296     },
35297
35298     getBodyTable : function(){
35299         return this.mainBody.dom.firstChild;
35300     },
35301
35302     getLockedRow : function(index){
35303         return this.getLockedTable().rows[index];
35304     },
35305
35306     getRow : function(index){
35307         return this.getBodyTable().rows[index];
35308     },
35309
35310     getRowComposite : function(index){
35311         if(!this.rowEl){
35312             this.rowEl = new Roo.CompositeElementLite();
35313         }
35314         var els = [], lrow, mrow;
35315         if(lrow = this.getLockedRow(index)){
35316             els.push(lrow);
35317         }
35318         if(mrow = this.getRow(index)){
35319             els.push(mrow);
35320         }
35321         this.rowEl.elements = els;
35322         return this.rowEl;
35323     },
35324     /**
35325      * Gets the 'td' of the cell
35326      * 
35327      * @param {Integer} rowIndex row to select
35328      * @param {Integer} colIndex column to select
35329      * 
35330      * @return {Object} 
35331      */
35332     getCell : function(rowIndex, colIndex){
35333         var locked = this.cm.getLockedCount();
35334         var source;
35335         if(colIndex < locked){
35336             source = this.lockedBody.dom.firstChild;
35337         }else{
35338             source = this.mainBody.dom.firstChild;
35339             colIndex -= locked;
35340         }
35341         return source.rows[rowIndex].childNodes[colIndex];
35342     },
35343
35344     getCellText : function(rowIndex, colIndex){
35345         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
35346     },
35347
35348     getCellBox : function(cell){
35349         var b = this.fly(cell).getBox();
35350         if(Roo.isOpera){ // opera fails to report the Y
35351             b.y = cell.offsetTop + this.mainBody.getY();
35352         }
35353         return b;
35354     },
35355
35356     getCellIndex : function(cell){
35357         var id = String(cell.className).match(this.cellRE);
35358         if(id){
35359             return parseInt(id[1], 10);
35360         }
35361         return 0;
35362     },
35363
35364     findHeaderIndex : function(n){
35365         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35366         return r ? this.getCellIndex(r) : false;
35367     },
35368
35369     findHeaderCell : function(n){
35370         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
35371         return r ? r : false;
35372     },
35373
35374     findRowIndex : function(n){
35375         if(!n){
35376             return false;
35377         }
35378         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
35379         return r ? r.rowIndex : false;
35380     },
35381
35382     findCellIndex : function(node){
35383         var stop = this.el.dom;
35384         while(node && node != stop){
35385             if(this.findRE.test(node.className)){
35386                 return this.getCellIndex(node);
35387             }
35388             node = node.parentNode;
35389         }
35390         return false;
35391     },
35392
35393     getColumnId : function(index){
35394         return this.cm.getColumnId(index);
35395     },
35396
35397     getSplitters : function()
35398     {
35399         if(this.splitterSelector){
35400            return Roo.DomQuery.select(this.splitterSelector);
35401         }else{
35402             return null;
35403       }
35404     },
35405
35406     getSplitter : function(index){
35407         return this.getSplitters()[index];
35408     },
35409
35410     onRowOver : function(e, t){
35411         var row;
35412         if((row = this.findRowIndex(t)) !== false){
35413             this.getRowComposite(row).addClass("x-grid-row-over");
35414         }
35415     },
35416
35417     onRowOut : function(e, t){
35418         var row;
35419         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
35420             this.getRowComposite(row).removeClass("x-grid-row-over");
35421         }
35422     },
35423
35424     renderHeaders : function(){
35425         var cm = this.cm;
35426         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
35427         var cb = [], lb = [], sb = [], lsb = [], p = {};
35428         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35429             p.cellId = "x-grid-hd-0-" + i;
35430             p.splitId = "x-grid-csplit-0-" + i;
35431             p.id = cm.getColumnId(i);
35432             p.title = cm.getColumnTooltip(i) || "";
35433             p.value = cm.getColumnHeader(i) || "";
35434             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
35435             if(!cm.isLocked(i)){
35436                 cb[cb.length] = ct.apply(p);
35437                 sb[sb.length] = st.apply(p);
35438             }else{
35439                 lb[lb.length] = ct.apply(p);
35440                 lsb[lsb.length] = st.apply(p);
35441             }
35442         }
35443         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
35444                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
35445     },
35446
35447     updateHeaders : function(){
35448         var html = this.renderHeaders();
35449         this.lockedHd.update(html[0]);
35450         this.mainHd.update(html[1]);
35451     },
35452
35453     /**
35454      * Focuses the specified row.
35455      * @param {Number} row The row index
35456      */
35457     focusRow : function(row)
35458     {
35459         //Roo.log('GridView.focusRow');
35460         var x = this.scroller.dom.scrollLeft;
35461         this.focusCell(row, 0, false);
35462         this.scroller.dom.scrollLeft = x;
35463     },
35464
35465     /**
35466      * Focuses the specified cell.
35467      * @param {Number} row The row index
35468      * @param {Number} col The column index
35469      * @param {Boolean} hscroll false to disable horizontal scrolling
35470      */
35471     focusCell : function(row, col, hscroll)
35472     {
35473         //Roo.log('GridView.focusCell');
35474         var el = this.ensureVisible(row, col, hscroll);
35475         this.focusEl.alignTo(el, "tl-tl");
35476         if(Roo.isGecko){
35477             this.focusEl.focus();
35478         }else{
35479             this.focusEl.focus.defer(1, this.focusEl);
35480         }
35481     },
35482
35483     /**
35484      * Scrolls the specified cell into view
35485      * @param {Number} row The row index
35486      * @param {Number} col The column index
35487      * @param {Boolean} hscroll false to disable horizontal scrolling
35488      */
35489     ensureVisible : function(row, col, hscroll)
35490     {
35491         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
35492         //return null; //disable for testing.
35493         if(typeof row != "number"){
35494             row = row.rowIndex;
35495         }
35496         if(row < 0 && row >= this.ds.getCount()){
35497             return  null;
35498         }
35499         col = (col !== undefined ? col : 0);
35500         var cm = this.grid.colModel;
35501         while(cm.isHidden(col)){
35502             col++;
35503         }
35504
35505         var el = this.getCell(row, col);
35506         if(!el){
35507             return null;
35508         }
35509         var c = this.scroller.dom;
35510
35511         var ctop = parseInt(el.offsetTop, 10);
35512         var cleft = parseInt(el.offsetLeft, 10);
35513         var cbot = ctop + el.offsetHeight;
35514         var cright = cleft + el.offsetWidth;
35515         
35516         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
35517         var stop = parseInt(c.scrollTop, 10);
35518         var sleft = parseInt(c.scrollLeft, 10);
35519         var sbot = stop + ch;
35520         var sright = sleft + c.clientWidth;
35521         /*
35522         Roo.log('GridView.ensureVisible:' +
35523                 ' ctop:' + ctop +
35524                 ' c.clientHeight:' + c.clientHeight +
35525                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
35526                 ' stop:' + stop +
35527                 ' cbot:' + cbot +
35528                 ' sbot:' + sbot +
35529                 ' ch:' + ch  
35530                 );
35531         */
35532         if(ctop < stop){
35533              c.scrollTop = ctop;
35534             //Roo.log("set scrolltop to ctop DISABLE?");
35535         }else if(cbot > sbot){
35536             //Roo.log("set scrolltop to cbot-ch");
35537             c.scrollTop = cbot-ch;
35538         }
35539         
35540         if(hscroll !== false){
35541             if(cleft < sleft){
35542                 c.scrollLeft = cleft;
35543             }else if(cright > sright){
35544                 c.scrollLeft = cright-c.clientWidth;
35545             }
35546         }
35547          
35548         return el;
35549     },
35550
35551     updateColumns : function(){
35552         this.grid.stopEditing();
35553         var cm = this.grid.colModel, colIds = this.getColumnIds();
35554         //var totalWidth = cm.getTotalWidth();
35555         var pos = 0;
35556         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35557             //if(cm.isHidden(i)) continue;
35558             var w = cm.getColumnWidth(i);
35559             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35560             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
35561         }
35562         this.updateSplitters();
35563     },
35564
35565     generateRules : function(cm){
35566         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
35567         Roo.util.CSS.removeStyleSheet(rulesId);
35568         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35569             var cid = cm.getColumnId(i);
35570             var align = '';
35571             if(cm.config[i].align){
35572                 align = 'text-align:'+cm.config[i].align+';';
35573             }
35574             var hidden = '';
35575             if(cm.isHidden(i)){
35576                 hidden = 'display:none;';
35577             }
35578             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
35579             ruleBuf.push(
35580                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
35581                     this.hdSelector, cid, " {\n", align, width, "}\n",
35582                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
35583                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
35584         }
35585         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
35586     },
35587
35588     updateSplitters : function(){
35589         var cm = this.cm, s = this.getSplitters();
35590         if(s){ // splitters not created yet
35591             var pos = 0, locked = true;
35592             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
35593                 if(cm.isHidden(i)) continue;
35594                 var w = cm.getColumnWidth(i); // make sure it's a number
35595                 if(!cm.isLocked(i) && locked){
35596                     pos = 0;
35597                     locked = false;
35598                 }
35599                 pos += w;
35600                 s[i].style.left = (pos-this.splitOffset) + "px";
35601             }
35602         }
35603     },
35604
35605     handleHiddenChange : function(colModel, colIndex, hidden){
35606         if(hidden){
35607             this.hideColumn(colIndex);
35608         }else{
35609             this.unhideColumn(colIndex);
35610         }
35611     },
35612
35613     hideColumn : function(colIndex){
35614         var cid = this.getColumnId(colIndex);
35615         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
35616         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
35617         if(Roo.isSafari){
35618             this.updateHeaders();
35619         }
35620         this.updateSplitters();
35621         this.layout();
35622     },
35623
35624     unhideColumn : function(colIndex){
35625         var cid = this.getColumnId(colIndex);
35626         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
35627         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
35628
35629         if(Roo.isSafari){
35630             this.updateHeaders();
35631         }
35632         this.updateSplitters();
35633         this.layout();
35634     },
35635
35636     insertRows : function(dm, firstRow, lastRow, isUpdate){
35637         if(firstRow == 0 && lastRow == dm.getCount()-1){
35638             this.refresh();
35639         }else{
35640             if(!isUpdate){
35641                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
35642             }
35643             var s = this.getScrollState();
35644             var markup = this.renderRows(firstRow, lastRow);
35645             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
35646             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
35647             this.restoreScroll(s);
35648             if(!isUpdate){
35649                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
35650                 this.syncRowHeights(firstRow, lastRow);
35651                 this.stripeRows(firstRow);
35652                 this.layout();
35653             }
35654         }
35655     },
35656
35657     bufferRows : function(markup, target, index){
35658         var before = null, trows = target.rows, tbody = target.tBodies[0];
35659         if(index < trows.length){
35660             before = trows[index];
35661         }
35662         var b = document.createElement("div");
35663         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
35664         var rows = b.firstChild.rows;
35665         for(var i = 0, len = rows.length; i < len; i++){
35666             if(before){
35667                 tbody.insertBefore(rows[0], before);
35668             }else{
35669                 tbody.appendChild(rows[0]);
35670             }
35671         }
35672         b.innerHTML = "";
35673         b = null;
35674     },
35675
35676     deleteRows : function(dm, firstRow, lastRow){
35677         if(dm.getRowCount()<1){
35678             this.fireEvent("beforerefresh", this);
35679             this.mainBody.update("");
35680             this.lockedBody.update("");
35681             this.fireEvent("refresh", this);
35682         }else{
35683             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
35684             var bt = this.getBodyTable();
35685             var tbody = bt.firstChild;
35686             var rows = bt.rows;
35687             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
35688                 tbody.removeChild(rows[firstRow]);
35689             }
35690             this.stripeRows(firstRow);
35691             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
35692         }
35693     },
35694
35695     updateRows : function(dataSource, firstRow, lastRow){
35696         var s = this.getScrollState();
35697         this.refresh();
35698         this.restoreScroll(s);
35699     },
35700
35701     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
35702         if(!noRefresh){
35703            this.refresh();
35704         }
35705         this.updateHeaderSortState();
35706     },
35707
35708     getScrollState : function(){
35709         
35710         var sb = this.scroller.dom;
35711         return {left: sb.scrollLeft, top: sb.scrollTop};
35712     },
35713
35714     stripeRows : function(startRow){
35715         if(!this.grid.stripeRows || this.ds.getCount() < 1){
35716             return;
35717         }
35718         startRow = startRow || 0;
35719         var rows = this.getBodyTable().rows;
35720         var lrows = this.getLockedTable().rows;
35721         var cls = ' x-grid-row-alt ';
35722         for(var i = startRow, len = rows.length; i < len; i++){
35723             var row = rows[i], lrow = lrows[i];
35724             var isAlt = ((i+1) % 2 == 0);
35725             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
35726             if(isAlt == hasAlt){
35727                 continue;
35728             }
35729             if(isAlt){
35730                 row.className += " x-grid-row-alt";
35731             }else{
35732                 row.className = row.className.replace("x-grid-row-alt", "");
35733             }
35734             if(lrow){
35735                 lrow.className = row.className;
35736             }
35737         }
35738     },
35739
35740     restoreScroll : function(state){
35741         //Roo.log('GridView.restoreScroll');
35742         var sb = this.scroller.dom;
35743         sb.scrollLeft = state.left;
35744         sb.scrollTop = state.top;
35745         this.syncScroll();
35746     },
35747
35748     syncScroll : function(){
35749         //Roo.log('GridView.syncScroll');
35750         var sb = this.scroller.dom;
35751         var sh = this.mainHd.dom;
35752         var bs = this.mainBody.dom;
35753         var lv = this.lockedBody.dom;
35754         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
35755         lv.scrollTop = bs.scrollTop = sb.scrollTop;
35756     },
35757
35758     handleScroll : function(e){
35759         this.syncScroll();
35760         var sb = this.scroller.dom;
35761         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
35762         e.stopEvent();
35763     },
35764
35765     handleWheel : function(e){
35766         var d = e.getWheelDelta();
35767         this.scroller.dom.scrollTop -= d*22;
35768         // set this here to prevent jumpy scrolling on large tables
35769         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
35770         e.stopEvent();
35771     },
35772
35773     renderRows : function(startRow, endRow){
35774         // pull in all the crap needed to render rows
35775         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
35776         var colCount = cm.getColumnCount();
35777
35778         if(ds.getCount() < 1){
35779             return ["", ""];
35780         }
35781
35782         // build a map for all the columns
35783         var cs = [];
35784         for(var i = 0; i < colCount; i++){
35785             var name = cm.getDataIndex(i);
35786             cs[i] = {
35787                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
35788                 renderer : cm.getRenderer(i),
35789                 id : cm.getColumnId(i),
35790                 locked : cm.isLocked(i)
35791             };
35792         }
35793
35794         startRow = startRow || 0;
35795         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
35796
35797         // records to render
35798         var rs = ds.getRange(startRow, endRow);
35799
35800         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
35801     },
35802
35803     // As much as I hate to duplicate code, this was branched because FireFox really hates
35804     // [].join("") on strings. The performance difference was substantial enough to
35805     // branch this function
35806     doRender : Roo.isGecko ?
35807             function(cs, rs, ds, startRow, colCount, stripe){
35808                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35809                 // buffers
35810                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35811                 
35812                 var hasListener = this.grid.hasListener('rowclass');
35813                 var rowcfg = {};
35814                 for(var j = 0, len = rs.length; j < len; j++){
35815                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
35816                     for(var i = 0; i < colCount; i++){
35817                         c = cs[i];
35818                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35819                         p.id = c.id;
35820                         p.css = p.attr = "";
35821                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35822                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35823                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35824                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35825                         }
35826                         var markup = ct.apply(p);
35827                         if(!c.locked){
35828                             cb+= markup;
35829                         }else{
35830                             lcb+= markup;
35831                         }
35832                     }
35833                     var alt = [];
35834                     if(stripe && ((rowIndex+1) % 2 == 0)){
35835                         alt.push("x-grid-row-alt")
35836                     }
35837                     if(r.dirty){
35838                         alt.push(  " x-grid-dirty-row");
35839                     }
35840                     rp.cells = lcb;
35841                     if(this.getRowClass){
35842                         alt.push(this.getRowClass(r, rowIndex));
35843                     }
35844                     if (hasListener) {
35845                         rowcfg = {
35846                              
35847                             record: r,
35848                             rowIndex : rowIndex,
35849                             rowClass : ''
35850                         }
35851                         this.grid.fireEvent('rowclass', this, rowcfg);
35852                         alt.push(rowcfg.rowClass);
35853                     }
35854                     rp.alt = alt.join(" ");
35855                     lbuf+= rt.apply(rp);
35856                     rp.cells = cb;
35857                     buf+=  rt.apply(rp);
35858                 }
35859                 return [lbuf, buf];
35860             } :
35861             function(cs, rs, ds, startRow, colCount, stripe){
35862                 var ts = this.templates, ct = ts.cell, rt = ts.row;
35863                 // buffers
35864                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
35865                 var hasListener = this.grid.hasListener('rowclass');
35866  
35867                 var rowcfg = {};
35868                 for(var j = 0, len = rs.length; j < len; j++){
35869                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
35870                     for(var i = 0; i < colCount; i++){
35871                         c = cs[i];
35872                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
35873                         p.id = c.id;
35874                         p.css = p.attr = "";
35875                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
35876                         if(p.value == undefined || p.value === "") p.value = "&#160;";
35877                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
35878                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
35879                         }
35880                         
35881                         var markup = ct.apply(p);
35882                         if(!c.locked){
35883                             cb[cb.length] = markup;
35884                         }else{
35885                             lcb[lcb.length] = markup;
35886                         }
35887                     }
35888                     var alt = [];
35889                     if(stripe && ((rowIndex+1) % 2 == 0)){
35890                         alt.push( "x-grid-row-alt");
35891                     }
35892                     if(r.dirty){
35893                         alt.push(" x-grid-dirty-row");
35894                     }
35895                     rp.cells = lcb;
35896                     if(this.getRowClass){
35897                         alt.push( this.getRowClass(r, rowIndex));
35898                     }
35899                     if (hasListener) {
35900                         rowcfg = {
35901                              
35902                             record: r,
35903                             rowIndex : rowIndex,
35904                             rowClass : ''
35905                         }
35906                         this.grid.fireEvent('rowclass', this, rowcfg);
35907                         alt.push(rowcfg.rowClass);
35908                     }
35909                     rp.alt = alt.join(" ");
35910                     rp.cells = lcb.join("");
35911                     lbuf[lbuf.length] = rt.apply(rp);
35912                     rp.cells = cb.join("");
35913                     buf[buf.length] =  rt.apply(rp);
35914                 }
35915                 return [lbuf.join(""), buf.join("")];
35916             },
35917
35918     renderBody : function(){
35919         var markup = this.renderRows();
35920         var bt = this.templates.body;
35921         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
35922     },
35923
35924     /**
35925      * Refreshes the grid
35926      * @param {Boolean} headersToo
35927      */
35928     refresh : function(headersToo){
35929         this.fireEvent("beforerefresh", this);
35930         this.grid.stopEditing();
35931         var result = this.renderBody();
35932         this.lockedBody.update(result[0]);
35933         this.mainBody.update(result[1]);
35934         if(headersToo === true){
35935             this.updateHeaders();
35936             this.updateColumns();
35937             this.updateSplitters();
35938             this.updateHeaderSortState();
35939         }
35940         this.syncRowHeights();
35941         this.layout();
35942         this.fireEvent("refresh", this);
35943     },
35944
35945     handleColumnMove : function(cm, oldIndex, newIndex){
35946         this.indexMap = null;
35947         var s = this.getScrollState();
35948         this.refresh(true);
35949         this.restoreScroll(s);
35950         this.afterMove(newIndex);
35951     },
35952
35953     afterMove : function(colIndex){
35954         if(this.enableMoveAnim && Roo.enableFx){
35955             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
35956         }
35957         // if multisort - fix sortOrder, and reload..
35958         if (this.grid.dataSource.multiSort) {
35959             // the we can call sort again..
35960             var dm = this.grid.dataSource;
35961             var cm = this.grid.colModel;
35962             var so = [];
35963             for(var i = 0; i < cm.config.length; i++ ) {
35964                 
35965                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
35966                     continue; // dont' bother, it's not in sort list or being set.
35967                 }
35968                 
35969                 so.push(cm.config[i].dataIndex);
35970             };
35971             dm.sortOrder = so;
35972             dm.load(dm.lastOptions);
35973             
35974             
35975         }
35976         
35977     },
35978
35979     updateCell : function(dm, rowIndex, dataIndex){
35980         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
35981         if(typeof colIndex == "undefined"){ // not present in grid
35982             return;
35983         }
35984         var cm = this.grid.colModel;
35985         var cell = this.getCell(rowIndex, colIndex);
35986         var cellText = this.getCellText(rowIndex, colIndex);
35987
35988         var p = {
35989             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
35990             id : cm.getColumnId(colIndex),
35991             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
35992         };
35993         var renderer = cm.getRenderer(colIndex);
35994         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
35995         if(typeof val == "undefined" || val === "") val = "&#160;";
35996         cellText.innerHTML = val;
35997         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
35998         this.syncRowHeights(rowIndex, rowIndex);
35999     },
36000
36001     calcColumnWidth : function(colIndex, maxRowsToMeasure){
36002         var maxWidth = 0;
36003         if(this.grid.autoSizeHeaders){
36004             var h = this.getHeaderCellMeasure(colIndex);
36005             maxWidth = Math.max(maxWidth, h.scrollWidth);
36006         }
36007         var tb, index;
36008         if(this.cm.isLocked(colIndex)){
36009             tb = this.getLockedTable();
36010             index = colIndex;
36011         }else{
36012             tb = this.getBodyTable();
36013             index = colIndex - this.cm.getLockedCount();
36014         }
36015         if(tb && tb.rows){
36016             var rows = tb.rows;
36017             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
36018             for(var i = 0; i < stopIndex; i++){
36019                 var cell = rows[i].childNodes[index].firstChild;
36020                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
36021             }
36022         }
36023         return maxWidth + /*margin for error in IE*/ 5;
36024     },
36025     /**
36026      * Autofit a column to its content.
36027      * @param {Number} colIndex
36028      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
36029      */
36030      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
36031          if(this.cm.isHidden(colIndex)){
36032              return; // can't calc a hidden column
36033          }
36034         if(forceMinSize){
36035             var cid = this.cm.getColumnId(colIndex);
36036             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
36037            if(this.grid.autoSizeHeaders){
36038                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
36039            }
36040         }
36041         var newWidth = this.calcColumnWidth(colIndex);
36042         this.cm.setColumnWidth(colIndex,
36043             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
36044         if(!suppressEvent){
36045             this.grid.fireEvent("columnresize", colIndex, newWidth);
36046         }
36047     },
36048
36049     /**
36050      * Autofits all columns to their content and then expands to fit any extra space in the grid
36051      */
36052      autoSizeColumns : function(){
36053         var cm = this.grid.colModel;
36054         var colCount = cm.getColumnCount();
36055         for(var i = 0; i < colCount; i++){
36056             this.autoSizeColumn(i, true, true);
36057         }
36058         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
36059             this.fitColumns();
36060         }else{
36061             this.updateColumns();
36062             this.layout();
36063         }
36064     },
36065
36066     /**
36067      * Autofits all columns to the grid's width proportionate with their current size
36068      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
36069      */
36070     fitColumns : function(reserveScrollSpace){
36071         var cm = this.grid.colModel;
36072         var colCount = cm.getColumnCount();
36073         var cols = [];
36074         var width = 0;
36075         var i, w;
36076         for (i = 0; i < colCount; i++){
36077             if(!cm.isHidden(i) && !cm.isFixed(i)){
36078                 w = cm.getColumnWidth(i);
36079                 cols.push(i);
36080                 cols.push(w);
36081                 width += w;
36082             }
36083         }
36084         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
36085         if(reserveScrollSpace){
36086             avail -= 17;
36087         }
36088         var frac = (avail - cm.getTotalWidth())/width;
36089         while (cols.length){
36090             w = cols.pop();
36091             i = cols.pop();
36092             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
36093         }
36094         this.updateColumns();
36095         this.layout();
36096     },
36097
36098     onRowSelect : function(rowIndex){
36099         var row = this.getRowComposite(rowIndex);
36100         row.addClass("x-grid-row-selected");
36101     },
36102
36103     onRowDeselect : function(rowIndex){
36104         var row = this.getRowComposite(rowIndex);
36105         row.removeClass("x-grid-row-selected");
36106     },
36107
36108     onCellSelect : function(row, col){
36109         var cell = this.getCell(row, col);
36110         if(cell){
36111             Roo.fly(cell).addClass("x-grid-cell-selected");
36112         }
36113     },
36114
36115     onCellDeselect : function(row, col){
36116         var cell = this.getCell(row, col);
36117         if(cell){
36118             Roo.fly(cell).removeClass("x-grid-cell-selected");
36119         }
36120     },
36121
36122     updateHeaderSortState : function(){
36123         
36124         // sort state can be single { field: xxx, direction : yyy}
36125         // or   { xxx=>ASC , yyy : DESC ..... }
36126         
36127         var mstate = {};
36128         if (!this.ds.multiSort) { 
36129             var state = this.ds.getSortState();
36130             if(!state){
36131                 return;
36132             }
36133             mstate[state.field] = state.direction;
36134             // FIXME... - this is not used here.. but might be elsewhere..
36135             this.sortState = state;
36136             
36137         } else {
36138             mstate = this.ds.sortToggle;
36139         }
36140         //remove existing sort classes..
36141         
36142         var sc = this.sortClasses;
36143         var hds = this.el.select(this.headerSelector).removeClass(sc);
36144         
36145         for(var f in mstate) {
36146         
36147             var sortColumn = this.cm.findColumnIndex(f);
36148             
36149             if(sortColumn != -1){
36150                 var sortDir = mstate[f];        
36151                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
36152             }
36153         }
36154         
36155          
36156         
36157     },
36158
36159
36160     handleHeaderClick : function(g, index){
36161         if(this.headersDisabled){
36162             return;
36163         }
36164         var dm = g.dataSource, cm = g.colModel;
36165         if(!cm.isSortable(index)){
36166             return;
36167         }
36168         g.stopEditing();
36169         
36170         if (dm.multiSort) {
36171             // update the sortOrder
36172             var so = [];
36173             for(var i = 0; i < cm.config.length; i++ ) {
36174                 
36175                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
36176                     continue; // dont' bother, it's not in sort list or being set.
36177                 }
36178                 
36179                 so.push(cm.config[i].dataIndex);
36180             };
36181             dm.sortOrder = so;
36182         }
36183         
36184         
36185         dm.sort(cm.getDataIndex(index));
36186     },
36187
36188
36189     destroy : function(){
36190         if(this.colMenu){
36191             this.colMenu.removeAll();
36192             Roo.menu.MenuMgr.unregister(this.colMenu);
36193             this.colMenu.getEl().remove();
36194             delete this.colMenu;
36195         }
36196         if(this.hmenu){
36197             this.hmenu.removeAll();
36198             Roo.menu.MenuMgr.unregister(this.hmenu);
36199             this.hmenu.getEl().remove();
36200             delete this.hmenu;
36201         }
36202         if(this.grid.enableColumnMove){
36203             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36204             if(dds){
36205                 for(var dd in dds){
36206                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
36207                         var elid = dds[dd].dragElId;
36208                         dds[dd].unreg();
36209                         Roo.get(elid).remove();
36210                     } else if(dds[dd].config.isTarget){
36211                         dds[dd].proxyTop.remove();
36212                         dds[dd].proxyBottom.remove();
36213                         dds[dd].unreg();
36214                     }
36215                     if(Roo.dd.DDM.locationCache[dd]){
36216                         delete Roo.dd.DDM.locationCache[dd];
36217                     }
36218                 }
36219                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
36220             }
36221         }
36222         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
36223         this.bind(null, null);
36224         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
36225     },
36226
36227     handleLockChange : function(){
36228         this.refresh(true);
36229     },
36230
36231     onDenyColumnLock : function(){
36232
36233     },
36234
36235     onDenyColumnHide : function(){
36236
36237     },
36238
36239     handleHdMenuClick : function(item){
36240         var index = this.hdCtxIndex;
36241         var cm = this.cm, ds = this.ds;
36242         switch(item.id){
36243             case "asc":
36244                 ds.sort(cm.getDataIndex(index), "ASC");
36245                 break;
36246             case "desc":
36247                 ds.sort(cm.getDataIndex(index), "DESC");
36248                 break;
36249             case "lock":
36250                 var lc = cm.getLockedCount();
36251                 if(cm.getColumnCount(true) <= lc+1){
36252                     this.onDenyColumnLock();
36253                     return;
36254                 }
36255                 if(lc != index){
36256                     cm.setLocked(index, true, true);
36257                     cm.moveColumn(index, lc);
36258                     this.grid.fireEvent("columnmove", index, lc);
36259                 }else{
36260                     cm.setLocked(index, true);
36261                 }
36262             break;
36263             case "unlock":
36264                 var lc = cm.getLockedCount();
36265                 if((lc-1) != index){
36266                     cm.setLocked(index, false, true);
36267                     cm.moveColumn(index, lc-1);
36268                     this.grid.fireEvent("columnmove", index, lc-1);
36269                 }else{
36270                     cm.setLocked(index, false);
36271                 }
36272             break;
36273             default:
36274                 index = cm.getIndexById(item.id.substr(4));
36275                 if(index != -1){
36276                     if(item.checked && cm.getColumnCount(true) <= 1){
36277                         this.onDenyColumnHide();
36278                         return false;
36279                     }
36280                     cm.setHidden(index, item.checked);
36281                 }
36282         }
36283         return true;
36284     },
36285
36286     beforeColMenuShow : function(){
36287         var cm = this.cm,  colCount = cm.getColumnCount();
36288         this.colMenu.removeAll();
36289         for(var i = 0; i < colCount; i++){
36290             this.colMenu.add(new Roo.menu.CheckItem({
36291                 id: "col-"+cm.getColumnId(i),
36292                 text: cm.getColumnHeader(i),
36293                 checked: !cm.isHidden(i),
36294                 hideOnClick:false
36295             }));
36296         }
36297     },
36298
36299     handleHdCtx : function(g, index, e){
36300         e.stopEvent();
36301         var hd = this.getHeaderCell(index);
36302         this.hdCtxIndex = index;
36303         var ms = this.hmenu.items, cm = this.cm;
36304         ms.get("asc").setDisabled(!cm.isSortable(index));
36305         ms.get("desc").setDisabled(!cm.isSortable(index));
36306         if(this.grid.enableColLock !== false){
36307             ms.get("lock").setDisabled(cm.isLocked(index));
36308             ms.get("unlock").setDisabled(!cm.isLocked(index));
36309         }
36310         this.hmenu.show(hd, "tl-bl");
36311     },
36312
36313     handleHdOver : function(e){
36314         var hd = this.findHeaderCell(e.getTarget());
36315         if(hd && !this.headersDisabled){
36316             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
36317                this.fly(hd).addClass("x-grid-hd-over");
36318             }
36319         }
36320     },
36321
36322     handleHdOut : function(e){
36323         var hd = this.findHeaderCell(e.getTarget());
36324         if(hd){
36325             this.fly(hd).removeClass("x-grid-hd-over");
36326         }
36327     },
36328
36329     handleSplitDblClick : function(e, t){
36330         var i = this.getCellIndex(t);
36331         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
36332             this.autoSizeColumn(i, true);
36333             this.layout();
36334         }
36335     },
36336
36337     render : function(){
36338
36339         var cm = this.cm;
36340         var colCount = cm.getColumnCount();
36341
36342         if(this.grid.monitorWindowResize === true){
36343             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
36344         }
36345         var header = this.renderHeaders();
36346         var body = this.templates.body.apply({rows:""});
36347         var html = this.templates.master.apply({
36348             lockedBody: body,
36349             body: body,
36350             lockedHeader: header[0],
36351             header: header[1]
36352         });
36353
36354         //this.updateColumns();
36355
36356         this.grid.getGridEl().dom.innerHTML = html;
36357
36358         this.initElements();
36359         
36360         // a kludge to fix the random scolling effect in webkit
36361         this.el.on("scroll", function() {
36362             this.el.dom.scrollTop=0; // hopefully not recursive..
36363         },this);
36364
36365         this.scroller.on("scroll", this.handleScroll, this);
36366         this.lockedBody.on("mousewheel", this.handleWheel, this);
36367         this.mainBody.on("mousewheel", this.handleWheel, this);
36368
36369         this.mainHd.on("mouseover", this.handleHdOver, this);
36370         this.mainHd.on("mouseout", this.handleHdOut, this);
36371         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
36372                 {delegate: "."+this.splitClass});
36373
36374         this.lockedHd.on("mouseover", this.handleHdOver, this);
36375         this.lockedHd.on("mouseout", this.handleHdOut, this);
36376         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
36377                 {delegate: "."+this.splitClass});
36378
36379         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
36380             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36381         }
36382
36383         this.updateSplitters();
36384
36385         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
36386             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36387             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
36388         }
36389
36390         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
36391             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
36392             this.hmenu.add(
36393                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
36394                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
36395             );
36396             if(this.grid.enableColLock !== false){
36397                 this.hmenu.add('-',
36398                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
36399                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
36400                 );
36401             }
36402             if(this.grid.enableColumnHide !== false){
36403
36404                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
36405                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
36406                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
36407
36408                 this.hmenu.add('-',
36409                     {id:"columns", text: this.columnsText, menu: this.colMenu}
36410                 );
36411             }
36412             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
36413
36414             this.grid.on("headercontextmenu", this.handleHdCtx, this);
36415         }
36416
36417         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
36418             this.dd = new Roo.grid.GridDragZone(this.grid, {
36419                 ddGroup : this.grid.ddGroup || 'GridDD'
36420             });
36421         }
36422
36423         /*
36424         for(var i = 0; i < colCount; i++){
36425             if(cm.isHidden(i)){
36426                 this.hideColumn(i);
36427             }
36428             if(cm.config[i].align){
36429                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
36430                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
36431             }
36432         }*/
36433         
36434         this.updateHeaderSortState();
36435
36436         this.beforeInitialResize();
36437         this.layout(true);
36438
36439         // two part rendering gives faster view to the user
36440         this.renderPhase2.defer(1, this);
36441     },
36442
36443     renderPhase2 : function(){
36444         // render the rows now
36445         this.refresh();
36446         if(this.grid.autoSizeColumns){
36447             this.autoSizeColumns();
36448         }
36449     },
36450
36451     beforeInitialResize : function(){
36452
36453     },
36454
36455     onColumnSplitterMoved : function(i, w){
36456         this.userResized = true;
36457         var cm = this.grid.colModel;
36458         cm.setColumnWidth(i, w, true);
36459         var cid = cm.getColumnId(i);
36460         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36461         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
36462         this.updateSplitters();
36463         this.layout();
36464         this.grid.fireEvent("columnresize", i, w);
36465     },
36466
36467     syncRowHeights : function(startIndex, endIndex){
36468         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
36469             startIndex = startIndex || 0;
36470             var mrows = this.getBodyTable().rows;
36471             var lrows = this.getLockedTable().rows;
36472             var len = mrows.length-1;
36473             endIndex = Math.min(endIndex || len, len);
36474             for(var i = startIndex; i <= endIndex; i++){
36475                 var m = mrows[i], l = lrows[i];
36476                 var h = Math.max(m.offsetHeight, l.offsetHeight);
36477                 m.style.height = l.style.height = h + "px";
36478             }
36479         }
36480     },
36481
36482     layout : function(initialRender, is2ndPass){
36483         var g = this.grid;
36484         var auto = g.autoHeight;
36485         var scrollOffset = 16;
36486         var c = g.getGridEl(), cm = this.cm,
36487                 expandCol = g.autoExpandColumn,
36488                 gv = this;
36489         //c.beginMeasure();
36490
36491         if(!c.dom.offsetWidth){ // display:none?
36492             if(initialRender){
36493                 this.lockedWrap.show();
36494                 this.mainWrap.show();
36495             }
36496             return;
36497         }
36498
36499         var hasLock = this.cm.isLocked(0);
36500
36501         var tbh = this.headerPanel.getHeight();
36502         var bbh = this.footerPanel.getHeight();
36503
36504         if(auto){
36505             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
36506             var newHeight = ch + c.getBorderWidth("tb");
36507             if(g.maxHeight){
36508                 newHeight = Math.min(g.maxHeight, newHeight);
36509             }
36510             c.setHeight(newHeight);
36511         }
36512
36513         if(g.autoWidth){
36514             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
36515         }
36516
36517         var s = this.scroller;
36518
36519         var csize = c.getSize(true);
36520
36521         this.el.setSize(csize.width, csize.height);
36522
36523         this.headerPanel.setWidth(csize.width);
36524         this.footerPanel.setWidth(csize.width);
36525
36526         var hdHeight = this.mainHd.getHeight();
36527         var vw = csize.width;
36528         var vh = csize.height - (tbh + bbh);
36529
36530         s.setSize(vw, vh);
36531
36532         var bt = this.getBodyTable();
36533         var ltWidth = hasLock ?
36534                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
36535
36536         var scrollHeight = bt.offsetHeight;
36537         var scrollWidth = ltWidth + bt.offsetWidth;
36538         var vscroll = false, hscroll = false;
36539
36540         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
36541
36542         var lw = this.lockedWrap, mw = this.mainWrap;
36543         var lb = this.lockedBody, mb = this.mainBody;
36544
36545         setTimeout(function(){
36546             var t = s.dom.offsetTop;
36547             var w = s.dom.clientWidth,
36548                 h = s.dom.clientHeight;
36549
36550             lw.setTop(t);
36551             lw.setSize(ltWidth, h);
36552
36553             mw.setLeftTop(ltWidth, t);
36554             mw.setSize(w-ltWidth, h);
36555
36556             lb.setHeight(h-hdHeight);
36557             mb.setHeight(h-hdHeight);
36558
36559             if(is2ndPass !== true && !gv.userResized && expandCol){
36560                 // high speed resize without full column calculation
36561                 
36562                 var ci = cm.getIndexById(expandCol);
36563                 if (ci < 0) {
36564                     ci = cm.findColumnIndex(expandCol);
36565                 }
36566                 ci = Math.max(0, ci); // make sure it's got at least the first col.
36567                 var expandId = cm.getColumnId(ci);
36568                 var  tw = cm.getTotalWidth(false);
36569                 var currentWidth = cm.getColumnWidth(ci);
36570                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
36571                 if(currentWidth != cw){
36572                     cm.setColumnWidth(ci, cw, true);
36573                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36574                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
36575                     gv.updateSplitters();
36576                     gv.layout(false, true);
36577                 }
36578             }
36579
36580             if(initialRender){
36581                 lw.show();
36582                 mw.show();
36583             }
36584             //c.endMeasure();
36585         }, 10);
36586     },
36587
36588     onWindowResize : function(){
36589         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
36590             return;
36591         }
36592         this.layout();
36593     },
36594
36595     appendFooter : function(parentEl){
36596         return null;
36597     },
36598
36599     sortAscText : "Sort Ascending",
36600     sortDescText : "Sort Descending",
36601     lockText : "Lock Column",
36602     unlockText : "Unlock Column",
36603     columnsText : "Columns"
36604 });
36605
36606
36607 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
36608     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
36609     this.proxy.el.addClass('x-grid3-col-dd');
36610 };
36611
36612 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
36613     handleMouseDown : function(e){
36614
36615     },
36616
36617     callHandleMouseDown : function(e){
36618         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
36619     }
36620 });